这篇文章的标题看似简单,那就先从简单的说起。众所周知:
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 等整数类型(为什么需要显示转换呢?在下面会详细讨论)。例如:
1
2
3
4
|
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()方法,例如
1
2
3
|
string
ss=
"abcdefg"
;
char
[] cc=ss.ToCharArray();
//实际上多数情况下不需要这么做,因为使用ss[N]可以访问指定下标的字符对象。
|
2、Char[] 转换成string,就更简单了,可以直接用string的构造函数
1
2
|
//char [] cc 定义同前段代码
string
s=
new
string
(cc);
|
前面说的是字符串和字符数组之间的转换,下面说说字符串和字节数组间怎么转换。
虽然只差一个字,但区别可就大了,因为这涉及编码……
请先看下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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/ | 小谢的小站