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

4 篇文章 0 订阅
1 篇文章 0 订阅

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

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/ | 小谢的小站

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值