Java 字符串格式化详解

版权声明:本文为博主原创文章,未经博主允许不得转载。
微博:厉圣杰
文中如有纰漏,欢迎大家留言指出。

在 Java 的 String 类中,可以使用 format() 方法格式化字符串,该方法有两种重载形式: String.format(String format, Object... args)String.format(Locale locale, String format, Object... args)。两者的唯一区别是前者使用本地语言环境,后者使用指定语言环境
查看源码可以发现,该方法最终调用 java.util.Formatter 类的 format 方法。

public static String format(String format, Object... args) {
   return new Formatter().format(format, args).toString();
}

public static String format(Locale l, String format, Object... args) {
   return new Formatter(l).format(format, args).toString();
}

所以,掌握了 Formatter 的使用,也就掌握了 String.format 的使用,从此 Java 中格式化字符串再无敌手~ 所以我们这里先来讲解 Formatter 的用法。

参考 Java Api 中关于 Formatter 的使用说明,我们可以发现 format 方法的第一个参数是有固定格式的。其格式如下:

%[argument_index$][flags][width][.precision]conversion

argument_index: 可选,是一个十进制整数,用于表明参数在参数列表中的位置。第一个参数由 "1$" 引用,第二个参数由 "2$" 引用,依此类推。

flags: 可选,用来控制输出格式

width: 可选,是一个正整数,表示输出的最小长度

precision:可选,用来限定输出的精度

conversion:必须,用来表示如何格式化参数的字符

参考文档,可以发现 Java 其实把格式化划分为两大类:常规类型格式化和时间日期格式化,下面我们就先来介绍一下常规类型的格式化。

补充:Java 中对格式化其实还有根据类型来分类的,但这里为了方便讲述,只简单的依据格式化的参数类型来讲述,如果以后有机会,会开一篇更详细的博客。

常规类型格式化

在开始之前,这里先放一段 Api 中提供的示例代码,你可以带着示例中的问题去看接下来的内容,也可以看完之后回来看示例,你看懂了多少呢?

StringBuilder sb = new StringBuilder();
// Send all output to the Appendable object sb
Formatter formatter = new Formatter(sb, Locale.US);

// Explicit argument indices may be used to re-order output.
formatter.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d")
// -> " d  c  b  a"

// Optional locale as the first argument can be used to get
// locale-specific formatting of numbers.  The precision and width can be
// given to round and align the value.
formatter.format(Locale.FRANCE, "e = %+10.4f", Math.E);
// -> "e =    +2,7183"

// The '(' numeric flag may be used to format negative numbers with
// parentheses rather than a minus sign.  Group separators are
// automatically inserted.
formatter.format("Amount gained or lost since last statement: $ %(,.2f",
               balanceDelta);
// -> "Amount gained or lost since last statement: $ (6,217.58)"

// Writes a formatted string to System.out.
System.out.format("Local time: %tT", Calendar.getInstance());
// -> "Local time: 13:34:18"

// Writes formatted output to System.err.
System.err.printf("Unable to open file '%1$s': %2$s",
                fileName, exception.getMessage());
// -> "Unable to open file 'food': No such file or directory"

Calendar c = new GregorianCalendar(1995, MAY, 23);
String s = String.format("Duke's Birthday: %1$tb %1$te, %1$tY", c);
// -> s == "Duke's Birthday: May 23, 1995"

conversion

从上述内容可以发现,只有 conversion 这个参数是必选的。 conversion 是用来表示如何格式化参数的字符。先来看个例子:

//输出:我的名字叫:小明
System.out.println(String.format("大家好,我叫:%s","小明"));

%s 是一个占位符,s 是一个转换符,指明将参数格式化为字符串。值得注意的是,占位符代表的格式化类型必须与参数的类型相兼容,否则运行时会抛出异常,如:

System.out.println(String.format("大家好,我叫:%d","小明"));

运行这段代码,就会抛出如下异常:

Exception in thread "main" java.util.IllegalFormatConversionException: d != java.lang.String
   at java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4302)
   at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2793)
   at java.util.Formatter$FormatSpecifier.print(Formatter.java:2747)
   at java.util.Formatter.format(Formatter.java:2520)
   at java.util.Formatter.format(Formatter.java:2455)
   at java.lang.String.format(String.java:2940)

