程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#編程總結(九)字符編碼

C#編程總結(九)字符編碼

編輯:關於C#

相信大家一定遇到過亂碼的問題,為什麼會亂碼呢?輸出的數據怎麼就跟輸入的不一樣呢?

最近在總結加密問題,也遇到了同樣的困擾。所以今天來集中解決這個問題。

什麼是字符?

字符是指計算機中使用的字母、數字、字和符號,包括:1、2、3、A、B、C、~!·#¥% ……—*()——+等等。

字符集(Charset)

字符集(Charset)是一個系統支持的所有抽象字符的集合。

字符是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。

什麼是字符編碼?

字符編碼(Character Encoding):簡單的說就是建立自然語言與機器語言之間的對應關系。是一套 法則,使用該法則能夠對自然語言的字符的一個集合(如字母表或音節表),與其他東西的一個集合( 如號碼或電脈沖)進行配對。即在符號集合與數字系統之間建立對應關系,它是信息處理的一項基本技 術。通常人們用符號集合(一般情況下就是文字)來表達信息。而以計算機為基礎的信息處理系統則是 利用元件(硬件)不同狀態的組合來存儲和處理信息的。元件不同狀態的組合能代表數字系統的數字, 因此字符編碼就是將符號轉換為計算機可以接受的數字系統的數,稱為數字代碼。

計算機中的信息包括數據信息和控制信息,數據信息又可分為數值和非數值信息。非數值信息和控制 信息包括了字母、各種控制符號、圖形符號等,它們都以二進制編碼方式存入計算機並得以處理,這種 對字母和符號進行編碼的二進制代碼稱為字符代碼(Character Code)。計算機中常用的字符編碼有 ASCII碼(美國標准信息交換碼)和EBCDIC碼(擴展的BCD交換碼)。

在 ASCII 編碼中,一個英文字母字符存儲需要1個字節。在 GB 2312 編碼或 GBK 編碼中,一個漢字 字符存儲需要2個字節。在UTF-8編碼中,一個英文字母字符存儲需要1個字節,一個漢字字符儲存需要3 到4個字節。在UTF-16編碼中,一個英文字母字符或一個漢字字符存儲都需要2個字節(Unicode擴展區的 一些漢字存儲需要4個字節)。在UTF-32編碼中,世界上任何字符的存儲都需要4個字節。

困擾與疑惑?

1、為什麼會有字符編碼?

含義介紹中已經給出了解釋,字符編碼就是讓計算機識別自然語言。

2、為什麼會有這麼多的字符集?

計算機發展分不同階段,最開始只是美國通用,建立了ASCII碼,但是一些歐洲國家字符無法使用 ASCII碼,然後就對ASCII進行了擴展補充,後來中國要使用計算機,為了標記漢語,定義了GB2312、GBK 、BIG5等,還有其他一些字符集。

3、就沒有一種統一的字符集嗎?

有,Unicode

4、UTF8與Unicode是什麼關系?

UTF8是Unicode的一種實現方式。

常用的幾種編碼

1. ASCII碼

ASCII(American Standard Code for Information Interchange,美國信息交換標准代碼)是基於 拉丁字母的一套電腦編碼系統。

ASCII字符集:主要包括控制字符(回車鍵、退格、換行鍵等);可顯示字符(英文大小寫字符、阿 拉伯數字和西文符號)。

特點:單字節編碼,只包含大小寫英文字母、標點符號及其他符號。

ASCII編碼:將ASCII字符集轉換為計算機可以接受的數字系統的數的規則。使用7位(bits)表示一 個字符,共128字符;但是7位編碼的字符集只能支持128個字符,為了表示更多的歐洲常用字符對ASCII 進行了擴展,ASCII擴展字符集使用8位(bits)表示一個字符,共256字符。

計算機為美國人發明,開始的時候也只能滿足自己,所以說此編碼很有局限性。在其他的國家、其他 語種無法使用此編碼集。

根據ASCII碼的編碼規則,最多只能標識256個字符,但是世界上有那麼多語言,漢字就多達10萬左右 ,那麼多字符,顯然很有局限性。

以下是ASCII代碼表,表二為其擴展表。

2、中文編碼

2.1 GB2312

特點:簡體中文字符集,采用雙字節編碼。

