Blue的博客

.NET 中的数字格式化,日期格式化

格式

基本内容是:可以在 Console.WriteLine(以及 String.Format,它被 Console.WriteLine 调用)中的格式字符串内的括号中放入非索引数字的内容。格式规范的完整形式如下:

{index [, width][:formatstring]}

其中,index 是此格式程序引用的格式字符串之后的参数,从零开始计数;width(如果有的话)是要设置格式的字段的宽度(以空格计)。width 取正数表示结果右对齐,取负数则意味着数字在字段中左对齐。(请参阅下面的前两个示例。)

formatstring 是可选项,其中包含有关设置类型格式的格式说明。如果对象实现 IFormattableformatstring 就会传递给对象的 Format 方法(在 Beta 2 和后续版本中,该方法的签名变为 ToString(string, IFormatProvider),但功能不变)。如果对象不实现 IFormattable,就会调用 Object.ToString(),而忽略 formatstring

另请注意,在 Beta 1 中不区分当前语言的 ToString 在 Beta 2 和后续版本中“将”区分语言。例如,对于用“.”分隔千位,用“,”分隔小数的国家,1,234.56 将会格式化成 1.234,56。如果您需要结果无论在什么语言下都是一样的,就请使用 CultureInfo.InvariantCulture 作为语言。

若要获取有关格式的完整信息,请查阅“.NET 框架开发人员指南”中的格式概述(英文)

数字格式

请注意,数字的格式是区分语言的:分隔符以及分隔符之间的空格,还有货币符号,都是由语言决定的 — 默认情况下,是您计算机上的默认语言。默认语言与执行线程相关,可以通过 Thread.CurrentThread.CurrentCulture 了解和设置语言。有几种方法,可以不必仅为一种给定的格式操作就立即更改语言。

内置类型的字母格式

有一种格式命令以单个字母开头,表示下列设置:

G—常规,E 或 F 中较短的

F—浮点数,常规表示法

E—用 E 表示法表示的浮点数(其中,E 代表 10 的次幂)

N—带有分隔符的浮点数(在美国为逗号)

C—货币,带有货币符号和分隔符(在美国为逗号)

D—十进制数,仅用于整型

X—十六进制数,仅用于整型

字母可以后跟一个数字,根据字母的不同,该数字可以表示总位数、有效位数或小数点后面的位数。

下面是字母格式的一些示例:

double pi = Math.PI;
double p0 = pi * 10000;
int i = 123;

Console.WriteLine("浮点格式,无分隔符(逗号)");
Console.WriteLine("pi, Left {0, -25}",   pi); // 3.1415926535897931
Console.WriteLine("p0, Rt.  {0, 25}",    p0); //        3.1415926535897931
Console.WriteLine("pi, E    {0, 25:E}",  pi); //               3.1416E+000

Console.WriteLine("使用 E 和 F 格式,小数点后保留 n(此处为 4)位");
Console.WriteLine("pi, E4   {0, 25:E4}", pi); //               3.1416E+000
Console.WriteLine("pi, F4   {0, 25:F4}", pi); //                    3.1416 
Console.WriteLine("使用 G 格式,保留 4 位有效数字——如果需要请使用 E 格式");
Console.WriteLine("pi, G4   {0, 25:G4}", pi); //                     3.142
Console.WriteLine("p0, G4   {0, 25:G4}", p0); //                   3.142E4 

Console.WriteLine("N 和 C 格式带有逗号(分隔符)," +
         "默认小数点后保留两位,四舍五入。");
Console.WriteLine("p0, N    {0, 25:N}",  p0); //                 31,415.93 
Console.WriteLine("p0, N4   {0, 25:N4}", p0); //               31,415.9265 
Console.WriteLine("p0, C    {0,25:C}",   pi); //                     $3.14

Console.WriteLine("D 和 X 格式仅用于整型," +
         "非整型将产生格式异常——X 指十六进制");
Console.WriteLine("i,  D    {0, 25:D}",  i ); //                       123
Console.WriteLine("i,  D7   {0, 25:D7}", i ); //                   0000123
Console.WriteLine("i,  X    {0, 25:X}",  i ); //                        7B
Console.WriteLine("i,  X8   {0, 25:X8}", i ); //                  0000007B

图片格式

与字母格式不同,formatstring 可以包含“图片格式”。下面是从代码中摘录的几个实例。(这类似于 Basic 中的“Print Using”语句。)图片格式功能甚至包括以不同方式设置负数、正数和零的格式的能力。还有几个图片格式功能,下面的示例中未包括在内。有关详细信息,请参阅“.NET 框架开发人员指南”或文档中的主题图片格式数字串(英文)

在下例中您将注意到,好心的博士既使用了“#”字符,又使用了“0”字符。如果相应的数字是前导零或尾随零,“#”字符就会替换为空值。无论相应数字的值如何,“0”字符都会被替换为零字符 — 因此,数字将会被零填补。句号(如果有的话)表示小数分隔符的位置。

那么,为什么要同时使用这些字母,比如“###0.##”? 如果要设置格式的值恰好为零,“#” 图片字符就被替换为“无”(连零字符也不是)。您可能“总是”希望在小数点的左边至少有一个“0”,否则,如果值为零,字段就没有输出。换言之,仅包含“#”字符,一个“0”也没有的格式常被认为是一个编程错误。

逗号有两种用法:如果一个逗号或一组逗号紧跟在句号的左边(或者没有句号时在结尾),它们就会告诉格式化程序分隔 10 ** (3 * n) 所显示的数字,其中,n 是逗号的个数。换言之,数字按千位、百万位、十亿位等分隔。

如果逗号的右侧至少有一个“0”或“#”占位符,它就会告诉格式化程序在各数位组之间放置适当的组分隔符字符(在美国为逗号。)(在美国,每三个数位算一组。)

可以设置百分比的格式,方法是在图片中放入“%”。“%”将在指定的位置显示,在显示前数字将被乘以 100(这样,0.28 就变成了 28%)。

如果希望将图片格式用于指数表示法,可以指定“e”或“E”后跟加号或减号,再后跟任意个零,比如“E+00”或“e-000”。如果使用“e”,则显示小写“e” 。如果使用“E”,则显示大写“E” 。如果使用加号,则指数的符号总是出现。如果使用减号,则符号只有在指数为负数时才会显示。(Beta 1 版在处理“-”时有问题,该符号会导致负号总是出现。)

根据要设置格式的数字的符号,还有一个条件格式。在格式字符串中仅包含两个或三个独立的格式,它们由分号分隔。如果有两个格式,则第一个将用于非负数,第二个用于负数。如果有三个格式,则第一个将用于正数,第二个用于负数,第三个用于零。

