Unicode编码、ASCII码、UTF-8编码、GB2312编码分析总结
一、由简到繁顺序来总结:
1、ASCII编码:
在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态。一个字节(byte)共由八个二进制位来组成,共有256种状态,从 0000000到11111111。
阿拉伯数字、英文字母、标点符号等这些字符,怎么定义才能让计算机识别呢?因为计算机只识别二进制位0和1,所以以上这些字符就必须与二进制位(0和1)建立关系,才能让计算机识别。
60年代初,计算机界制定了一套统一的字符编码,来表示字符与二进制位之间的关系。这种统一的字符编码就叫做ASCII编码。ASCII码一共规定了128个字符的编码,比如空格“SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。
在英语国家,128个ASCII编码足以表达所有字符,但其它非英语国家,字符不是由英文字符组成,这样就需要增加编码以表达这些字符,对于超过128个字符的编码被称为非ASCII编码。比如:在中国,我们用简体中文,字符编码方式为GB2312。
2、Unicode编码:
Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。码位就是可以分配给字符的数字。Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。Unicode编码可以表示字符很多,所以采用不定字节长来表示。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。
3、UTF-8编码
UTF-8编码是Unicode 的实现方式之一。UTF-8以字节为单位对Unicode进行编码。它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
4、Unicode与UTF-8编码的转化
从Unicode到UTF-8的编码方式如下:
Unicode编码(16进制) ║ UTF-8 字节流(二进制)
000000 - 00007F ║ 0xxxxxxx
000080 - 0007FF ║ 110xxxxx 10xxxxxx
000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF ║ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。
例1:“汉”字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
UTF-16与UTF-32在此不做介绍。
5、GB2312编码方式:
我国采用简体中文,所有中文字符都采用GB2312编码方式。
GB2312编码使用的是区位码寻字方式,1-9区存放中文符号,16-55区存放一级汉字,56-87区存放二级汉字,如第1个汉字位于16区的第1位,为:'啊',所以1601,即0x1001就代表了该'啊'的区位码值.
区位码变换为机器内码需要分别将区码和位码加0xA0,所以'啊'的机器内码就是,0xB0A1
4.国际码=区位码+0x2020
机器内码=国际码+0x8080 也就是=区位码+0xa0a0
6、Unicode编码与GB2312编码的关系
Unicode对汉字进行了重新编码,这和GB2312编码的方式和顺序完全不同,unicode对汉字编码从0x4E00开始,到0x9FA5为止,所以Unicode和GB2312编码的转换,就需要一个转换对照表,实现快速转换。
7、转义字符序列与Unicode编码关系
一般转义字符序列用‘\?’表示,?代表所想表达的字符。如:‘\n’代表换行标志。转义字符序列也可以用\U????来表示。????代表16进制的Unicode编码。如:‘\u0066’代表字符‘f‘。
例:"my name\'s GuoXiong"与"my name\u0027s GuoXiong"//都是表示my name's GuoXiong
C#字符串、字节数组和内存流间的相互转换
定义string变量为str,内存流变量为ms,比特数组为bt
1.字符串=>比特数组
(1)byte[] bt=System.Text.Encoding.Default.GetBytes("字符串");
(2)byte[] bt=Convert.FromBase64String("字符串");
补充:
System.Text.Encoding.Unicode.GetBytes(str);
System.Text.Encoding.UTF8.GetBytes(str);
System.Text.Encoding.GetEncoding("gb2312").GetBytes(str); //指定编码方式
string str = "中国?ss123?";
byte[] bytes = System.Text.Encoding.Default.GetBytes(str); //gb2312编码 汉字占2个字节、英文字母占1个字节 bytes长度为12
string s = System.Text.Encoding.Default.GetString(new byte[] { bytes[0],bytes[1] });//解码后为“中”
byte[] bytes = {97, 98, 99, 100, 101, 102};
string str = System.Text.Encoding.ASCII.GetString(bytes); //结果为:abcdef ASCII码表
常用方法:
GetString 已重载。 在派生类中重写时,将一个字节序列解码为一个字符串。
GetChars 已重载。 在派生类中重写时,将一个字节序列解码为一组字符。
GetBytes 已重载。 在派生类中重写时,将一组字符编码为一个字节序列。
GetByteCount 已重载。 在派生类中重写时,计算对一组字符进行编码所产生的字节数。
GetCharCount 已重载。 在派生类中被重写时,计算对字节序列进行解码所产生的字符数。
GetDecoder 在派生类中重写时,获取一个解码器,该解码器将已编码的字节序列转换为字符序列。
GetEncoder 在派生类中重写时,获取一个解码器,该解码器将Unicode字符序列转换为已编码的字节序列
2.比特数组 => 字符串
(1)string str=System.Text.Encoding.Default.GetString(bt);
(2)string str=Convert.ToBase64String(bt);
3.字符串 => 流
(1)MemoryStream ms=new MemoryStream(System.Text.Encoding.Default.GetBytes("字符串"));
(2)MemoryStream ms=new MemoryStream(Convert.FromBase64String("字符串"));
4.流 => 字符串
(1)string str=Convert.ToBase64String(ms.ToArray());
(2)string str=System.Text.Encoding.Default.GetString(ms.ToArray());
5.比特数组 => 流
(1)MemoryStream ms=new MemoryStream(bt);
(2)MemoryStream ms=new MemoryStream();ms.Read(bt,0,bt.Lenght);
6.流 => 比特数组
(1)byte[] bt=ms.ToArray();
(2)MemoryStream ms=new MemoryStream();ms.Write(bt,0,ms.Length);
1、两种不同的方法计算字符串的长度
string strTmp = "wk986王克东";
int i = System.Text.Encoding.Default.GetBytes(strTmp).Length; //算汉字的长度
int j = strTmp.Length; //不算汉字的长度
Console.WriteLine("字符串{0},算汉字的长度:{1},不算汉字长度:{2}", strTmp,i,j);
//转换成数组计算数组的长度
byte[] bytStr = System.Text.Encoding.Default.GetBytes(strTmp);
int len = bytStr.Length;
Console.WriteLine("字符串长度:"+len.ToString());
Console.Read();
2、System.Text.StringBuilder("")
和字符串“+”是不一样的,在C#中,字符串是“引用”类型,每加一个是重新建立了一个字符串,当字符串特别大的时候,性能消耗大,所以要用StringBuilder。
System.Text.StringBuilder sb = new System.Text.StringBuilder("");
sb.Append("中华");
sb.Append("人民");
sb.Append("共和国");
Console.WriteLine(sb);
//判断汉字个数
private int ChkGBKLen(string str)
{
System.Text.ASCIIEncoding n = new System.Text.ASCIIEncoding();
byte[] b = n.GetBytes(str);
int l = 0;
for (int i = 0; i <= b.Length - 1; i++)
{
if (b[i] == 63) //判断是否为汉字或全脚符号
{
l++;
}
}
return l;
}
C#中流,字节,字符,字符串
首先要明白它们本身是由什么组成的:
流:二进制
字节:无符号整数
字符:Unicode编码字符
字符串:多个Unicode编码字符
那么在.net下它们之间如何转化呢?
一般是遵守以下规则:
流->字节数组->字符数组->字符串
下面就来具体谈谈转化的语法:
流->字节数组
MemoryStream ms = new MemoryStream();
byte[] buffer = new byte[ms.Length];
ms.Read(buffer, 0, (int)ms.Length);
字节数组->流
byte[] buffer = new byte[10];
MemoryStream ms = new MemoryStream(buffer);
字节数组->字符数组
1.
byte[] buffer = new byte[10];
char[] ch = new ASCIIEncoding().GetChars(buffer);
//或者:char[] ch = Encoding.UTF8.GetChars(buffer)
2.
byte[] buffer = new byte[10];
char[] ch = new char[10];
for(int i=0; i<buffer.Length; i++)
{
ch[i] = Convert.ToChar(buffer[i]);
}
字符数组->字节数组
1.
char[] ch = new char[10];
byte[] buffer = new ASCIIEncoding().GetBytes(ch);
//或者:byte[] buffer = Encoding.UTF8.GetBytes(ch)
2.
char[] ch = new char[10];
byte[] buffer = new byte[10];
for(int i=0; i<ch.Length; i++)
{
buffer[i] = Convert.ToByte(ch[i]);
}
字符数组->字符串
char[] ch = new char[10];
string str = new string(ch);
字符串->字符数组
string str = "abcde";
char[] ch=str .ToCharArray();
字节数组->字符串
byte[] buffer = new byte[10];
string str = System.Text.Encoding.UTF8.GetString(buffer);
//或者:string str = new ASCIIEncoding().GetString(buffer);
字符串->字节数组
string str = "abcde";
byte[] buffer=System.Text.Encoding.UTF8.GetBytes(str);
//或者:byte[] buffer= new ASCIIEncoding().GetBytes(str);
说明:主要就是用到了Convert类和System.Text命名空间下的类,Encoding是静态类,ASCIIEncoding是实体类,方法都是一样的!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace Encoding
{
class Program
{
static void Main(string[] args)
{
var gb1 = System.Text.Encoding.GetEncoding("Unicode");
Console.WriteLine(gb1.GetString(gb1.GetBytes("测试")));
var gb0 = System.Text.Encoding.GetEncoding("UTF-8");
Console.WriteLine(gb0.GetString(gb0.GetBytes("测试")));
var gb = System.Text.Encoding.GetEncoding("GB2312");
Console.WriteLine(gb.GetString(gb.GetBytes("测试")));
#region 测试代码
Console.WriteLine(IsChineseLetter("测试", 0));
Console.WriteLine(ChineseLetterCode("测试", 0));
Console.WriteLine(ChineseLetterFromCode(ChineseLetterCode("测试", 0)));
Console.WriteLine(gb1.GetString(
Utf8_2_Unicode(gb0.GetBytes("测试test"))));
//http://qkzz.net/article/3d697483-a5ae-4b50-9ae9-45dc6dd26141.htm
//http://topic.csdn.net/u/20090617/18/1907627e-ce38-4ae5-9755-1cc349a4ed1a.html
//一级汉字有 3755 个, 40 * 94=3760 个, 其中 d7fe, d7fd, d7fc, d7fb, d7fa 五位置为空
for (byte i = 0xb0; i < 0xd8; i++)
{
for (byte j = 0xa1; j < (i != 0xd7 ? 0xff : 0xfa); j++)
{
Console.Write(gb.GetString(new[] { i, j }));
if (j == 0xc7 || j == 0xee || j == (i != 0xd7 ? 0xfe : 0xf9))
Console.WriteLine();
}
}
Console.WriteLine(GetChineseLetterFromGb2312(0));
Console.WriteLine(GetChineseLetterFromGb2312(3754));
//汉字的 Unicode 编码范围
for (var i = 19968; i <= 40959; i++)
{
Console.Write(ChineseLetterFromCode(i));
}
#endregion
Console.Read();
}
public static string GetChineseLetterFromGb2312(int rNum)
{
if (rNum < 0 || rNum > 3754)
throw new ArgumentOutOfRangeException("rNum", "超出一级汉字的范围!");
var gb = System.Text.Encoding.GetEncoding("GB2312");
return gb.GetString(new[] { (byte)(0xb0 + (rNum / 94)), (byte)(0xa1 + (rNum % 94)) });
}
/// <summary>
/// UTF8 汉字字节流转成 Unicode 汉字字节流
/// </summary>
/// <param name="input"></param>
/// <see cref="http://hi.baidu.com/hyqsoft/blog/item/263795a164d1728346106464.html"/>
public static byte[] Utf8_2_Unicode(byte[] input)
{
var ret = new List<byte>();
for (var i = 0; i < input.Length; i++)
{
if (input[i] >= 240) // 11110xxx
{
//i += 3;
throw new Exception("四字节的 UTF-8 字符不能转换成两字节的 Unicode 字符!");
}
//else if (input[i] >= 224)
if (input[i] >= 224) // 1110xxxx
{
ret.Add((byte)((input[i + 2] & 63) | ((input[i + 1] & 3) << 6)));
ret.Add((byte)((input[i] << 4) | ((input[i + 1] & 60) >> 2)));
i += 2;
}
else if (input[i] >= 192) // 110xxxxx
{
ret.Add((byte)((input[i + 1] & 63) | ((input[i] & 3) << 6)));
ret.Add((byte)((input[i] & 28) >> 2));
i += 1;
}
else
{
ret.Add(input[i]);
ret.Add(0);
}
}
return ret.ToArray();
}
#region 汉字与Unicode编码
public static bool IsChineseLetter(string input, int index)
{
var chfrom = Convert.ToInt32("4e00", 16); //范围(0x4e00~0x9fff)转换成int(chfrom~chend)
var chend = Convert.ToInt32("9fa5", 16);
if (input != "")
{
//var code = Char.ConvertToUtf32(input, index);
var gb = System.Text.Encoding.GetEncoding("Unicode");
var b = gb.GetBytes(input.Substring(index, 1));
var code = b[0] + b[1] * 0x100;
return code >= chfrom && code <= chend;
}
return false;
}
public static int ChineseLetterCode(string input, int index)
{
var chfrom = Convert.ToInt32("4e00", 16); //范围(0x4e00~0x9fff)转换成int(chfrom~chend)
var chend = Convert.ToInt32("9fa5", 16);
if (input != "")
{
var code = Char.ConvertToUtf32(input, index);
return code >= chfrom && code <= chend ? code : 0;
}
return 0;
}
public static string ChineseLetterHexCode(string input, int index)
{
var code = ChineseLetterCode(input, index);
return code != 0 ? code.ToString("X4") : string.Empty;
}
public static string ChineseLetterFromCode(int code)
{
var chfrom = Convert.ToInt32("4e00", 16); //范围(0x4e00~0x9fff)转换成int(chfrom~chend)
var chend = Convert.ToInt32("9fa5", 16);
//return code >= chfrom && code <= chend ? Char.ConvertFromUtf32(code) : string.Empty;
if (code >= chfrom && code <= chend)
{
var gb = System.Text.Encoding.GetEncoding("Unicode");
var b = new[] { (byte)(code % 0x100), (byte)(code / 0x100) };
return gb.GetString(b);
}
return string.Empty;
}
public static string ChineseLetterFromHexCode(string hexCode)
{
//var code = Convert.ToInt32(hexCode, 16);
var code = int.Parse(hexCode, NumberStyles.HexNumber);
return ChineseLetterFromCode(code);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace QuotedPrintable解码
{
class Program
{
static void Main(string[] args)
{
string init_str = "K度空间";
string encode_str = QuotedPrintable.Encode(init_str);
string decode_str = QuotedPrintable.Decode(encode_str);
Console.WriteLine(
"需要进行QuotedPrintable编码的原始字符串: " +
" {0} " +
"编码后得到的字符串: " +
" {1} " +
"对编码后得到的字符串进行解码得到的字符串: " +
" {2} ", init_str, encode_str, decode_str);
}
public class QuotedPrintable
{
/// <summary>
/// QuotedPrintable解码函数
/// </summary>
/// <param name="input">需要解码的QuotedPrintable字符串</param>
/// <returns>解码后的字符串</returns>
public static string Decode(string input)
{
System.Text.Encoding encoding = System.Text.Encoding.GetEncoding("gb2312");
StringBuilder result = new StringBuilder();
StringReader sr = new StringReader(input);
string line = sr.ReadLine();
while( line!=null )
{
bool addCRLF = true;
byte[] bytes = System.Text.Encoding.ASCII.GetBytes( line.ToCharArray() );
for(int i=0;i<bytes.Length;i++)
{
if( ( bytes[i]>=33 && bytes[i]<=60 ) || ( bytes[i]>=62 && bytes[i]<=126 ) || bytes[i]==9 || bytes[i]==32)
{
result.Append( Convert.ToChar( bytes[i] ) );
continue;
}
else if( bytes[i]==61 )
{
if( i==bytes.Length-1 )
{
//eg. = soft line break;
addCRLF = false;
break;
}
else if( bytes[i+1]==51 && bytes[i+2]==68 )
{
//eg. =3D
i++;i++;
result.Append( "=" );
continue;
}
else
{
//eg. =B7=D6
byte[] b = new byte[2];
b[0] = Convert.ToByte( Convert.ToChar(bytes[i+1]).ToString()+Convert.ToChar(bytes[i+2]).ToString(),16 );
i++;i++;i++;
b[1] = Convert.ToByte( Convert.ToChar(bytes[i+1]).ToString()+Convert.ToChar(bytes[i+2]).ToString(),16 );
i++;i++;
result.Append(encoding.GetString(b));
continue;
}
}
}//end of for
line = sr.ReadLine();
if( line!=null && addCRLF )
result.Append(" ");
}//end of while
return result.ToString();
}
public static string Encode(string input)
{
System.Text.Encoding encoding = System.Text.Encoding.GetEncoding("gb2312");
const int MAXLINELENGTH = 76;
int currentLineLength = 0;
byte[] bytes = encoding.GetBytes(input.ToCharArray());
StringBuilder result = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
if (bytes[i] == 10 || bytes[i] == 13)
{
if (bytes[i] == 13 && GetNextByte(i, bytes, 1) == 10)
{
CheckLineLength(MAXLINELENGTH, ref currentLineLength, 0, result);
result.Append(" ");
currentLineLength = 0;
i++;
continue;
}
if (bytes[i] == 10)
{
CheckLineLength(MAXLINELENGTH, ref currentLineLength, 0, result);
result.Append(" ");
currentLineLength = 0;
}
if (bytes[i] == 13)
{
CheckLineLength(MAXLINELENGTH, ref currentLineLength, 3, result);
result.Append("=" + ConvertToHex(bytes[i]));
}
}
else
{
if ((bytes[i] >= 33 && bytes[i] <= 60) || (bytes[i] >= 62 && bytes[i] <= 126))
{
CheckLineLength(MAXLINELENGTH, ref currentLineLength, 1, result);
result.Append(System.Convert.ToChar(bytes[i]));
}
else
{
if (bytes[i] == 9 || bytes[i] == 32)
{
CheckLineLength(MAXLINELENGTH, ref currentLineLength, 0, result);
result.Append(System.Convert.ToChar(bytes[i]));
currentLineLength++;
}
else
{
CheckLineLength(MAXLINELENGTH, ref currentLineLength, 3, result);
result.Append("=" + ConvertToHex(bytes[i]));
}
}
}
}
return result.ToString();
}
private static void CheckLineLength(int maxLineLength, ref int currentLineLength, int newStringLength, StringBuilder sb)
{
if (currentLineLength + 1 == maxLineLength || currentLineLength + newStringLength + 1 >= maxLineLength)
{
sb.Append("= ");
currentLineLength = 0+newStringLength;
}
else
{
currentLineLength += newStringLength;
}
}
private static int GetNextByte(int index, byte[] bytes, int shiftValue)
{
int newIndex = index + shiftValue;
if (newIndex < 0 || newIndex > bytes.Length - 1 || bytes.Length == 0)
return -1;
else
return bytes[newIndex];
}
private static string ConvertToHex(byte number)
{
string result = System.Convert.ToString(number, 16).ToUpper();
return (result.Length == 2) ? result : "0" + result;
}
}
}
}