GB2312或GB2312-80是中國國家標准簡體中文字符集,全稱《信息交換用漢字編碼字符集·基 本集》,又稱GB0,由中國國家標准總局發布,1981年5月1日實施。GB2312編碼通行於中國大陸;新加坡 等地也采用此編碼。中國大陸幾乎所有的中文系統和國際化的軟件都支持GB2312。GB2312的出現,基本 滿足了漢字的計算機處理需要,它所收錄的漢字已經覆蓋中國大陸99.75%的使用頻率。對於人名、古漢 語等方面出現的罕用字,GB2312不能處理,這導致了後來GBK及GB 18030漢字字符集的出現。

2.2 GBK

特點:擴展了GB2312,包括非常用簡體漢字、繁體字、日語及朝鮮漢字等。

由於GB 2312只收錄6763個漢字,有不少漢字,如部分在GB 2312-80推出以後才簡化的漢字(如 "啰"),部分人名用字(如中國前總理朱镕基的"镕"字),台灣及香港使用的繁 體字,日語及朝鮮語漢字等,並未有收錄在內。於是廠商微軟利用GB 2312-80未使用的編碼空間,收錄 GB 13000.1-93全部字符制定了GBK編碼。

2.3 BIG編碼

特點:繁體中文,流行於台灣、香港與澳門,采用雙字節編碼。

Big5,又稱為大五碼或五大碼,是使用繁體中文(正體中文)社區中最常用的電腦漢字字符集標准, 共收錄13,060個漢字。中文碼分為內碼及交換碼兩類,Big5屬中文內碼,知名的中文交換碼有CCCII、 CNS11643。Big5雖普及於台灣、香港與澳門等繁體中文通行區,但長期以來並非當地的國家標准,而只 是業界標准。Big5碼是一套雙字節字符集,使用了雙八碼存儲方法,以兩個字節來安放一個字。第一個 字節稱為"高位字節",第二個字節稱為"低位字節"。"高位字節"使用 了0x81-0xFE,"低位字節"使用了0x40-0x7E,及0xA1-0xFE。

2.4 GB18030

GB 18030,全稱:國家標准GB 18030-2005《信息技術 中文編碼字符集》,是中華人民共和國現時最 新的內碼字集,是GB 18030-2000《信息技術 信息交換用漢字編碼字符集 基本集的擴充》的修訂版。與 GB 2312-1980完全兼容,與GBK基本兼容,支持GB 13000及Unicode的全部統一漢字,共收錄漢字70244個 。

3.Unicode

特點:涵蓋所有的文字、符號,每個符號都有獨一無二的編碼,全世界通用,四字節存儲。

Unicode(統一碼、萬國碼、單一碼)是一種在計算機上使用的字符編碼。Unicode 是為了解決傳統 的字符編碼方案的局限而產生的,它為每種語言中的每個字符設定了統一並且唯一的二進制編碼,以滿 足跨語言、跨平台進行文本轉換、處理的要求。1990年開始研發,1994年正式公布。隨著計算機工作能 力的增強,Unicode也在面世以來的十多年裡得到普及。

具體的符號對應表,可以查詢unicode.org,或者專門的漢字對應表。

全部編碼采用4字節存儲,如果是ASCII碼,本來是單字節存儲的,也要用四字節來存儲,前三個字節 都是0,很浪費空間。

4.UTF-8

特點:Unicode的一種實現方式,變長的編碼方式,可以使用1~4個字節表示一個符號,根據 不同的符號而變化字節長度。

互聯網的普及,強烈要求出現一種統一的編碼方式。UTF-8就是在互聯網上使用最廣的一種Unicode的 實現方式。其他實現方式還包括UTF-16(字符用兩個字節或四個字節表示)和UTF-32(字符用四個字節 表示),不過在互聯網上基本不用。

UTF-8最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節表示一個符號,根據不 同的符號而變化字節長度。

UTF-8的編碼規則很簡單,只有二條:

1)對於單字節的符號,字節的第一位設為0,後面7位為這個符號的unicode碼。因此對於英語字母, UTF-8編碼和ASCII碼是相同的。

2)對於n字節的符號(n>1),第一個字節的前n位都設為1,第n+1位設為0,後面字節的前兩位一 律設為10。剩下的沒有提及的二進制位,全部為這個符號的unicode碼。

下表總結了編碼規則,字母x表示可用編碼的位。

Unicode符號范圍 | UTF-8編碼方式

(十六進制) | (二進制)

--------------------+---- -----------------------------------------

0000 0000-0000 007F | 0xxxxxxx

0000 0080- 0000 07FF | 110xxxxx 10xxxxxx

0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx

0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

跟據上表,解讀UTF-8編碼非常簡單。如果一個字節的第一位是0,則這個字節單獨就是一個字符;如 果第一位是1,則連續有多少個1,就表示當前字符占用多少個字節。

