11章 转换

说明:本文翻译自《TangoRefMan_Sep_1_2008》

      

由于本人是编程初学者,对很多程序设计概念不是非常熟悉,编程经验不多,再加上英语水平不高,翻译纯属一个D语言爱好者实验之作,很多错误在所难免,还请读者见谅。另外,如果你发现本文有不当和错误之处,还请多提宝贵意见。  


11章  转换


在像D语言这样的静态语言中,文本转换是必需的,Tango提供给我们一些简单的方法转换文本,并包含一个综合性的格式化框架,它吸取了.net框架的精华。这个框架也是Tango的场所(locale)支持的基础。
转换模块在Tango的tango.text.convert包中(不包括场所支持的需要)。
本章将开始介绍tango.text.convert的两个主要部分:从基本类型转换到文本和从文本转换到基本类型与及文本的格式化。之后,将给出每个模块更详细的描述。
第一节  在文本和数值间转换
具体的转换通过各个转换器,多数的转换器执行从文本翻译到数值或相反的操作。每个转换器都有两个能用方法:
parse  从文本表示转换到某种类型。
format 从某种类型转换到文本表示。
format
所有的format函数需要有一个输出缓冲器作为第一个参数,这样避免堆活动,并且指示转换器你将要输出到哪一种文本编码(char[],wchar[]还是dchar[])。输入被转换的数据通常用作第二个参数,输出的文本结果是所提供的输出缓冲流的一个切片。用户利用返回值代替原始缓冲器,尽管内存是相同的(返回值是正确大小的一个切片,不是整个输出缓冲器)。
在这种情况下,有两种通用方法提供缓冲器。如果结果能存在栈上可以这样做:
char[10] output ;//转换器使用.length作为最大尺寸
auto  result=format(output,15);//格式化数字15
如果结果需要放置在堆上,如排除其他一些函数,最简单的解决方法是:
auto  result = format (new char[10],15);
两种方法都可以,但后一种形式具自说明性且更为简洁。
每个format方法被模块操作用于输出自然的D字符数组:char[],wchar[],dchar[] 。它们的通常格式为:
T[] format(T[] dst,V x)
T可以是char,wchar或dchar类型,V是要转换的基本类型,上面的形式只说明性的,一些模板有更多选项。
不同的format版本可能有附加的参数用于具体说明关于要转换的类型的格式细节。
parse
parse函数用一个说明编码的字符串作为它的第一个参数,返回一个转换后的值。
和format类似,parse被模块操作处理自然的D文本类型输入数组(char[],wchar[],dchar[] ),通常形式为:
V parse (T[])
其中的T是char,wchar或dchar类型,V是字符串转换成的数值类型。
像format一样,不同版本的parse有可以附加参数提供用于解析输入字符串的相关细节。
转换器提供一部分方便的包装,如下面的形式:
toString,toString16,toString32
这些函数把输入的数值类型转换成像函数名一样的特定字符串类型。这些函数不需要一个安放结果的预分配数组。模板形式类似于format,但没有结果参数。
T []  toString××(V x )

 

 

toType
给定一个输入字符串,多数模块有一个函数产生和函数名一样的特定返回值,输入的字符串必须能够正确解析。
模块形式为: V toTypd (T[])
避免符号冲突
转换模块由大多数独立式函数组成,并且许多有通用名字,如parse,format等等,为避免编译时符号冲突,也为了避免导入其他库时引起意外冲突,建议实践中使用重命引入给导入函数创建一个名字空间,使用Integer模块的一个例子如下:
import Integer = tango.text.convert.Integer;
auto I =Integer.parse(‘32767’);

Integer 模块
tango.text.convert.Integer模块有一些函数帮助我们从字符串类型转换成整数类型,或执行相反操作。这些改变从快速和脏的方法到更综合化的安全的方法,在下面的例子中,一个默认的parse和format用法被展示:
auto value = Integer.parse(“0xff005500“);
auto text= Integer.format(new char[32],12345L);
parse 能使用两个附加参数,一个uint型参数提供要被转换的数制(radix of the number),一个指向uint的ate参数限定被解析的字符串长度。注意:如果字符串自己有明确的数制,提供的radix参数将会被忽略。
支持的radix变量
详细说明 Radix 示例
B或b 2(binary,即二进制) 0b10101
O或o 8(octal即二进制) 0o654
X或x 16(hexadecimal即二进制) 0xDEADBEEF
无 10(decimal,即二进制) 150
format也有两个附加参数,即一个标志使用什么格式,一个标志附加的修改
格式类型标志
格式标志 描述
Format.Unsigned 格式化为无符号十进制数
Format.Signed 格式化为有符号十进制数(默认)
Format.Octal 格式化为一个八进制数
Format.Hex 格式化为小写十六进制数
Fornat.HexUpper 格式化为大写十六进制数
Format.Binary 格式化为一个二进制数
附加修改形式为
修改标志 描述
Flags.None(默认) 不修改
Flags.Prefix 添加具体数制前缀
Flags.Plus 正数前添加“+”号
Flags.Space 正数前添加一个空格
Flags.Zero 从左起填充“0”
下面是一个输出前缀为十六进制数的例子
auto text =Integer.format(new char[32],12345L,Format.HexUpper,Flags.Prefix)
toString,toString16,toString32也能使用format的格式标志和修改标志,但更为简单,它们不需要预分配输出数组。
toInt和toLong提供从字符串到整数值类型转换的方便包装,它们要求输入字符串必须全部可解析,同时,任意提供的radix在字符串将被推翻。如果输入字符串不能被完整解析将会抛出一个异常。
附加函数包括convert,它不查找输入字符串中的radix。trim会试图从字符串中提取任意的符号和radix,整理字符串的空格。
atoi和itoa用于快速从字符串转换到uint类型和从uint类型转换到字符串,后者将仅用于当输入已经知道有根据的情况。
Float
Tango.text.convert.Float模块提供给用户快速容易地转换浮点数(Float/duble/real)到字符串和从字符串转换到浮点数。下面 的例子是parse和format的默认用法:
auto  value = Float.parse(“3.145”);
auto  text = Float.format( new char[64],3.145);
parse仅有一个附加参数可以使用,一个指向一个uint型的ate参数控制字符串解析的长度到提供的结果。
format不同,有两个附加 参数,一个给十进制数用(默认为6),一个bool型指明格式是否为科学计数法表示(默认为false)。
toString,toString16,toString32可以使用format一样的参数,但不需要预分配的数组,它返回一个函数名指明的具体类型数组。
toDouble是从字符串转换到浮点数的最简单形式,返回一个double值。如果提供的输入不能完整解析,会抛出一个异常。
Layout(布局)
 Layout模板类有一个convert()方法可以用来动态格式化文本,并且被一些别的模块如Stdout 和 Sprint使用。
  一般说来,Layout工作要么调用sprint()要么调用convert()。opCall别名为convert。
  不同的是,convert 并堆分配结果,然而用户可以为结果提供一个缓冲器给sprint方法。
  在Format中的sprintf函数大体等价物是静态的Format.sprint 函数它要取得一个格式化字符串和一些参数,并且产生一个输出字符串。(这是一个超越sprintf的很好的改进,由于sprintf没有给你机会,你会溢出输出缓冲器)。
布局格式化字符串(Layouts format string)
语法和C#语法接近
例子:
Layout!(char) Layouter = new Layout!(char)();
int   nError = 12;
char  []  res =Layouter.format(“Error{} occurred.”,nError);//”Error 12 occurred.”
注意格式器的opCall影响使.format语法冗长,以下两个调用是等价的:
char []  res1 = Layouter(“Error{} occurred.”,nError);//”Error 12 occurred.”
char []  res2 = Layouter.format(“Error{} occurred.”,nError);//”Error 12 occurred.”
用简洁的.formatln()进行转换和添加一个新行。
利用D的metadata,格式器不需要格式字符串告之你要格式的数据类型。在上例中{}将会替换为nError的值,如果我们要具体说明用几位数字,是什么数制该如何办呢?
在{}中的文字有这个格式:
’{’ [ArgIndex][,Alignment][:FormatSpecifier]’}’
’{’ [参数索引][,对齐协定][:格式细节]’}’
参数索引指出使用第几个参数(从0数起)。如果没有参数索引,通常将用下一个参数,大多数时候,不需要使用它,只需要简单地使用{}就行了。如果要从一个文件中读取格式字符串,并处理不同的译文,参数索引就非常有用了。因为不同的语言翻译字符串可能在不同的地方,所以需要使用确切的参数索引值。另外,多次使用同一个参数出因参数索引而显得方便。
对齐协定,如果是正的,文字被右对齐,不足的字符位置填充空格,如果对齐协定是负的,则进行左对齐,如
Layouter("->{,10}<-'',"Hello");// ”->     Hello<-”
Layouter("->{,-10}<-'',"Hello");// ”->Hello     <-”
对齐协定不限定参数大小,它仅填充必要的空格,如
Layouter("->{,5}<-'',"HelloHello");// ”->HelloHello<-”
数字格式(即第三个可选项“格式细节”
格式 d x X B,b O,o
描述 十进制(默认) 小写十六进制 大写十六进制 二进制 八进制
格式字母后再加数字表示要用几位最少数字来表示,如下例:
Layouter(“{}”,123;   //”123”  默认十进制
Layouter(“{:d}”,123;   //”123”  十进制
Layouter(“0x{:x}”,123;   //  ”0x7b”  小写十六进制
Layouter(“{:4}”,123;   //”0x007b”  小写十六进制,最少4位
Layouter(“0x{:X}”,123;   //”0X7B”  大写十六进制
Layouter(“0o{:o}”,123;   //”0o173”  八进制
Layouter(“0b{:b}”,123;   //”0b1111011” 二进制
数组类型
Layout能格式数组类型
Int [] a = [123,456];         //动态数组
Ushort[3] b = [cast(ushort) 1,2,3];    //静态数组
Layouter (“{},a);   //           ”[123,456]”
Layouter (“{},b);   //   “[1,2,3]
Char[] [double] f ;
F[1.0] = “one”.dup;
F[3.14}= “PI”.dup;
Layouter (“{},f);   //   “{1.00=>one,3.14=>PI}”
数字格式和对齐协定可以应用到数组元素  map的情况下,数字格式和对齐协定也能应用。
如果要输出左大括号,就重复写两次,右大括号不用重复写。
使用Sprint或cstom sink(自定义搜索)。
Sprint成员函数超过format调用的好处是,没有堆活动(heap activity),Layout.sprint方法需要一个预分配的缓冲器。
警告:在多线程程序中,不要使用全局缓冲器,这样会使数据讹误。
如果需要的数据总量不太大,可使用一个基于栈的缓冲器:
char[250]  buf = void;  //仅获得一个缓冲器,没有初始化
char[] res = Layouter.sprint(buf,”My formatter number{}”,123);
也可以使用一个自己调用返回委托去消耗Layout数据。
void sink(char[] text){
//do something with text portion
Return text.Length;   //number of consumed chars
}
char[] res = Layouter(&sink,”My formatted number {}”,123);
Sprint
Sprint是一个格式前端,预分配一个输出缓冲器。Sprint接受标准C#风格格式,结果被缓冲器大小约束。Sprint作为Format相同风格的函数是:Printf函数家族的代替者。Sprint在tango.text.convert.Sprint模块中。
当你希望为 Logger(日志记录器)(或相类似的器)格式文字时,sprint是很方便的。它避免使用固定大小缓冲器进行转换期间产生堆活动Sprint类本身是stateful(独立的?),一个简单的实例不能被多个线程共享。
//创建一个Sprint实例
auto sprint = new Sprint!(char);
//写格式化的文字到控制台
Cout(sprint(“{0} is  {1} times {2}”,”julio”,32,5.68732));
如上例所见,Sprint可使用char,wchar,dchar进行模板实例化。
Utf
tango.text.convert.Utf模块包含所有的在各种通用编码(unicode)间互相转换的函数,包括ASCII子集。
toString,toString16,toString32分别返回char[],wchar[]和dchar[]。
例子:
auto utf8 = toString(“test”);
auto utf16 = toString16(“test”);
auto utf32 = toString32(“test”);
这些函数的完整原形为
T[] toStringⅩⅩ(U[] input, T[] output=null,uint* ate =null)
output参数用于用户想提供一个预分配空间给结果的情况,而ate参数提供给调用者将有几个字符会被处理。
UnicodeBom
tango.text.convert.UnicodeBom模块用于把内容编码成带BOM(字节序标志)的通用编码或从通用编码中解码。你可以间接地通过tango.io.UnicodeFile使用它,不过它直接使用也很方便。
通过检测最开始的少量特定部分字节,UnicodeBom可以自动检测文件的编码形式。了可以使用提供的常量Encoding.UTF_16BE,Encoding.UTF_32LE等等明确指定是什么编码方式。
举个例子,我们读和转化一wh带有BOM标志的文件内容到char[]数组并在控制台显示:
auto file =new File(“myfile.txt”);
auto bom = new UnicodeBom!(char)(Encoding.Unknown);
Stdout(bom.decode(file.read));
getEncodint和getSignature让用户获取关于BOM的信息,setup让用户设置(或重置)编码。
编码 标志 signature 描述
Encoding.Unknown N/A 完全未知编码
Encoding.UTF_8 N/A 预期UTF_8编码
Encoding.UTF_8N x”efbbbf” 明确设置为UTF_8N编码
Encoding.UTF_16 N/A 预期UTF_16编码
Encoding.UTF_16BE x”feff” 明确设置为UTF_16BE编码(大端)
Encoding.UTF_16LE x”fffe” 明确设置为UTF_16LE编码(小端)
Encoding.UTF_32 N/A 预期UTF_32编码
Encoding.UTF_32BE x”0000feff” 明确设置为UTF_32BE编码(大端)
Encoding.UTF_32LE x”fffe0000” 明确设置为UTF_32LE编码(小端)
encode和decode将会在不同编码间转换,取决于实例如何设置。encode把提供的内容编码成指定的unicodeBom类型,decode把输入数据按特定类型(或检测到的类型)解码成具体类型的数据。
TimeStamp(时间戳)
使用tango.text.convert.TimeStamp模块,HTML时间戳可以从字符串类型转换到ulong类型或从ulong类型转换到字符串类型。转换器会把任意的RFC1123、RFC850、asctime、Dostime或ISO-8601格式作为输入字符串,返回从1970年1月1日起的秒数,或相反地,转换从那天起的秒数到RFC1123时间(字符)串。
在下例中,parse和format的默认使用被展示。
auto date =”Sun,06 Nov 1994 08:49:37 GMT”;//RFC1123时间字符串
auto msSinceEpoch =TimeStamp.parse(date);
auto text = TimeStamp.format(new char[64],msEpoch);
parse可使用一个附加参数,一个指向uint类型的ate参数控制被解析的字符串长度,这个函数尝试与RFC1123、RFC850和asctime格式对比,如果都失败将返回InvalidEpoch(无效)值,toTime是一个更简单的封装,如果输入字符串不能被完整解析,将会抛出一个异常。
format没有附加参数,这和其它通用转换器模块不同,它把输入时间看作一个ulong值类型,toString,toString16,toString32封装这个函数,以免用户需要提供一个预分配缓冲器给结果。
要转换Dos time和ISO-8601格式,分别使用dostime和iso8601.随之还有rfc1123、rfc850和asctime返回元素在输入字符串中被解析的部分,结果值通过一个inout参数被提供。抽有这些函数的通用外形特征可看作
int fromTimeFormat(T[] src,inout ulong value)
fromFormat可以是上述五个函数中的任一种,输入源可以是char[],wchar[]或dchar[]中的任一种。
通用转换
Tango通过tango.util.Convert模块中的to函数支持通用值转换,这个函数允许你在任意类型间进行值保护转换。
如    real pi = to!(real)(“3.14159”);
上面的例子将把字符串值3.14159转换成最接近的等价实数类型值。有两种情况to函数转换会失败:
1.如果to不能找到在给定的两种类型间合适的转换器,它会流出一个编译时错误信息给你,如不能把一个虚数类型转换为一个整数类型,它们间是完全不相干的两个范畴。
2.如果to函数发现在运行时不能用目标类型描述给定的值,它将抛出一个Conversion Exception异常。例如,不能把字符串值“256”、“-1”或“que?”转换成ubyte类型,否则会抛出此异常。
下表是to函数支持的转换
目标类型 源类型
bool 整数型(0/!0)和字符串类型(”true”/”false”)
整数类型 bool、实数类型和字符串类型
实数类型 整数类型、字符串类型
虚数类型 复数类型
复数类型 整数类型、实数类型、虚数类型
字符串类型 bool、整数类型、实数类型
当然也可以在不同类型的数组间进行转换,不同类型的关联数组间转换。按顺序从一个数组T[]转换到数组S[],需要能够从每个T转换到每个S,要在关联数组Va[Ka]和Vb[Kb]间进行转换,需要能从每个Va转换到Vb,从每个Ka转换到Kb。
最后,to可以扩展到支持用户自定义的结构和类,这样做要在你自定义类型中定义适当的静态和实例成员函数。下表归纳了应包括的规则
目标 源 调用尝试
dst_type dst src_type src  src.toDstType(),src.to!(dst_type)(),dst_type.fromType(src),
dst_type.from!(src_type)(src)
dst_type dst[] src_type src src.toDstTypeArray()
dst_type dst src_type[] src dst_type.fromDstTypeArray(src)
dst_type[key_type] src_type src Src.toKeyTypeToDstTypeMap(0
char[],wchar[],
dchar[] src_type src src.toString(),src.toString16(),src.toString32()
dst_type  char[],wchar[],
dchar[] src dst_type..fromUtf8(src),dst_type..fromUtf16(src),dst_type..fromUtf32(src)
注意:在字符串转换情况下,如果合适的UTF编码不能直接支持,Tango将会转换到无论哪个可行的编码,然后翻译结果到期待的类型。也要注意,如果特定名称的方法没找到,to会落到通用的src.to!(dst_type)()和dst_type.from!(src_type)(src)调用上来。
这里是一个简单的用户自定义结构例子,招待一些转换。
struct Foo
{
real value;
int toint()
{
      Return to!(int)(value);
     }
static Foo fromReal!(real v)
     {
     return Foo(v);
     }
 T to(T))
     {
static if(is(T==iral))
retirm value*-1.0i;
else
static assert(false);
}
static Foo from(T)(T src)
   {
static if(is(T==iral))
return Foo(src*-1.0i);
else
static assert(false);
  }
}
下面的转换是有效的
to!(int)(Foo(42));  // ==42
to!(Foo)(3.14159);  // ==Foo(3.14159)
to!(char[])(Foo(123.456));  //==”123.456”c
to!(ireal)(Foo(456.789));   //==-456.789i
to!(Foo)(-789.123i);  //==Foo(789.123)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值