那么,转换符除了 s ,还有哪些呢?那我们就来看下代码:

/**
* conversion 的具体占位符
*/
private static void formatConversion() {
  System.out.println(String.format("'b':将参数格式化为boolean类型输出,`B`的效果相同,但结果中字母为大写。%b", false));
  System.out.println(String.format("'h':将参数格式化为散列输出,原理:Integer.toHexString(arg.hashCode()),`H`的效果相同,但结果中字母为大写。%h", "ABC"));
  System.out.println(String.format("'s':将参数格式化为字符串输出,如果参数实现了 Formattable接口,则调用 formatTo方法。`S`的效果相同。%s", 16));
  System.out.println(String.format("FormatImpl类实现了Formattable接口:%s", new FormatImpl()));
  System.out.println(String.format("'c':将参数格式化为Unicode字符,'C'的效果相同。%c", 'A'));
  System.out.println(String.format("'d':将参数格式化为十进制整数。%d", 11));
  System.out.println(String.format("'o':将参数格式化为八进制整数。%o", 9));
  System.out.println(String.format("'x':将参数格式化为十六进制整数。%x", 17));
  System.out.println(String.format("'e':将参数格式化为科学计数法的浮点数,'E'的效果相同。%E", 10.000001));
  System.out.println(String.format("'f':将参数格式化为十进制浮点数。%f", 10.000001));
  System.out.println(String.format("'g':根据具体情况,自动选择用普通表示方式还是科学计数法方式,'G'效果相同。10.01=%g", 10.01));
  System.out.println(String.format("'g':根据具体情况,自动选择用普通表示方式还是科学计数法方式,'G'效果相同。10.00000000005=%g", 10.00000000005));
  System.out.println(String.format("'a':结果被格式化为带有效位数和指数的十六进制浮点数,'A'效果相同,但结果中字母为大写。%a", 10.1));
  System.out.println(String.format("'t':时间日期格式化前缀,会在后面讲述"));
  System.out.println(String.format("'%%':输出%%。%%"));
  System.out.println(String.format("'n'平台独立的行分隔符。System.getProperty(\"line.separator\")可以取得平台独立的行分隔符,但是用在format中间未免显得过于烦琐了%n已经换行"));
}
   
private static class FormatImpl implements Formattable {
   
  @Override
  public void formatTo(Formatter formatter, int flags, int width, int precision) {
      formatter.format("我是Formattable接口的实现类");
  }
}

输出如下:

'b':将参数格式化为boolean类型输出,`B`的效果相同,但结果中字母为大写。false
'h':将参数格式化为散列输出,原理:Integer.toHexString(arg.hashCode()),`H`的效果相同,但结果中字母为大写。fc42
's':将参数格式化为字符串输出,如果参数实现了 Formattable接口,则调用 formatTo方法。`S`的效果相同。16
FormatImpl类实现了Formattable接口:我是Formattable接口的实现类
'c':将参数格式化为Unicode字符,'C'的效果相同。A
'd':将参数格式化为十进制整数。11
'o':将参数格式化为八进制整数。11
'x':将参数格式化为十六进制整数。11
'e':将参数格式化为科学计数法的浮点数,'E'的效果相同。1.000000E+01
'f':将参数格式化为十进制浮点数。10.000001
'g':根据具体情况,自动选择用普通表示方式还是科学计数法方式,'G'效果相同。10.01=10.0100
'g':根据具体情况,自动选择用普通表示方式还是科学计数法方式,'G'效果相同。10.00000000005=10.0000
'a':结果被格式化为带有效位数和指数的十六进制浮点数,'A'效果相同,但结果中字母为大写。0x1.4333333333333p3
't':时间日期格式化前缀,会在后面讲述
'%':输出%。%
'n':平台独立的行分隔符。System.getProperty("line.separator")可以取得平台独立的行分隔符,但是用在format中间未免显得过于烦琐了
已经换行

补充

  • 对于浮点转换符 'e' 、'E' 和 'f',精度是小数点分隔符后的位数。如果转换符是 'g' 或 'G',那么精度是舍入计算后所得数值的所有位数。如果转换是 'a' 或 'A',则不必指定精度。
  • 对于部分转换符,如 'b' 和 'B' ,两者转换效果是相似的,但是 'B' 会把输出中的字母都转换为大写,其它相似特性的转换符应该还有好多,大家可以去找一下O(∩_∩)O哈哈~