下面,還是以漢字"嚴"為例,演示如何實現UTF-8編碼。

已知"嚴"的unicode是4E25(100111000100101),根據上表,可以發現4E25處在第三行的 范圍內(0000 0800-0000 FFFF),因此"嚴"的UTF-8編碼需要三個字節,即格式是 "1110xxxx 10xxxxxx 10xxxxxx"。然後,從"嚴"的最後一個二進制位開始,依次 從後向前填入格式中的x,多出的位補0。這樣就得到了,"嚴"的UTF-8編碼是"11100100 10111000 10100101",轉換成十六進制就是E4B8A5。

為什麼會出現亂碼?

由於字符編碼不同,一種編碼無法兼容另一種編碼,就會出現亂碼。

舉個簡單例子,ASCII編碼轉為UTF8是沒問題的,但是當UTF8轉ASCII碼就可能出問題。

舉個簡單例子:

你如果是一個會英語的中國人,當一個英國人給你說話時,你明白他的意思,但回答的是中文,他估 計不會聽懂的。

通過以下例子,來對具體的編碼規則以及亂碼情況進行比較吧:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
    
namespace EncodingSample
{
    class Program
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //英文字符串
            string str_en = "Welcome to the Encoding world.";
            //簡體中文
            string str_cn = "歡迎來到編碼世界!";
            //繁體中文
            string str_tw = "歡迎來到編碼世界";
    
            Encoding defaultEncoding = Encoding.Default;
            Console.WriteLine("默認編碼:{0}", defaultEncoding.BodyName);
    
    
            Encoding dstEncoding = null;
            //ASCII碼
            Console.WriteLine("----ASCII編碼----");
            dstEncoding = Encoding.ASCII;
            OutputByEncoding(defaultEncoding, dstEncoding, str_en);
            OutputByEncoding(defaultEncoding, dstEncoding, str_cn);
            OutputByEncoding(defaultEncoding, dstEncoding, str_tw);
    
            OutputBoundary();
    
            //GB2312
            Console.WriteLine("----GB2312編碼----");
            dstEncoding = Encoding.GetEncoding("GB2312");
            OutputByEncoding(defaultEncoding, dstEncoding, str_en);
            OutputByEncoding(defaultEncoding, dstEncoding, str_cn);
            OutputByEncoding(defaultEncoding, dstEncoding, str_tw);
    
            OutputBoundary();
    
            //BIG5
            Console.WriteLine("----BIG5編碼----");
            dstEncoding = Encoding.GetEncoding("BIG5");
            OutputByEncoding(defaultEncoding, dstEncoding, str_en);
            OutputByEncoding(defaultEncoding, dstEncoding, str_cn);
            OutputByEncoding(defaultEncoding, dstEncoding, str_tw);
    
            OutputBoundary();
    
            //Unicode
            Console.WriteLine("----Unicode編碼----");
            dstEncoding = Encoding.Unicode;
            OutputByEncoding(defaultEncoding, dstEncoding, str_en);
            OutputByEncoding(defaultEncoding, dstEncoding, str_cn);
            OutputByEncoding(defaultEncoding, dstEncoding, str_tw);
    
            OutputBoundary();
    
            //UTF8
            Console.WriteLine("----UTF8編碼----");
            dstEncoding = Encoding.UTF8;
            OutputByEncoding(defaultEncoding, dstEncoding, str_en);
            OutputByEncoding(defaultEncoding, dstEncoding, str_cn);
            OutputByEncoding(defaultEncoding, dstEncoding, str_tw);
    
            Console.ReadKey();
    
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="srcEncoding">原編碼</param>
        /// <param name="dstEncoding">目標編碼</param>
        /// <param name="srcBytes">原</param>
        public static void OutputByEncoding(Encoding srcEncoding,Encoding dstEncoding,string srcStr)
        {
            byte[] srcBytes = srcEncoding.GetBytes(srcStr);
            Console.WriteLine("Encoding.GetBytes: {0}", BitConverter.ToString(srcBytes));
            byte[] bytes = Encoding.Convert(srcEncoding, dstEncoding, srcBytes);
            Console.WriteLine("Encoding.GetBytes: {0}", BitConverter.ToString(bytes));
            string result = dstEncoding.GetString(bytes);
            Console.WriteLine("Encoding.GetString: {0}", result);
        }
        /// <summary>
        /// 分割線
        /// </summary>
        public static void OutputBoundary()
        {
            Console.WriteLine("------------------------------------");
        }
    }
}

執行結果:

查看本欄目

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved