编码解析
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"http://都是表示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;
}
}
}
}
SQL基礎知識
總結
- 上一篇: 北京时间与太平洋时间相互转换
- 下一篇: BP: Useful report to