argument_index

还记得前面那个小明的例子嘛?现在我们来改变一下输出,要求输出如下内容:

//其中,小明、25、小小明都是由参数传递
大家好,我叫:小明,今年:25岁。小明是小小明的爸爸。

看到这个,大家会怎么做呢?也许,你会写成:

System.out.println(String.format("大家好,我叫:%s,今年:%d岁。%s是%s的爸爸。", "小明", 25, "小明", "小小明"));

恩,这样做输出的确没错,但是我们却重复输入参数“小明”。这里,就要用到 argument_index 这个参数。使用 argument_index 可以指定使用第几个参数来替换占位符,一旦使用 argument_index 用于指出参数在参数列表中位置,则所有占位符都要加上,否则会出错。修改之后的代码如下:

System.out.println(String.format("大家好,我叫:%1$s,今年:%2$d岁。%1$s是%3$s的爸爸。", "小明", 25, "小小明"));

补充:对于

System.out.println(String.format("大家好,我叫:%s,今年:%d岁。%s是%s的爸爸。", "小明", 25, "小明", "小小明"));

在 Java 中执行可能没什么问题,但如果把 "大家好,我叫:%s,今年:%d岁。%s是%s的爸爸。" 放到 Android 的 strings.xml 中,则会出现错误,解决办法就是指明每个参数在参数列表中位置。

flags

flags是可选参数,用于控制输出的格式,比如左对齐、金额用逗号隔开。

'-' 在最小宽度内左对齐,不可以与“用0填充”同时使用

'+' 结果总是包括一个符号

' ' 正值前加空格,负值前加负号

'0' 结果将用零来填充

',' 每3位数字之间用“,”分隔(只适用于fgG的转换)

'(' 若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(只适用于eEfgG的转换)
/**
* flags 的标识
*/
private static void formatFlags() {
   System.out.println("'-':在最小宽度内左对齐,不可与\"用0填充\"同时使用。");
   System.out.println(String.format("设置最小宽度为8为,左对齐。%-8d:%-8d:%-8d%n", 1, 22, 99999999));
   System.out.println(String.format("'0':结果将用零来填充。设置最小宽度为8,%08d:%08d:%08d", 1, -22, 99999990));
   System.out.println(String.format("'+':结果总是包括一个符号。%+d:%+d:%+d", 1, -2, 0));
   System.out.println(String.format("' ':正值前加空格,负值前加负号。% d:% d:% d", 1, -2, 0));
   System.out.println(String.format("',':每3位数字之间用“,”分隔(只适用于fgG的转换)。%,d:%,d:%,d", 1, 100, 1000));
   System.out.println(String.format("'(':若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(只适用于eEfgG的转换)。%(d:%(d", 1, -1));
}

输出如下:

'-':在最小宽度内左对齐,不可与"用0填充"同时使用。
设置最小宽度为8为,左对齐。1       :22      :99999999

'0':结果将用零来填充。设置最小宽度为8,00000001:-0000022:99999990
'+':结果总是包括一个符号。+1:-2:+0
' ':正值前加空格,负值前加负号。 1:-2: 0
',':每3位数字之间用“,”分隔(只适用于fgG的转换)。1:100:1,000
'(':若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(只适用于eEfgG的转换)。1:(1)

width

width是可选参数,用于控制输出的宽度。示例如下:

System.out.println(String.format("设置最小宽度为8,不满8位用0填充:%08d:%08d", 1, -10000000));

输出如下:

设置最小宽度为8,不满8位用0填充:00000001:-10000000

但是 width 的值不能为 0 ,否则会抛出

Exception in thread "main" java.util.DuplicateFormatFlagsException: Flags = '0'
    at java.util.Formatter$Flags.parse(Formatter.java:4443)
    at java.util.Formatter$FormatSpecifier.flags(Formatter.java:2640)
    at java.util.Formatter$FormatSpecifier.<init>(Formatter.java:2709)
    at java.util.Formatter.parse(Formatter.java:2560)
    at java.util.Formatter.format(Formatter.java:2501)
    at java.util.Formatter.format(Formatter.java:2455)
    at java.lang.String.format(String.java:2940)

precision