可以在格式字符串中包含文字字符。如果所需的字符具有特殊意义,请在其前面使用反斜杠符号,使其“转义”。例如,如果希望在不乘以 100 的情况下显示百分比符号,就可以在数字前面使用反斜杠(在 C++ 和 C# 中必须使用两个反斜杠),比如“#0.##\\%”。(如果正在使用 C#,就可以使用极酷的逐字字符串文字,比如@"#0.##\%"。)或者,也可以将字符串放入单引号或双引号中,以避免将其字符解释为格式命令。在 Beta 2 及更高版本中,可以通过使用双括号,从而在格式字符串中包含文字括号。

下面是有关图片格式的一些示例:

long m34 = 34000000; // 34,000,000
Console.WriteLine("几种图片格式");
Console.WriteLine("如果没有数位,0 将打印 0;" +
         "诸如 i: 的文字总是打印");
Console.WriteLine("\t句点代表小数分隔符的位置");
Console.WriteLine("i,  i: 0000.0 {0, 10:i: 0000.0}", i); //    
i:0123.0
Console.WriteLine("如果没有有效数字 # 将不显示," +
         "逗号意味着放入分隔符");
Console.WriteLine("请确保在数字图片中至少使用一个 0。");
Console.WriteLine("p0, ##,##0.#  {0, 10:##,##0.#}",-p0); //   -31,415.9
Console.WriteLine("m34, 0,,      {0, 10:0,, 百万}", m34); // 34 百万
Console.WriteLine("p0, #0.#E+00  {0, 10:#0.#E+00}", p0); //    31.4E+03
Console.WriteLine("% 乘以 100 并打印百分号");
Console.WriteLine("pi, ###0.##%  {0, 10:###0.##%}", pi); //     314.16%
Console.WriteLine("因为 \\ 而没有进行乘法运算" +
"(注意:两个反斜线!)");
Console.WriteLine("pi, ###0.##\\\\% {0, 10:###0.##\\%}", pi); //  3.14%
Console.WriteLine("与 C# 的逐字字符串相同");
Console.WriteLine(@"pi, ###0.##\\% {0, 10:###0.##\%}", pi); //    3.14%
Console.WriteLine("10, '#'#0     {0, 10:'#'#0}",      10); //       #10
Console.WriteLine("基于符号的条件格式");
Console.WriteLine("如果是 0 或正数打印 #,如果是负数打印 (#)");
Console.WriteLine("-5  0;(0)     {0, 10:0;(0)}",    -5); //         (5)
Console.WriteLine("如果是正数打印 #,如果是负数打印 -#,如果是 0 打印 zip");
Console.WriteLine(" 0  0;-0;zip  {0, 10:0;-0;zip}",  0); //         zip

如您所见,格式功能非常强大。

格式的工作方式

文档中的示例对所传递的对象类型的变量调用 Format 方法。对这些 Format 方法仅传递格式规范的 formatstring 部分,而不传递 indexwidth。(在 Beta 2 中,对 Format 的调用将改为对 ToString 的调用。)

indexwidthString.Format(它被 Console.WriteConsole.WriteLine 调用)使用,以获得调用 Format 的正确对象以及将该调用的结果左或右对齐。(顺便说一下,如果要设置格式的对象不实现 IFormattable(并因此调用 Format 方法),String.Format 将调用对象的 ToString() 方法,而忽略 formatstring。)

换言之,Console.WriteLine 调用 String.Format,传递向它传递的所有参数。String.Format 分析字符串,查找“{”字符。找到该字符后,它将分析子字符串直到第一个“}”为止,以确定 index 数、widthformatstring。然后,它按照 index 访问相应的参数,并调用其 Format 方法,传递“{}”段中的 formatstring 部分。(如果参数对象不实现 IFormattable,则被调用的是 ToString。)

无论是实现还是不实现,都会返回一个字符串,并且 String.Format 在继续分析格式字符串之前会将其与结果字符串连接。之后,String.Format 将生成的带格式字符串返回给 Console.WriteLine,由 Console.WriteLine 进行显示。

对于 Beta 2 及更高版本,对象的 Format 方法(它是 IFormattable 中的 Format 方法)被 ToString 所替代,ToString 获取一个格式字符串和一个 IFormatProvider(或 null)。但 String.Format 仍存在,因此这些调用将不改变。

自定义格式

您自己也可以编写格式化程序,用于自己的类型或作为内置类型的自定义格式化程序,如“.NET 框架开发人员指南”中的自定义 Format 方法所说明的那样。如果编写内置类型的自定义格式化程序,就不能从 Console.WriteLine 中使用它,但可以通过调用 String.Format 的重载而使用它,String.Format 的重载将采用 IServiceObjectProvider(在 beta 2 及更高版本中称为 IFormatProvider)作为参数。

日期和时间格式

您将记起,有一个叫做 DateTime 的类,用于保存日期和时间。像您所猜想的那样,有大量方法可供设置 DateTime 对象的格式:仅日期、仅时间、世界时或本地时、若干种日/月/年顺序,甚至可分类。日期和时间格式是区分语言的。

还可以使用自定义格式字符串来设置 DateTime 对象的格式。这种字符串将包含由某些字母组成的区分大小写的子字符串,以表示日期和时间的各个不同部分,如星期几、几号、月份、年份、纪元、小时、分钟、秒或时区。这些部分中有许多具有多种格式,例如,M 是没有前导零的数字月份,MM 是有前导零的数字月份,MMM 是三个字母的月份缩写,MMMM 是所在国家语言对应的完整月份名称的拼写。在“.NET 框架参考”中可以找到自定义和标准格式字符的完整列表

下面是有关日期和时间格式的一个示例:

Console.WriteLine("标准格式");
// 后面的“分析”中会有更多信息
DateTime dt = DateTime.Parse("2001 年 1 月 1 日,12:01:00am"); 
Console.WriteLine("d:   {0:d}", dt); // 1/1/2001
Console.WriteLine("D:   {0:D}", dt); // 2001 年 1 月 1 日,星期一
Console.WriteLine("f:   {0:f}", dt); // 2001 年 1 月 1 日,星期一 12:01 AM
Console.Write("F:   {0:F}", dt); // 2001 年 1 月 1 日,星期一 12:01:00 AM
Console.WriteLine();
Console.WriteLine("g:   {0:g}", dt); // 1/1/2001 12:01 AM
Console.WriteLine("G:   {0:G}", dt); // 1/1/2001 12:01:00 AM
Console.WriteLine("M/m: {0:M}", dt); // 2001 年 1 月 
Console.WriteLine("R/r: {0:R}", dt); // 2001 年 1 月 1 日,星期一 08:01:00 GMT
Console.WriteLine("s:   {0:s}", dt); // 2001-01-01T00:01:00
Console.WriteLine("t:   {0:t}", dt); // 12:01 AM
Console.WriteLine("T:   {0:T}", dt); // 12:01:00 AM
Console.WriteLine("u:   {0:u}", dt); // 2001-01-01 08:01:00Z
Console.Write("U:   {0:U}", dt); // 2001 年 1 月 1 日,星期一 8:01:00 AM
Console.WriteLine();
Console.WriteLine("Y/y: {0:Y}", dt); // 2001 年 1 月
Console.WriteLine("自定义格式");
// 对作为格式使用的字符必须“转义”—此处为 t 和 z
// 同时使用引号(在文字字符串中)和反斜杠
Console.WriteLine(@"dddd, dd MMMM yyyy"" at ""HH:mm:ss in \zone zzz:");
Console.WriteLine(@"{0:dddd, dd MMMM yyyy"" at ""HH:mm:ss in \zone zzz}",
      dt);
   //    2001 年 1 月 1 日,星期一  00:01:00 于时区 -08:00
http://www.microsoft.com/china/MSDN/library/archives/library/welcome/dsmsdn/drguinet03292001.asp
程序:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
 class Program
 {
  static void Main(string[] args)
  {
   double pi = Math.PI;
   double p0 = pi * 10000;
   int i = 123;
   long m34 = 34000000; // 34,000,000
   Console.WriteLine("几种图片格式");
   Console.WriteLine("如果没有数位,0 将打印 0;" +
      "诸如 i: 的文字总是打印");
   Console.WriteLine("\t句点代表小数分隔符的位置");
   Console.WriteLine("i,  i: 0000.0 {0, 10:i: 0000.0}", i); //    i:0123.0
   Console.WriteLine("如果没有有效数字 # 将不显示," +
      "逗号意味着放入分隔符");
   Console.WriteLine("请确保在数字图片中至少使用一个 0。");
   Console.WriteLine("p0, ##,##0.#  {0, 10:##,##0.#}", -p0); //   -31,415.9
   Console.WriteLine("m34, 0,,      {0, 10:0,, 百万}", m34); // 34 百万
   Console.WriteLine("p0, #0.#E+00  {0, 10:#0.#E+00}", p0); //    31.4E+03
   Console.WriteLine("% 乘以 100 并打印百分号");
   Console.WriteLine("pi, ###0.##%  {0, 10:###0.##%}", pi); //     314.16%
   Console.WriteLine("pi, ###0.##%  {0, 10:#######0.##%}", pi); //     314.16%
   Console.WriteLine("因为 \\ 而没有进行乘法运算" +
   "(注意:两个反斜线!)");
   Console.WriteLine("pi, ###0.##\\\\% {0, 10:###0.##\\%}", pi); //  3.14%
   Console.WriteLine("与 C# 的逐字字符串相同");
   Console.WriteLine(@"pi, ###0.##\\% {0, 10:###0.##\%}", pi); //    3.14%
   Console.WriteLine("10, '#'#0     {0, 10:'#'#0}", 10); //       #10
   Console.WriteLine("10, '#'#0     {0, 10:##0}", 10); //       10
   Console.WriteLine("基于符号的条件格式");
   Console.WriteLine("如果是 0 或正数打印 #,如果是负数打印 (#)");
   Console.WriteLine("-5  0;(0)     {0, 10:0;(0)}", -5); //         (5)
   Console.WriteLine("如果是正数打印 #,如果是负数打印 -#,如果是 0 打印 zip");
   Console.WriteLine(" 0  0;-0;zip  {0, 10:0;-0;zip}", 0); //         zip
   Console.ReadLine();
  }
 }
}

posted on 2007-05-07 15:23  Blueeyes  阅读(1751)  评论(0编辑  收藏  举报