TearSnow Fan


Char, String 和 Byte 等类型间的转换和编码

这篇文章的标题看似简单,那就先从简单的说起。众所周知:

1、char 是字符类型;

2、string 是字符串类型;

两者都是及其常用数据类型。虽然一字之差,但是有本质区别:

1、char 在C++中是基础类型,在C#中是值类型(Value Type)。因此,一个 char 类型的对象所占用的控件总是确定的(不要认为是1个字节!Byte才是)。在 C++中,一个char变量可能会是1个字节,也可能是2个字节,这取决于它是否是Unicode Char(也就是所谓的wchar_t)。然而在C#中,它永远是2个字节(没错,是永远,因为其定义就是16位Unicode字符,详见MSDN)。

2、string 是一个模板类(C++),也就是一个class了。在C#中,string是引用类型(Reference Type,即使在某些方面有一定值类型的特点)。因此,string对象占用的空间是可变的,理论上只受内存限制,也无法通过 sizeof 来取得的(sizeof 是C++和C#中中的作用一样)。

其用法当然也是众所周知的:

1、char 类型用单引号和一个字符来表示,例如: 'A' ,或者 '谢' 。那么要表示多个字符怎么办,就用字符数组 char [] 就可以了。

这里还需要注意一个问题,就是C++里面将一个汉字赋值给一个char类型变量会丢失信息(因为其只占用1个字节),而应该用wchar_t类型。

2、string 类型用双引号表示,例如:"你好再见。"

下面要开始探讨转换了。

1、char 类型虽然说是字符类型,但是可以隐式或者显式地转换为short, int, long 等整数类型(为什么需要显示转换呢?在下面会详细讨论)。例如:

char c1 = 'A';
Console.WriteLine((short) c1);  //输出为 65
char c2 = (char) 97;
Console.WriteLine(c2);    //c2值为 'a'

这两个值相信很多人都能背下来:大写A的ASCII编码值为65,小写a为97。注意一个问题,在C#中,Char的编码是Unicode,根本不是什么ASCII,因为定长16位的原因,甚至也不能说“兼容ASCII”,但好在这些ASCII字符的数值起码还是一样的。

想到点别的顺便废话一下,想说在Excel中的两个函数Char()和Code():

=CHAR(97) 会返回“a"

=CHAR(98) 会返回“b"

=Code(“a") 会返回“97"

=Code(“b") 会返回“98"

以此类推……

2、char 类型既然可以用来存储数值,就必然有 signed 和 unsigned 之分。在C++中,

  • signed char 是8位有符号数,取值范围 -128 到 +127;
  • unsigned char 是8位无符号数,取值范围从 0 到 255 。

但是在C#中,Char 被规定为无符号的,Byte 类型也是无符号的:

a)Byte 变量以无符号的 8 位(1 个字节)数字的形式存储,取值范围为 0 到 255,因此Byte 数据类型可以隐式转换为 short、int、long、Single、Double 或 Decimal 数据类型(好像是废话)。

b)Char 变量以无符号的 16 位(2 个字节)数字的形式存储,取值范围为 0 到 65535(因为其用于表示本来是用来代表 Unicode 字符,Unicode 哪来的负数)。所以重新考虑上面的几种 Byte 可以隐式转换成的数据类型,由于short是16位有符号数,不能完全覆盖 Char 所标示的数值范围,因此 Char 是不能够隐式地转换为 short 类型的。强制转换当然是可以的,但是要自己承担System.OverflowException的风险。这是很显然的,考虑下其占用的存储空间就能够明白,所以建议用Int16,Int32,Int64代替short,int和long的写法。

那么至于string类型,它需要占多少空间呢?

前面已经说了,string的大小是不可以通过sizeof来获得的。如果尝试通过sizeof取得string的大小,会遇到编译错误。理论上字符串中每个字符都和Char一样,应该占2个字节,而且需要考虑string的最后还有一个特殊的字符,是不可见的'\0',也占2个字节。(其实由于string是引用类型,考察其本身在堆上所占空间其实意义不大)

下面说说字符串和字符数组之间的转换,也就是 string 和 char []。

1、string 转换成 Char[] 需要用到 String 的 ToCharArray()方法,例如

string ss="abcdefg";
char[] cc=ss.ToCharArray();
//实际上多数情况下不需要这么做,因为使用ss[N]可以访问指定下标的字符对象。

2、Char[] 转换成string,就更简单了,可以直接用string的构造函数

//char [] cc 定义同前段代码
string s=new string(cc);

前面说的是字符串和字符数组之间的转换,下面说说字符串和字节数组间怎么转换。

虽然只差一个字,但区别可就大了,因为这涉及编码……

请先看下面的代码:

static void Main(string[] args)
{
    string input = "你好,再见。";//逗号为半角字符,句号为全角字符。
    Console.WriteLine(input);

    //测试Default编码
    Console.WriteLine("Default Encoding: {0}", Encoding.Default.EncodingName);
    byte[] bytesDefault = Encoding.Default.GetBytes(input);
    Console.WriteLine("Default Encoding Size: "+bytesDefault.Length);//返回11,每个汉字占2字节,半角逗号占1个字节

    //测试Unicode(就是UTF16)
    byte[] bytesUnicode = Encoding.Unicode.GetBytes(input);
    Console.WriteLine("Unicode/UTF16 Encoding Size: "+bytesUnicode.Length);//返回12,所有字符都用两个字节

    //测试UTF8
    byte[] bytesUTF8 = Encoding.UTF8.GetBytes(input);
    Console.WriteLine("UTF8 Encoding Size: " + bytesUTF8.Length);//返回16,所有字符都用两个字节

    //测试UTF32
    byte[] bytesUTF32 = Encoding.UTF32.GetBytes(input);
    Console.WriteLine("UTF32 Encoding Size: " + bytesUTF32.Length);//返回16,所有字符都用两个字节

    //测试ASCII
    byte[] bytesASCII = Encoding.ASCII.GetBytes(input);
    Console.WriteLine("ASCII Encoding Size: " + bytesASCII.Length);//返回6,所有字符都用一个字节(汉字和全角句号没有真正编码成功)
    Console.WriteLine("ASCII编码复原:{0}",Encoding.ASCII.GetString(bytesASCII));

    //从字符串生成Encoding对象,以及Unicode编码的WebName(utf-16)
    Console.WriteLine("Encoding.GetEncoding(\"unicode)\").WebName: {0}",System.Text.Encoding.GetEncoding("uniCode").WebName);

    Console.Read();
}

结果如图:

关于几种编码的介绍,详见《常见编码总结:Unicode、UTF、ISO 8859-1等》一文。上面的代码说明了以下几个问题:

1、如果操作系统的Current System Locale设置为“中国,简体中文”Chinese (Simplified,PRC),则默认的编码 System.Encoding.Default 就是"gb2312"。

如果你想得到这个编码,除了用System.Encoding.Default以外,还可以:

System.Text.Encoding.GetEncoding(936)
System.Text.Encoding.GetEncoding("gb2312")

当然,如果确定要使用GB2312编码,用后两个要比Default靠谱多了。

2、Encoding对象的 GetBytes() 方法和 GetString() 方法提供了 string 和 Byte[] 之间的转换,

3、可以认为 Unicode 编码就是 UTF-16,在Encoding.GetEncoding()方法参数中使用"unicode"和"utf-16"的名称是完全一样的,这个方法也不区分字符串的大小写,但是注意不能省略连字符,例如将utf-16写作utf16将无法识别。

4、ASCII编码是不能用于保存中文字符的,不要奢望系统会用ASCII字符当容器来封装Unicode一类的。但是诡异的是,居然也不抛出个异常……

还有一种比较特殊的转换也经常能够见到,尤其是HTTP协议中,这就是Base64编码。

Base64是网络上最常见的用于传输8-bits字节代码的编码方式之一,具体可以查看RFC2045~RFC2049,上面有MIME的详细规范。Base64编码可用于在HTTP环境下传递较长的标识信息。例如,在Java Persistence系统Hibernate中,就采用了Base64来将一个较长的唯一标识符(一般为128-bit的UUID)编码为一个字符串,用作HTTP表单和HTTP GET URL中的参数。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码不仅比较简短,同时也具有不可读性,即所编码的数据不会被人用肉眼所直接看到。

这么看来,base64编码需要解决的主要问题是把 Byte[] 转换为 Base64 编码的 String,以及复原(将 Base64String 恢复为 Byte [])。这可以通过 System.Convert静态类中的静态方法类实现。

详见MSDN:http://msdn.microsoft.com/library/dsfy6sz9.aspx

最后说个与string 有关的技巧:

由于C#中,string是不可变的。在动态构造字符串的时候(例如:result = result+" Test " ),其实会产生一个新的字符串,原有的result还会保留,新创建的一个的result是旧的result和“Test”连起来的结果。所以,为了更好的使用资源,应该使用StringBuilder。其Append()方法带有多种参数,可以对字符串进行各种组合操作,最后用其 ToString() 输出即可。

最后附个表,不要忘记有一些字符是比较特殊的,也就是所谓的“转义字符”。在使用char或者string的时候,都需要转义。

\b:   回退:向后退一格
\f:   换页 
\n:   换行,光标到下行行首
\r:   回车,光标到本行行首 
\t:   水平制表 
\v:   垂直制表 
\\:   反斜杠
\‘:   单引号
\":   双引号 
\?:   问号
\ddd: 三位八进制 
\xhh: 二位十六进制 
\0:   空字符(NULL),什么都不做。换行只是换一行,不改变光标的横坐标;回车只是回到行首,不改变光标的纵坐标。

参考资料:

[1] 博客园:char类型和string类型(作者:陈希章)

本文固定链接: http://blog.xieyc.com/casting-and-encodings-of-char-string-byte-etc/ | 小谢的小站

该日志由 xieyc 于2013年07月11日发表在 编程 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: Char, String 和 Byte 等类型间的转换和编码 | 小谢的小站
关键字: , ,

Char, String 和 Byte 等类型间的转换和编码:等您坐沙发呢!

发表评论

:wink: :neutral: :mad: :twisted: :smile: :shock: :sad: :roll: :oops: :eek: :mrgreen: :lol: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!:

快捷键:Ctrl+Enter