precision是可选参数,用来限定输出的精度,用于浮点数。示例如下:

/**
* 格式化精度,仅限浮点数
*/
private static void formatPrecision() {
   //System.out.println(String.format("设置精度为2位:%.2d", 1));
   System.out.println(String.format("设置精度为2位:%.2f", 1f));
}

输出如下:

设置精度为2位:1.00

值得注意的是,如果对整型数据设置精度,则会抛出如下异常:

Exception in thread "main" java.util.IllegalFormatPrecisionException: 2
    at java.util.Formatter$FormatSpecifier.checkInteger(Formatter.java:2984)
    at java.util.Formatter$FormatSpecifier.<init>(Formatter.java:2729)
    at java.util.Formatter.parse(Formatter.java:2560)
    at java.util.Formatter.format(Formatter.java:2501)
    at java.util.Formatter.format(Formatter.java:2455)
    at java.lang.String.format(String.java:2940)

时间日期格式化

在平时开发中,经常会碰到要显示时间日期的。以前写过一篇 Android 时间、日期相关类和方法 的博客,里面对 Android 中经常出现的时间日期格式做了总结,但觉得还是过于繁琐,这次总结 Java 中格式化输出,没想到 Java 中已经包含了如此多关于时间的转换符,完全能应对日常开发需要,而且不用复杂的计算。

Java 中时间日期格式化的转换符可以分为三类,分别是:时间格式化转换符、日期格式化转换符、时间日期格式化转换符。相比于日期和时间日期格式化转换符,时间格式化转换符就相对多一点。

时间日期格式化字符串的格式如下:

%[argument_index$][flags][width]conversion

相对于普通的格式,时间日期格式化少了 precision ,而 conversion 是由两个字符组成,且第一个字符固定为 tT

网上部分博文是贴了转换符说明的表格,但是写完代码之后突然发现,Java 格式化输出这部分内容,看代码和输出其实比看表格更直观,谁让我们是程序员呢?下面就用代码来讲述一下时间日期格式化转换符的三种类别。

格式化时间

示例代码如下:

/**
* 格式化时间
*/
private static void formatTime() {
   System.out.println("这是格式化时间相关的,具体输出跟你执行代码时间有关");
   Calendar calendar = Calendar.getInstance();
   System.out.println(String.format("'H':2位数24小时制,不足两位前面补0:%tH(范围:00-23)", calendar));
   System.out.println(String.format("'I':2位数12小时制,不足两位前面补0:%tI(范围:01-12)", calendar));
   System.out.println(String.format("'k':24小时制,不足两位不补0:%tk(范围:0-23)", calendar));
   System.out.println(String.format("'l':12小时制,不足两位不补0:%tl(范围:1-12)", calendar));
   System.out.println(String.format("'M':2位数的分钟,不足两位前面补0:%tM(范围:00-59)", calendar));
   System.out.println(String.format("'S':分钟中的秒,2位数,不足两位前面补0,60是支持闰秒的一个特殊值:%tS(范围:00-60)", calendar));
   System.out.println(String.format("'L':3位数的毫秒,不足三位前面补0:%tL(范围:000-999)", calendar));
   System.out.println(String.format("'N':9位数的微秒,不足九位前面补0:%tN(范围:000000000-999999999)", calendar));

   System.out.println(String.format("'p':输出本地化的上午下午,例如,Locale.US为am或pm,Locale.CHINA为上午或下午", calendar));
   System.out.println(String.format(Locale.US, "Local.US=%tp", calendar));
   System.out.println(String.format(Locale.CHINA, "Local.CHINA=%tp", calendar));
   System.out.println();

   System.out.println(String.format("'z':时区:%tz", calendar));
   System.out.println(String.format("'Z':时区缩写字符串:%tZ", calendar));
   System.out.println(String.format("'s':从1970-1-1 00:00到现在所经历的秒数:%ts", calendar));
   System.out.println(String.format("'Q':从1970-1-1 00:00到现在所经历的豪秒数:%tQ", calendar));
}

输出结果如下:

这是格式化时间相关的,具体输出跟你执行代码时间有关
'H':2位数24小时制,不足两位前面补0:21(范围:00-23)
'I':2位数12小时制,不足两位前面补0:09(范围:01-12)
'k':24小时制,不足两位不补0:21(范围:0-23)
'l':12小时制,不足两位不补0:9(范围:1-12)
'M':2位数的分钟,不足两位前面补0:26(范围:00-59)
'S':分钟中的秒,2位数,不足两位前面补0,60是支持闰秒的一个特殊值:44(范围:00-60)
'L':3位数的毫秒,不足三位前面补0:502(范围:000-999)
'N':9位数的微秒,不足九位前面补0:502000000(范围:000000000-999999999)
'p':输出本地化的上午下午,例如,Locale.US为am或pm,Locale.CHINA为上午或下午
Local.US=pm
Local.CHINA=下午

'z':时区:+0800
'Z':时区缩写字符串:CST
's':从1970-1-1 00:00到现在所经历的秒数:1476883604
'Q':从1970-1-1 00:00到现在所经历的豪秒数:1476883604502

格式化日期

示例代码如下:

/**
 * 格式化日期
 */
private static void formatDate() {
    System.out.println("-----------------------我是微笑的分割线O(∩_∩)O哈哈~-----------------------------");
    System.out.println("这是格式化时间相关的,具体输出跟你执行代码时间有关");
    Calendar calendar = Calendar.getInstance();
    System.out.println(String.format("'B':本地化显示月份字符串,如:January、February"));
    System.out.println(String.format("'b':本地化显示月份字符串的缩写,如:Jan、Feb"));
    System.out.println(String.format("'h':本地化显示月份字符串的缩写,效果同'b'"));
    System.out.println(String.format(Locale.US, "Locale.US 月份=%1$tB,缩写=%1$tb", calendar));
    System.out.println(String.format(Locale.CHINA, "Locale.CHINA 月份=%1$tB,缩写=%1$tb", calendar));

    System.out.println(String.format("'A':本地化显示星期几字符串,如:Sunday、Monday"));
    System.out.println(String.format("'a':本地化显示星期几字符串的缩写,如:Sun、Mon"));
    System.out.println(String.format(Locale.US, "Locale.US 星期几=%1$tA,缩写=%1$ta", calendar));
    System.out.println(String.format(Locale.CHINA, "Locale.CHINA 星期几=%1$tA,缩写=%1$ta", calendar));

    System.out.println(String.format("'C':年份除以100的结果,显示两位数,不足两位前面补0:%tC(范围:00-99)", calendar));
    System.out.println(String.format("'Y':显示四位数的年份,格利高里历,即公历。不足四位前面补0:%tY", calendar));
    System.out.println(String.format("'y':显示年份的后两位:%ty(范围:00-99)", calendar));
    System.out.println(String.format("'j':显示当前公历年的天数:第%tj天(范围:001-366)", calendar));
    System.out.println(String.format("'m':显示当前月份:%tm月(范围:01-13?怎么会有13个月?)", calendar));
    System.out.println(String.format("'d':显示是当前月的第几天,不足两位前面补0:%1$tm月第%1$td天(范围:01-31)", calendar));
    System.out.println(String.format("'e':显示是当前月的第几天:%1$tm月第%1$te天(范围:1-31)", calendar));
}

输出结果如下:

-----------------------我是微笑的分割线O(∩_∩)O哈哈~-----------------------------
这是格式化时间相关的,具体输出跟你执行代码时间有关
'B':本地化显示月份字符串,如:January、February
'b':本地化显示月份字符串的缩写,如:Jan、Feb
'h':本地化显示月份字符串的缩写,效果同'b'
Locale.US 月份=October,缩写=Oct
Locale.CHINA 月份=十月,缩写=十月
'A':本地化显示星期几字符串,如:Sunday、Monday
'a':本地化显示星期几字符串的缩写,如:Sun、Mon
Locale.US 星期几=Wednesday,缩写=Wed
Locale.CHINA 星期几=星期三,缩写=星期三
'C':年份除以100的结果,显示两位数,不足两位前面补0:20(范围:00-99)
'Y':显示四位数的年份,格利高里历,即公历。不足四位前面补0:2016
'y':显示年份的后两位:16(范围:00-99)
'j':显示当前公历年的天数:第293天(范围:001-366)
'm':显示当前月份:10月(范围:01-13?怎么会有13个月?)
'd':显示是当前月的第几天,不足两位前面补0:10月第19天(范围:01-31)
'e':显示是当前月的第几天:10月第19天(范围:1-31)

补充:在文档中发现一个略微奇怪的问题,就是 m 转换符,文档上的大意是:该转换符用于显示当前月是这一年的第几个月,文档里给的范围竟然是01-13,一年怎么会有13个月,告诉我不是我理解错了?

格式化时间日期

示例代码如下:

/**
 * 格式化时间日期
 */
private static void formatTimeAndDate() {
    System.out.println("-----------------------我是微笑的分割线O(∩_∩)O哈哈~-----------------------------");
    System.out.println("这是格式化时间相关的,具体输出跟你执行代码时间有关");
    Calendar calendar = Calendar.getInstance();
    //%tH:%tM的缩写
    System.out.println(String.format("'R':将时间格式化为:HH:MM(24小时制)。输出:%tR", calendar));
    //%tH:%tM:%tS的缩写
    System.out.println(String.format("'T':将时间格式化为:HH:MM:SS(24小时制)。输出:%tT", calendar));
    //%tI:%tM:%tS %Tp的缩写,输出形如:
    System.out.println(String.format("'r':将时间格式化为:09:23:15 下午,跟设置的语言地区有关。输出:%tr", calendar));
    //%tm/%td/%ty的缩写,输出形如
    System.out.println(String.format("'D':将时间格式化为:10/19/16。输出:%tD", calendar));
    //%tY-%tm-%td,输出形如:
    System.out.println(String.format("'F':将时间格式化为:2016-10-19。输出:%tF", calendar));
    //%ta %tb %td %tT %tZ %tY,输出形如:Sun Jul 20 16:17:00 EDT 1969
    System.out.println(String.format("'c':将时间格式化为\"Sun Jul 20 16:17:00 EDT 1969\"。输出:%tc", calendar));
}

输出结果如下:

-----------------------我是微笑的分割线O(∩_∩)O哈哈~-----------------------------
这是格式化时间相关的,具体输出跟你执行代码时间有关
'R':将时间格式化为:HH:MM(24小时制)。输出:21:26
'T':将时间格式化为:HH:MM:SS(24小时制)。输出:21:26:44
'r':将时间格式化为:09:23:15 下午,跟设置的语言地区有关。输出:09:26:44 下午
'D':将时间格式化为:10/19/16。输出:10/19/16
'F':将时间格式化为:2016-10-19。输出:2016-10-19
'c':将时间格式化为"Sun Jul 20 16:17:00 EDT 1969"。输出:星期三 十月 19 21:26:44 CST 2016

总结

系统的介绍了 Java 中格式化字符串的方式及相关的转换符。不过只是对此有印象,方便日后温习,还得在日常中不断加强使用。

虽然 Formatter 中提供了很多关于时间日期的转换符,而且能满足日常的绝大部分使用,但还是存在限制,所以对于有特殊要求的时间格式,还是要学会自己定制。

String.format 这个方法很实用,但如果是大批量进行字符串格式化,就需要考虑到性能方面的问题,因为每次调用 format() 方法都会 new 一个 Formatter 对象。而在 Java 中频繁创建对象需要大量时间,而且还要花时间对这些对象进行垃圾回收和处理。最好的办法就是自己把 DateFormat 、 NumberFormat 、 MessageFormat 这些类封装成静态工具。

参考

  1. Java Api -- Formatter
  2. String.format()方法使用说明
  3. JAVA字符串格式化-String.format()的使用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • 在JavaSe5中,推出了C语言中printf()风格的格式化输出。这不仅使得控制输出的代码更加简单,同时也给与J...
    三藏君阅读 771评论 0 0
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 相关阅读 Java基础:String类 Java字符串格式化 Java基础:正则表达式 1. 常规类型的格式化 S...
    JackChen1024阅读 582评论 0 1
  • 说起隋炀帝杨广,那是个家喻户晓的人物。当然,是以反面为主的。据历史记载,他荒淫无耻,聚众淫乱,,杀兄弑父,残暴无比...
    微言微评阅读 595评论 0 0
  • 车子停下来,苏莹莹才觉得无比熟悉。 是一片小花园,隔着夜色就能闻到好闻的蔷薇香味,想来白天,一朵朵会更加炫目。苏莹...
    安东托尼阅读 175评论 0 1