一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

淺談文字編碼和Unicode

 gold 2008-08-19

我曾經(jīng)寫過一篇《談?wù)刄nicode編碼,簡要解釋UCS、UTF、BMP、BOM等名詞》(以 下簡稱《談?wù)刄nicode編碼》),在網(wǎng)上流傳較廣,我也收到不少朋友的反饋。本文探討《談?wù)刄nicode編碼》中未介紹或介紹較少的代碼頁、 Surrogates等問題,補充一些Unicode資料,順帶介紹一下我最近編寫的一個Unicode工具:UniToy。本文雖然是前文的補充,但在 寫作上盡量做到獨立成篇。

標題中的“淺談”是對自己的要求,我希望文字能盡量淺顯易懂。但本文還是假設(shè)讀者知道字節(jié)、16進制,了解《談?wù)刄nicode編碼》中介紹過的字節(jié)序和Unicode的基本概念。

0 UniToy

UniToy是我編寫的一個小工具。通過UniToy,我們可以全方位、多角度地查看Unicode,了解Unicode和語言、代碼頁的關(guān)系,完成一些文字編碼的相關(guān)工作。本文的一些內(nèi)容是通過UniToy演示的。大家可以從我的網(wǎng)站(www.)下載UniToy的演示版本。

1 文字的顯示

1.1 發(fā)生了什么?

我們首先以Windows為例來看看文字顯示過程中發(fā)生了什么。用記事本打開一個文本文件,可以看到文件包含的文字:

字符和編碼

如果我們用UltraEdit或Hex Workshop查看這個文件的16進制數(shù)據(jù),可以看到:

D7D6 b7fb bacd b1e0 c2eb

我們看到:文件“例子GBK.txt”有10個字節(jié),依次是“D7 D6 B7 FB BA CD B1 E0 C2 EB”,這就是記事本從文件中讀到的內(nèi)容。記事本是用來打開文本文件的,所以它會調(diào)用Windows的文本顯示函數(shù)將讀到的數(shù)據(jù)作為文本顯示。 Windows首先將文本數(shù)據(jù)轉(zhuǎn)換到它內(nèi)部使用的編碼格式:Unicode,然后按照文本的Unicode去字體文件中查找字體圖像,最后將圖像顯示到窗 口上??偨Y(jié)一下前面的分析,文字的顯示應(yīng)該是這樣的:

  • 步驟1:文字首先以某種編碼保存在文件中。
  • 步驟2:Windows將文件中的文字編碼映射到Unicode。
  • 步驟3:Windows按照Unicode在字體文件中查找字體圖像,畫到窗口上。
所謂編碼就是用數(shù)字表示字符,例如用D7D6表示“字”。當然,編碼還意味著約定,即大家都 認可。從《談?wù)刄nicode編碼》中,我們知道Unicode也是一種文字編碼,它的特殊性在于它是由國際組織設(shè)計,可以容納全世界所有語言文字。而我 們平常使用的文字編碼通常是針對一個區(qū)域的語言、文字設(shè)計,只支持特定的語言文字。例如:在上面的例子中,文件“例子GBK.txt”采用的就是GBK編 碼。

 

如果上述3個步驟中任何一步發(fā)生了錯誤,文字就不能被正確顯示,例如:

  • 錯誤1:如果弄錯了編碼,例如將Big5編碼的文字當作GBK編碼,就會出現(xiàn)亂碼。

  • 錯誤2:如果從特定編碼到Unicode的映射發(fā)生錯誤,例如文本數(shù)據(jù)中出現(xiàn)該編碼方案未定義的字符,Windows就會使用缺省字符,通常是?。

  • 如果當前字體不支持要顯示的字符,Windows就會顯示字體文件中的缺省圖像:空白或方格。

在Unicode被廣泛使用前,有多少種語言、文字,就可能有多少種文字編碼方案。一種文字也可能有多種編碼方案。那么我們怎么確定文本數(shù)據(jù)采用了什么編碼?

1.2 采用了哪種編碼?

按照慣例,文本文件中的數(shù)據(jù)都是文本編碼,那么它怎么表明自己的編碼格式?在記事本的“打開”對話框上:

我們可以看到記事本支持4種編碼格式:ANSI、Unicode、Unicode big endian、UTF-8。如果讀者看過《談?wù)刄nicode編碼》,對Unicode、Unicode big endian、UTF-8應(yīng)該不會陌生,其實它們更準確的名稱應(yīng)該是UTF-16LE(Little Endian)、UTF-16BE(Big Endian)和UTF-8,它們是基于Unicode的不同編碼方案。

在《談?wù)刄nicode編碼》中介紹過,Windows通過在文本文件開頭增加一些特殊字節(jié)(BOM)來區(qū)分上述3種編碼,并將沒有BOM的文本數(shù)據(jù)按照ANSI代碼頁處理。那么什么是代碼頁,什么是ANSI代碼頁?

2 代碼頁和字符集

2.1 Windows的代碼頁

2.1.1 代碼頁

代碼頁(Code Page)是個古老的專業(yè)術(shù)語,據(jù)說是IBM公司首先使用的。代碼頁和字符集的含義基本相同,代碼頁規(guī)定了適用于特定地區(qū)的字符集合,和這些字符的編碼??梢詫⒋a頁理解為字符和字節(jié)數(shù)據(jù)的映射表。

Windows為自己支持的代碼頁都編了一個號碼。例如代碼頁936就是簡體中文 GBK,代碼頁950就是繁體中文 Big5。代碼頁的概念比較簡單,就是一個字符編碼方案。但要說清楚Windows的ANSI代碼頁,就要從Windows的區(qū)域(Locale)說起 了。

2.1.2 區(qū)域和ANSI代碼頁

微軟為了適應(yīng)世界上不同地區(qū)用戶的文化背景和生活習慣,在Windows中設(shè)計了區(qū)域(Locale)設(shè)置的功能。 Local是指特定于某個國家或地區(qū)的一組設(shè)定,包括代碼頁,數(shù)字、貨幣、時間和日期的格式等。在Windows內(nèi)部,其實有兩個Locale設(shè)置:系統(tǒng) Locale和用戶Locale。系統(tǒng)Locale決定代碼頁,用戶Locale決定數(shù)字、貨幣、時間和日期的格式。我們可以在控制面板的“區(qū)域和語言選 項”中設(shè)置系統(tǒng)Locale和用戶Locale:

每個Locale都有一個對應(yīng)的代碼頁。Locale和代碼頁的對應(yīng)關(guān)系,大家可以參閱我的另一篇文章《談?wù)刉indows程序中的字符編碼》的附錄1。系統(tǒng)Locale對應(yīng)的代碼頁被作為Windows的默認代碼頁。在沒有文本編碼信息時,Windows按照默認代碼頁的編碼方案解釋文本數(shù)據(jù)。這個默認代碼頁通常被稱作ANSI代碼頁(ACP)。

ANSI代碼頁還有一層意思,就是微軟自己定義的代碼頁。在歷史上,IBM的個人計算機和微軟公司的操作系統(tǒng)曾經(jīng)是 PC的標準配置。微軟公司將IBM公司定義的代碼頁稱作OEM代碼頁,在IBM公司的代碼頁基礎(chǔ)上作了些增補后,作為自己的代碼頁,并冠以ANSI的字 樣。我們在“區(qū)域和語言選項”高級頁面的代碼頁轉(zhuǎn)換表中看到的包含ANSI字樣的代碼頁都是微軟自己定義的代碼頁。例如:

  • 874 (ANSI/OEM - 泰文)
  • 932 (ANSI/OEM - 日文 Shift-JIS)
  • 936 (ANSI/OEM - 簡體中文 GBK)
  • 949 (ANSI/OEM - 韓文)
  • 950 (ANSI/OEM - 繁體中文 Big5)
  • 1250 (ANSI - 中歐)
  • 1251 (ANSI - 西里爾文)
  • 1252 (ANSI - 拉丁文 I)
  • 1253 (ANSI - 希臘文)
  • 1254 (ANSI - 土耳其文)
  • 1255 (ANSI - 希伯來文)
  • 1256 (ANSI - 阿拉伯文)
  • 1257 (ANSI - 波羅的海文)
  • 1258 (ANSI/OEM - 越南)

在UniToy中,我們可以按照代碼頁編碼順序查看這些代碼頁的字符和編碼:

我們不能直接設(shè)置ANSI代碼頁,只能通過選擇系統(tǒng)Locale,間接改變當前的ANSI代碼頁。微軟定義的Locale只使用自己定義的代碼頁。所以,我們雖然可以通過“區(qū)域和語言選項”中的代碼頁轉(zhuǎn)換表安裝很多代碼頁,但只能將微軟的代碼頁作為系統(tǒng)默認代碼頁。

2.1.3 代碼頁轉(zhuǎn)換表

在Windows 2000以后,Windows統(tǒng)一采用UTF-16作為內(nèi)部字符編碼。現(xiàn)在,安裝一個代碼頁就是安裝一張代碼頁轉(zhuǎn)換表。通過代碼頁轉(zhuǎn)換表,Windows 既可以將代碼頁的編碼轉(zhuǎn)換到UTF-16,也可以將UTF-16轉(zhuǎn)換到代碼頁的編碼。代碼頁轉(zhuǎn)換表的具體實現(xiàn)可以是一個以nls為后綴的數(shù)據(jù)文件,也可以 是一個提供轉(zhuǎn)換函數(shù)的動態(tài)鏈接庫。有的代碼頁是不需要安裝的。例如:Windows將UTF-7和UTF-8分別作為代碼頁65000和代碼頁 65001。UTF-7、UTF-8和UTF-16都是基于Unicode的編碼方案。它們之間可以通過簡單的算法直接轉(zhuǎn)換,不需要安裝代碼頁轉(zhuǎn)換表。

在安裝過一個代碼頁后,Windows就知道怎樣將該代碼頁的文本轉(zhuǎn)換到Unicode文本,也知道怎樣將 Unicode文本轉(zhuǎn)換成該代碼頁的文本。例如:UniToy有導(dǎo)入和導(dǎo)出功能。所謂導(dǎo)入功能就是將任一代碼頁的文本文件轉(zhuǎn)換到Unicode文本;導(dǎo)出 功能就是將Unicode文本轉(zhuǎn)換到任一指定的代碼頁。這里所說的代碼頁就是指系統(tǒng)已安裝的代碼頁:

其實,如果全世界人民在計算機剛發(fā)明時就統(tǒng)一采用Unicode作為字符編碼,那么代碼頁就沒有存在的必要了??上? 在Unicode被發(fā)明前,世界各國人民都發(fā)明并使用了各種字符編碼方案。所以,Windows必須通過代碼頁支持已經(jīng)被廣泛使用的字符編碼。從這種意義 看,代碼頁主要是為了兼容現(xiàn)有的數(shù)據(jù)、程序和習慣而存在的。

2.1.4 SBCS、DBCS和MBCS

SBCS、DBCS和MBCS分別是單字節(jié)字符集、雙字節(jié)字符集和多字節(jié)字符集的縮寫。SBCS、DBCS和 MBCS的最大編碼長度分別是1字節(jié)、兩字節(jié)和大于兩字節(jié)(例如4或5字節(jié))。例如:代碼頁1252 (ANSI-拉丁文 I)是單字節(jié)字符集;代碼頁936 (ANSI/OEM-簡體中文 GBK)是雙字節(jié)字符集;代碼頁54936 (GB18030 簡體中文)是多字節(jié)字符集。

單字節(jié)字符集中的字符都用一個字節(jié)表示。顯然,SBCS最多只能容納256個字符。

雙字節(jié)字符集的字符用一個或兩個字節(jié)表示。那么我們從文本數(shù)據(jù)中讀到一個字節(jié)時,怎么判斷它是單字節(jié)字符,還是雙字 節(jié)字符的首字符?答案是通過字節(jié)所處范圍來判斷。例如:在GBK編碼中,單字節(jié)字符的范圍是0x00-0x80,雙字節(jié)字符首字節(jié)的范圍是0x81到 0xFE。我們順序讀取字節(jié)數(shù)據(jù),如果讀到的字節(jié)在0x81到0xFE內(nèi),那么這個字節(jié)就是雙字節(jié)字符的首字節(jié)。GBK定義雙字節(jié)字符的尾字節(jié)范圍是 0x40到0x7E和0x80到0xFE。

GB18030是多字節(jié)字符集,它的字符可以用一個、兩個或四個字節(jié)表示。這時我們又如何判斷一個字節(jié)是屬于單字節(jié) 字符,雙字節(jié)字符,還是四字節(jié)字符?GB18030與GBK是兼容的,它利用了GBK雙字節(jié)字符尾字節(jié)的未使用碼位。GB18030的四字節(jié)字符的第一字 節(jié)的范圍也是0x81到0xFE,第二字節(jié)的范圍是0x30-0x39。通過第二字節(jié)所處范圍就可以區(qū)分雙字節(jié)字符和四字節(jié)字符。GB18030定義四字 節(jié)字符的第三字節(jié)范圍是0x81到0xFE,第四字節(jié)范圍是0x30-0x39。

2.2 代碼頁實例

2.2.1 實例一:GB18030代碼頁

1.1節(jié)的“錯誤2”中演示了一個全被顯示成‘?‘的文件。這個文件的數(shù)據(jù)是:

其實,這是一個包含了6個四字節(jié)字符的GB18030編碼的文件。記事本按照GBK顯示這些數(shù)據(jù),而GB18030 的四字節(jié)字符編碼在GBK中是未定義的。Windows根據(jù)首字節(jié)范圍判斷出12個雙字節(jié)字符,然后因為找不到匹配的轉(zhuǎn)換而將其映射到默認字符‘?‘。使 用UniToy按照GB18030代碼頁導(dǎo)入這個文件,就可以看到:

這個GB18030編碼的文件是用UniToy創(chuàng)建的,編輯Unicode文本,然后導(dǎo)出到GB18030編碼格式。

2.2.2 實例二:GBK和Big5的轉(zhuǎn)換

綜合使用UniToy的導(dǎo)入、導(dǎo)出功能就可以在任意兩個代碼頁之間轉(zhuǎn)換文本。其實,由于各代碼頁支持的字符范圍不同,我們一般不會直接在代碼頁間轉(zhuǎn)換文本。例如將以下GBK編碼的文本:

直接轉(zhuǎn)換到Big5編碼,就會看到:

變成‘?‘的字符都是Big5編碼不支持的簡化字。在從Unicode轉(zhuǎn)換到Big5編碼時,由于Big5編碼不支持這些字符,Windows就用默認字符‘?‘代替。在UniToy中,我們可以先將簡體字轉(zhuǎn)換到繁體字,然后再導(dǎo)出到Big5編碼,就可以正常顯示:

同理,將Big5編碼的文本轉(zhuǎn)換到GBK編碼的步驟應(yīng)該是:

  • 將Big5編碼的文本導(dǎo)入到Unicode文本;
  • 將繁體的Unicode文本轉(zhuǎn)換簡體的Unicode文本;
  • 將簡體的Unicode文本導(dǎo)出到GBK文本。

2.3 互聯(lián)網(wǎng)的字符集

2.3.1 字符集

互聯(lián)網(wǎng)上的信息繽紛多彩,但文本依然是最重要的信息載體。html文件通過標記表明自己使用的字符集。例如:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

或者:

<meta http-equiv="charset" content="iso-8859-1">

那么我們可以使用哪些字符集(charset)呢?在IETF(互聯(lián)網(wǎng)工程任務(wù)組)的網(wǎng)頁上維護著一份可以在互聯(lián)網(wǎng)上使用的字符集的清單:CHARACTER SETS。如果有新的字符集被登記,IETF會更新這份文檔。

簡單瀏覽一下,2006年12月7日的版本列出了253個字符集。其中也包括微軟的CP1250 ~ CP1258,在這里它們不會被稱作什么ANSI代碼頁,而是被簡單地稱作windows-1250、windows-1251等。其實在Unicode 被廣泛使用前,除了中日韓等大字符集,世界上,特別是西方使用最廣泛的字符集應(yīng)該是ISO 8859系列字符集。

2.3.2 ISO 8859系列字符集

ISO 8859系列字符集是歐洲計算機制造商協(xié)會(ECMA)在上世紀80年代中期設(shè)計,并被國際標準化(ISO)組織采納為國際標準。ISO 8859系列字符集目前有15個字符集,包括:

  • ISO 8859-1 大部分的西歐語系,例如英文、法文、西班牙文和德文等(Latin-1)
  • ISO 8859-2 大部分的中歐和東歐語系,例如捷克文、波蘭文和匈牙利文等(Latin-2)
  • ISO 8859-3 歐洲東南部和其它各種文字(Latin-3)
  • ISO 8859-4 斯堪的那維亞和波羅的海語系(Latin-4)
  • ISO 8859-5 拉丁文與斯拉夫文(俄文、保加利亞文等)
  • ISO 8859-6 拉丁文與阿拉伯文
  • ISO 8859-7 拉丁文與希臘文
  • ISO 8859-8 拉丁文與希伯來文
  • ISO 8859-9 為土耳其文修正的Latin-1(Latin-5)
  • ISO 8859-10 拉普人、北歐與愛斯基摩人的文字(Latin-6)
  • ISO 8859-11 拉丁文與泰文
  • ISO 8859-13 波羅的海周邊語系,例如拉脫維亞文等(Latin-7)
  • ISO 8859-14 凱爾特文,例如蓋爾文、威爾士文等(Latin-8)
  • ISO 8859-15 改進的Latin-1,增加遺漏的法文、芬蘭文字符和歐元符號(Latin-9)
  • ISO 8859-16 羅馬尼亞文(Latin-10)

其中缺少的編號12據(jù)說是為了預(yù)留給天城體梵文字母(Deva-nagari)的。印地文和尼泊爾文都使用了這種在 七世紀形成的字母表。由于印度定義了自己的編碼ISCII(Indian Script Code for Information Interchange),所以這個編號就未被使用。ISO 8859系列字符集都是單字節(jié)字符集,即只使用0x00-0xFF對字符編碼。

大家都知道ASCII吧,那么大家知道ANSI X3.4和ISO 646嗎?在1968年發(fā)布的ANSI X3.4和1972年發(fā)布的ISO 646就是ASCII編碼,只不過是不同組織發(fā)布的。絕大多數(shù)字符集都與ASCII編碼保持兼容,ISO 8859系列字符集也不例外,它們的0x00-0x7f都與ASCII碼保持一致,各字符集的不同之處在于如何利用0x80-0xff的碼位。使用 UniToy可以查看ISO 8859系列所有字符集的編碼,例如:

通過這些演示,大家是不是覺得代碼頁和字符集都是很簡單、樸實的東西呢?好,在進入Unicode的話題前,讓我們先看一個很深奧的概念。

3 字符編碼模型

程序員經(jīng)常會面對復(fù)雜的問題,而降低復(fù)雜性的最簡單的方法就是分而治之。Peter Constable在他的文章"Character set encoding basics Understanding character set encodings and legacy encodings"中描述了字符編碼的四層模型。我覺得這種說法確實可以更清晰地展現(xiàn)字符編碼中發(fā)生的事情,所以在這里也介紹一下。

3.1 字符的范圍(Abstract character repertoire)

設(shè)計字符編碼的第一層就是確定字符的范圍,即要支持哪些字符。有些編碼方案的字符范圍是固定的,例如ASCII、ISO 8859 系列。有些編碼方案的字符范圍是開放的,例如Unicode的字符范圍就是世界上所有的字符。

3.2 用數(shù)字表示字符(Coded character set)

設(shè)計字符編碼的第二層是將字符和數(shù)字對應(yīng)起來??梢詫⑦@個層次理解成數(shù)學家(即從數(shù)學角度)看到的字符編碼。數(shù)學家看到的字符編碼是一個正整數(shù)。例如在Unicode中:漢字“字”對應(yīng)的數(shù)字是23383。漢字“”對應(yīng)的數(shù)字是134192。

在寫html文件時,可以通過輸入"字"來插入字符“字”。不過在設(shè)計字符編碼時,我們還是習慣用16進制表示數(shù)字。即將23383寫成0x5BD7,將134192寫成0x20C30。

3.3 用基本數(shù)據(jù)類型表示字符(Character encoding form)

設(shè)計字符編碼的第三層是用編程語言中的基本數(shù)據(jù)類型來表示字符??梢詫⑦@個層次理解成程序員看到的字符編碼。在 Unicode中,我們有很多方式將數(shù)字23383表示成程序中的數(shù)據(jù),包括:UTF-8、UTF-16、UTF-32。UTF是“UCS Transformation Format”的縮寫,可以翻譯成Unicode字符集轉(zhuǎn)換格式,即怎樣將Unicode定義的數(shù)字轉(zhuǎn)換成程序數(shù)據(jù)。例如,“漢字”對應(yīng)的數(shù)字是 0x6c49和0x5b57,而編碼的程序數(shù)據(jù)是:

BYTE data_utf8[]={0xE6,0xB1,0x89,0xE5,0xAD,0x97}; // UTF-8編碼
WORD data_utf16[]={0x6c49,0x5b57};                            // UTF-16編碼
DWORD data_utf32[]={0x6c49,0x5b57};                         // UTF-32編碼

這里用BYTE、WORD、DWORD分別表示無符號8位整數(shù),無符號16位整數(shù)和無符號32位整數(shù)。UTF-8、UTF-16、UTF-32分別以BYTE、WORD、DWORD作為編碼單位。

“漢字”的UTF-8編碼需要6個字節(jié)。“漢字”的UTF-16編碼需要兩個WORD,大小是4個字節(jié)。“漢字”的UTF-32編碼需要兩個DWORD,大小是8個字節(jié)。4.2節(jié)會介紹將數(shù)字映射到UTF編碼的規(guī)則。

3.4 作為字節(jié)流的字符(Character encoding scheme)

字符編碼的第四層是計算機看到的字符,即在文件或內(nèi)存中的字節(jié)流。例如,“字”的UTF-32編碼是0x5b57,如果用little endian表示,字節(jié)流是“57 5b 00 00”。如果用big endian表示,字節(jié)流是“00 00 5b 57”。

字符編碼的第三層規(guī)定了一個字符由哪些編碼單位按什么順序表示。字符編碼的第四層在第三層的基礎(chǔ)上又考慮了編碼單位 內(nèi)部的字節(jié)序。UTF-8的編碼單位是字節(jié),不受字節(jié)序的影響。UTF-16、UTF-32根據(jù)字節(jié)序的不同,又衍生出UTF-16LE、UTF- 16BE、UTF-32LE、UTF-32BE四種編碼方案。LE和BE分別是Little Endian和Big Endian的縮寫。

3.5 小結(jié)

通過四層模型,我們又把字符編碼中發(fā)生的這些事情梳理了一遍。其實大多數(shù)代碼頁都不需要完整的四層模型,例如GB18030以字節(jié)為編碼單位,直接規(guī)定了字節(jié)序列和字符的映射關(guān)系,跳過了第二層,也不需要第四層。

4 再談Unicode

Unicode是國際組織制定的可以容納世界上所有文字和符號的字符編碼方案。Unicode用數(shù)字 0-0x10FFFF來映射這些字符,最多可以容納1114112個字符,或者說有1114112個碼位。碼位就是可以分配給字符的數(shù)字。UTF-8、 UTF-16、UTF-32都是將數(shù)字轉(zhuǎn)換到程序數(shù)據(jù)的編碼方案。

Unicode字符集可以簡寫為UCS(Unicode Character Set)。早期的Unicode標準有UCS-2、UCS-4的說法。UCS-2用兩個字節(jié)編碼,UCS-4用4個字節(jié)編碼。UCS-4根據(jù)最高位為0的 最高字節(jié)分成2^7=128個group。每個group再根據(jù)次高字節(jié)分為256個平面(plane)。每個平面根據(jù)第3個字節(jié)分為256行 (row),每行有256個碼位(cell)。group 0的平面0被稱作BMP(Basic Multilingual Plane)。將UCS-4的BMP去掉前面的兩個零字節(jié)就得到了UCS-2。

Unicode標準計劃使用group 0 的17個平面: 從BMP(平面0)到平面16,即數(shù)字0-0x10FFFF?!墩?wù)刄nicode編碼》主要介紹了BMP的編碼,本文將介紹完整的Unicode編碼, 并從多個角度瀏覽Unicode。本文的介紹基于Unicode 5.0.0版本。

4.1 瀏覽Unicode

先看一些數(shù)字:每個平面有2^16=65536個碼位。Unicode計劃使用了17個平面,一共有 17*65536=1114112個碼位。其實,現(xiàn)在已定義的碼位只有238605個,分布在平面0、平面1、平面2、平面14、平面15、平面16。其 中平面15和平面16上只是定義了兩個各占65534個碼位的專用區(qū)(Private Use Area),分別是0xF0000-0xFFFFD和0x100000-0x10FFFD。所謂專用區(qū),就是保留給大家放自定義字符的區(qū)域,可以簡寫為 PUA。

平面0也有一個專用區(qū):0xE000-0xF8FF,有6400個碼位。平面0的0xD800-0xDFFF,共2048個碼位,是一個被稱作代理區(qū)(Surrogate)的特殊區(qū)域。它的用途將在4.2節(jié)介紹。

238605-65534*2-6400-2408=99089。余下的99089個已定義碼位分布在平面0、平面 1、平面2和平面14上,它們對應(yīng)著Unicode目前定義的99089個字符,其中包括71226個漢字。平面0、平面1、平面2和平面14上分別定義 了52080、3419、43253和337個字符。平面2的43253個字符都是漢字。平面0上定義了27973個漢字。

在更深入地了解Unicode字符前,我們先了解一下UCD。

4.1.1 什么是UCD

UCD是Unicode字符數(shù)據(jù)庫(Unicode Character Database)的縮寫。UCD由一些描述Unicode字符屬性和內(nèi)部關(guān)系的純文本或html文件組成。大家可以在Unicode組織的網(wǎng)站看到UCD的最新版本。

UCD中的文本文件大都是適合于程序分析的Unicode相關(guān)數(shù)據(jù)。其中的html文件解釋了數(shù)據(jù)庫的組織,數(shù)據(jù)的 格式和含義。UCD中最龐大的文件無疑就是描述漢字屬性的文件Unihan.txt。在UCD 5.0,0中,Unihan.txt文件大小有28,221K字節(jié)。Unihan.txt中包含了很多有參考價值的索引,例如漢字部首、筆劃、拼音、使用 頻度、四角號碼排序等。這些索引都是基于一些比較權(quán)威的辭典,但大多數(shù)索引只能檢索部分漢字。

我介紹UCD的目的主要是為了使用其中的兩個概念:Block和Script。

4.1.2 Block

UCD中的Blocks.txt將Unicode的碼位分割成一些連續(xù)的Block,并描述了每個Block的用途:

開始碼位 結(jié)束碼位 Block名稱(英文) Block名稱(中文)
0000 007F Basic Latin 基本拉丁字母
0080 00FF Latin-1 Supplement 拉丁字母補充-1
0100 017F Latin Extended-A 拉丁字母擴充-A
0180 024F Latin Extended-B 拉丁字母擴充-B
0250 02AF IPA Extensions 國際音標擴充
02B0 02FF Spacing Modifier Letters 進格修飾字符
0300 036F Combining Diacritical Marks 組合附加符號
0370 03FF Greek and Coptic 希臘文和哥普特文
0400 04FF Cyrillic 西里爾文
0500 052F Cyrillic Supplement 西里爾文補充
0530 058F Armenian 亞美尼亞文
0590 05FF Hebrew 希伯來文
0600 06FF Arabic 基本阿拉伯文
0700 074F Syriac 敘利亞文
0750 077F Arabic Supplement 阿拉伯文補充
0780 07BF Thaana 塔納文
07C0 07FF NKo N‘Ko字母表
0900 097F Devanagari 天成文書(梵文)
0980 09FF Bengali 孟加拉文
0A00 0A7F Gurmukhi 錫克教文
0A80 0AFF Gujarati 古吉拉特文
0B00 0B7F Oriya 奧里亞文
0B80 0BFF Tamil 泰米爾文
0C00 0C7F Telugu 泰盧固文
0C80 0CFF Kannada 卡納達文
0D00 0D7F Malayalam 德拉維族文
0D80 0DFF Sinhala 僧伽羅文
0E00 0E7F Thai 泰文
0E80 0EFF Lao 老撾文
0F00 0FFF Tibetan 藏文
1000 109F Myanmar 緬甸文
10A0 10FF Georgian 格魯吉亞文
1100 11FF Hangul Jamo 朝鮮文
1200 137F Ethiopic 埃塞俄比亞文
1380 139F Ethiopic Supplement 埃塞俄比亞文補充
13A0 13FF Cherokee 切羅基文
1400 167F Unified Canadian Aboriginal Syllabics 加拿大印第安方言
1680 169F Ogham 歐甘文
16A0 16FF Runic 北歐古字
1700 171F Tagalog 塔加路文
1720 173F Hanunoo 哈努諾文
1740 175F Buhid 布迪文
1760 177F Tagbanwa Tagbanwa文
1780 17FF Khmer 高棉文
1800 18AF Mongolian 蒙古文
1900 194F Limbu 林布文
1950 197F Tai Le 德宏傣文
1980 19DF New Tai Lue 新傣文
19E0 19FF Khmer Symbols 高棉文
1A00 1A1F Buginese 布吉文
1B00 1B7F Balinese 巴厘文
1D00 1D7F Phonetic Extensions 拉丁字母音標擴充
1D80 1DBF Phonetic Extensions Supplement 拉丁字母音標擴充增補
1DC0 1DFF Combining Diacritical Marks Supplement 組合附加符號補充
1E00 1EFF Latin Extended Additional 拉丁字母擴充附加
1F00 1FFF Greek Extended 希臘文擴充
2000 206F General Punctuation 一般標點符號
2070 209F Superscripts and Subscripts 上標和下標
20A0 20CF Currency Symbols 貨幣符號
20D0 20FF Combining Diacritical Marks for Symbols 符號用組合附加符號
2100 214F Letterlike Symbols 似字母符號
2150 218F Number Forms 數(shù)字形式
2190 21FF Arrows 箭頭符號
2200 22FF Mathematical Operators 數(shù)學運算符號
2300 23FF Miscellaneous Technical 零雜技術(shù)用符號
2400 243F Control Pictures 控制圖符
2440 245F Optical Character Recognition 光學字符識別
2460 24FF Enclosed Alphanumerics 帶括號的字母數(shù)字
2500 257F Box Drawing 制表符
2580 259F Block Elements 方塊元素
25A0 25FF Geometric Shapes 幾何形狀
2600 26FF Miscellaneous Symbols 零雜符號
2700 27BF Dingbats 雜錦字型
27C0 27EF Miscellaneous Mathematical Symbols-A 零雜數(shù)學符號-A
27F0 27FF Supplemental Arrows-A 箭頭符號補充-A
2800 28FF Braille Patterns 盲文
2900 297F Supplemental Arrows-B 箭頭符號補充-B
2980 29FF Miscellaneous Mathematical Symbols-B 零雜數(shù)學符號-B
2A00 2AFF Supplemental Mathematical Operators 數(shù)學運算符號
2B00 2BFF Miscellaneous Symbols and Arrows 零雜符號和箭頭
2C00 2C5F Glagolitic 格拉哥里字母表
2C60 2C7F Latin Extended-C 拉丁字母擴充-C
2C80 2CFF Coptic 科普特文
2D00 2D2F Georgian Supplement 格魯吉亞文補充
2D30 2D7F Tifinagh 提非納字母
2D80 2DDF Ethiopic Extended 埃塞俄比亞文擴充
2E00 2E7F Supplemental Punctuation 標點符號補充
2E80 2EFF CJK Radicals Supplement 中日韓部首補充
2F00 2FDF Kangxi Radicals 康熙字典部首
2FF0 2FFF Ideographic Description Characters 漢字結(jié)構(gòu)描述字符
3000 303F CJK Symbols and Punctuation 中日韓符號和標點
3040 309F Hiragana 平假名
30A0 30FF Katakana 片假名
3100 312F Bopomofo 注音符號
3130 318F Hangul Compatibility Jamo 朝鮮文兼容字母
3190 319F Kanbun 日文的漢字批注
31A0 31BF Bopomofo Extended 注音符號擴充
31C0 31EF CJK Strokes 中日韓筆劃
31F0 31FF Katakana Phonetic Extensions 片假名音標擴充
3200 32FF Enclosed CJK Letters and Months 帶括號的中日韓字母及月份
3300 33FF CJK Compatibility 中日韓兼容字符
3400 4DBF CJK Unified Ideographs Extension A 中日韓統(tǒng)一表意文字擴充A
4DC0 4DFF Yijing Hexagram Symbols 易經(jīng)六十四卦象
4E00 9FFF CJK Unified Ideographs 中日韓統(tǒng)一表意文字
A000 A48F Yi Syllables 彝文音節(jié)
A490 A4CF Yi Radicals 彝文字根
A700 A71F Modifier Tone Letters 聲調(diào)修飾字母
A720 A7FF Latin Extended-D 拉丁字母擴充-D
A800 A82F Syloti Nagri Syloti Nagri字母表
A840 A87F Phags-pa Phags-pa字母表
AC00 D7AF Hangul Syllables 朝鮮文音節(jié)
D800 DB7F High Surrogates 高位替代
DB80 DBFF High Private Use Surrogates 高位專用替代
DC00 DFFF Low Surrogates 低位替代
E000 F8FF Private Use Area 專用區(qū)
F900 FAFF CJK Compatibility Ideographs 中日韓兼容表意文字
FB00 FB4F Alphabetic Presentation Forms 字母變體顯現(xiàn)形式
FB50 FDFF Arabic Presentation Forms-A 阿拉伯文變體顯現(xiàn)形式-A
FE00 FE0F Variation Selectors 字型變換選取器
FE10 FE1F Vertical Forms 豎排標點符號
FE20 FE2F Combining Half Marks 組合半角標示
FE30 FE4F CJK Compatibility Forms 中日韓兼容形式
FE50 FE6F Small Form Variants 小型變體形式
FE70 FEFF Arabic Presentation Forms-B 阿拉伯文變體顯現(xiàn)形式-B
FF00 FFEF Halfwidth and Fullwidth Forms 半角及全角字符
FFF0 FFFF Specials 特殊區(qū)域
10000 1007F Linear B Syllabary 線形文字B音節(jié)文字
10080 100FF Linear B Ideograms 線形文字B表意文字
10100 1013F Aegean Numbers 愛琴海數(shù)字
10140 1018F Ancient Greek Numbers 古希臘數(shù)字
10300 1032F Old Italic 古意大利文
10330 1034F Gothic 哥特文
10380 1039F Ugaritic 烏加里特楔形文字
103A0 103DF Old Persian 古波斯文
10400 1044F Deseret 德塞雷特大學音標
10450 1047F Shavian 肅伯納速記符號
10480 104AF Osmanya Osmanya字母表
10800 1083F Cypriot Syllabary 塞浦路斯音節(jié)文字
10900 1091F Phoenician 腓尼基文
10A00 10A5F Kharoshthi 迦婁士悌文
12000 123FF Cuneiform 楔形文字
12400 1247F Cuneiform Numbers and Punctuation 楔形文字數(shù)字和標點
1D000 1D0FF Byzantine Musical Symbols 東正教音樂符號
1D100 1D1FF Musical Symbols 音樂符號
1D200 1D24F Ancient Greek Musical Notation 古希臘音樂符號
1D300 1D35F Tai Xuan Jing Symbols 太玄經(jīng)符號
1D360 1D37F Counting Rod Numerals 算籌
1D400 1D7FF Mathematical Alphanumeric Symbols 數(shù)學用字母數(shù)字符號
20000 2A6DF CJK Unified Ideographs Extension B 中日韓統(tǒng)一表意文字擴充 B
2F800 2FA1F CJK Compatibility Ideographs Supplement 中日韓兼容表意文字補充
E0000 E007F Tags 標簽
E0100 E01EF Variation Selectors Supplement 字型變換選取器補充
F0000 FFFFF Supplementary Private Use Area-A 補充專用區(qū)-A
100000 10FFFF Supplementary Private Use Area-B 補充專用區(qū)-B

Block是Unicode字符的一個屬性。屬于同一個Block的字符有著相近的用途。Block表中的開始碼 位、結(jié)束碼位只是用來劃分出一塊區(qū)域,在開始碼位和結(jié)束碼位之間可能還有很多未定義的碼位。使用UniToy,大家可以按照Block瀏覽Unicode 字符,既可以按列表顯示:

也可以顯示每個字符的詳細信息:

4.1.3 Script

Unicode中每個字符都有一個Script屬性,這個屬性表明字符所屬的文字系統(tǒng)。Unicode目前支持以下Script:

Script名稱(英文) Script名稱(中文) Script包含的字符數(shù)
Arabic 阿拉伯文 966
Armenian 亞美尼亞文 90
Balinese 巴厘文 121
Bengali 孟加拉文 91
Bopomofo 漢語注音符號 64
Braille 盲文 256
Buginese 布吉文 30
Buhid 布迪文 20
Canadian Aboriginal 加拿大印第安方言 630
Cherokee 切羅基文 85
Common Common 5020
Coptic 科普特文 128
Cuneiform 楔形文字 982
Cypriot 塞浦路斯音節(jié)文字 55
Cyrillic 西里爾文 277
Deseret 德塞雷特大學音標 80
Devanagari 天成文書(梵文) 107
Ethiopic 埃塞俄比亞文 461
Georgian 格魯吉亞文 120
Gothic 哥特文 94
Glagolitic 格拉哥里字母表 27
Greek 希臘文 506
Gujarati 古吉拉特文 83
Gurmukhi 錫克教文 77
Han 漢文 71570
Hangul 韓文書寫系統(tǒng) 11619
Hanunoo 哈努諾文 21
Hebrew 希伯來文 133
Hiragana 平假名 89
Inherited Inherited 461
Kannada 卡納達文 86
Katakana 片假名 164
Kharoshthi 迦婁士悌文 65
Khmer 高棉文 146
Lao 老撾文 65
Latin 拉丁文系 1070
Limbu 林布文(尼泊爾東部) 66
Linear B 線形文字B 211
Malayalam 德拉維族文(印度) 78
Mongolian 蒙古文 152
Myanmar 緬甸文 78
New Tai Lue 新傣文 80
Nko N‘Ko字母表 59
Ogham 歐甘文字 29
Old Italic 古意大利文 35
Old Persian 古波斯文 50
Oriya 奧里亞文 81
Osmanya Osmanya字母表 40
Phags Pa Phags Pa字母表(蒙古) 56
Phoenician 腓尼基文 27
Runic 古代北歐文 78
Shavian 肅伯納速記符號 48
Sinhala 僧伽羅文 80
Syloti Nagri Syloti Nagri字母表(印度) 44
Syriac 敘利亞文 77
Tagalog 塔加路文(菲律賓) 20
Tagbanwa Tagbanwa文(菲律賓) 18
Tai Le 德宏傣文 35
Tamil 泰米爾文 71
Telugu 泰盧固文(印度) 80
Thaana 馬爾代夫書寫體 50
Thai 泰國文 86
Tibetan 藏文 195
Tifinagh 提非納字母表 55
Ugaritic 烏加里特楔形文字 31
Yi 彝文 1220

其中,有兩個Script值有著特殊的含義:

  • Common:Script屬性為Common的字符可能在多個文字系統(tǒng)中使用,不是某個文字系統(tǒng)特有的。例如:空格、數(shù)字等。
  • Inherited:Script屬性為Inherited的字符會繼承前一個字符的Script屬性。主要是一些組合用符號,例如:在“組合附加符號”區(qū)(0x300-0x36f),字符的Script屬性都是Inherited。

UCD中的Script.txt列出了每個字符的Script屬性。使用UniToy可以按照Script屬性查看字符。例如:

左側(cè)Script窗口中,第一層節(jié)點是按英文字母順序排列的Script屬性。第二層節(jié)點是包含該Script文字的行(row),點擊后顯示該行內(nèi)屬于這個Script的字符。這樣,就可以集中查看屬于同一文字系統(tǒng)的字符。

4.1.4 Unicode中的漢字

前面提過,在Unicode已定義的99089個字符中,有71226個字符是漢字。它們的分布如下:

Block名稱 開始碼位 結(jié)束碼位 數(shù)量
中日韓統(tǒng)一表意文字擴充A 3400 4db5 6582
中日韓統(tǒng)一表意文字 4e00 9fbb 20924
中日韓兼容表意文字 f900 fa2d 302
中日韓兼容表意文字 fa30 fa6a 59
中日韓兼容表意文字 fa70 fad9 106
中日韓統(tǒng)一表意文字擴充B 20000 2a6d6 42711
中日韓兼容表意文字補充 2f800 2fa1d 542

UCD的Unihan.txt中的部首偏旁索引(kRSUnicode)可以檢索全部71226個漢字。 kRSUnicode的部首是按照康熙字典定義的,共214個部首。簡體字按照簡體部首對應(yīng)的繁體部首檢索。UniToy整理了康熙字典部首對應(yīng)的簡體部 首,提供了按照部首檢索漢字的功能:

4.2 UTF編碼

在字符編碼的四個層次中,第一層的范圍和第二層的編碼在4.1節(jié)已經(jīng)詳細討論過了。本節(jié)討論第三層的UTF編碼和第四層的字節(jié)序,主要談?wù)劦谌龑拥腢TF編碼,即怎樣將Unicode定義的編碼轉(zhuǎn)換成程序數(shù)據(jù)。

4.2.1 UTF-8

UTF-8以字節(jié)為單位對Unicode進行編碼。從Unicode到UTF-8的編碼方式如下:

Unicode編碼(16進制) UTF-8 字節(jié)流(二進制)
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個字節(jié)。從上表可以看出,4字節(jié)模板有21個x,即可以容納21位二進制數(shù)字。Unicode的最 大碼位0x10FFFF也只有21位。

例1:“漢”字的Unicode編碼是0x6C49。0x6C49在0x0800-0xFFFF之間,使用用3字節(jié)模板了:1110xxxx 10xxxxxx 10xxxxxx。將0x6C49寫成二進制是:0110 1100 0100 1001, 用這個比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

例2:“”字的Unicode編碼是0x20C30。0x20C30在0x010000-0x10FFFF之間,使用用4字節(jié)模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。將0x20C30寫成21位二進制數(shù)字(不足21位就在前面補0):0 0010 0000 1100 0011 0000,用這個比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。

4.2.2 UTF-16

UniToy有個“輸出編碼”功能,可以輸出當前選擇的文本編碼。因為UniToy內(nèi)部采用UTF-16編碼,所以 輸出的編碼就是文本的UTF-16編碼。例如:如果我們輸出“漢”字的UTF-16編碼,可以看到0x6C49,這與“漢”字的Unicode編碼是一致 的。如果我們輸出“”字的UTF-16編碼,可以看到0xD843, 0xDC30。“”字的Unicode編碼是0x20C30,它的UTF-16編碼是怎樣得到的呢?

4.2.2.1 編碼規(guī)則

UTF-16編碼以16位無符號整數(shù)為單位。我們把Unicode編碼記作U。編碼規(guī)則如下:

  • 如果U<0x10000,U的UTF-16編碼就是U對應(yīng)的16位無符號整數(shù)(為書寫簡便,下文將16位無符號整數(shù)記作WORD)。
  • 如果U≥0x10000,我們先計算U‘=U-0x10000,然后將U‘寫成二進制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16編碼(二進制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

為什么U‘可以被寫成20個二進制位?Unicode的最大碼位是0x10ffff,減去0x10000后,U‘的最大值是0xfffff,所以肯定可以用20個二進制位表示。例如:“”字的Unicode編碼是0x20C30,減去0x10000后,得到0x10C30,寫成二進制是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用后10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD843 0xDC30。

4.2.2.2 代理區(qū)(Surrogate)

按照上述規(guī)則,Unicode編碼0x10000-0x10FFFF的UTF-16編碼有兩個WORD,第一個WORD的高6位是110110,第二個WORD的高6位是110111??梢姡谝粋€WORD的取值范圍(二進制)是11011000 00000000到11011011 11111111,即0xD800-0xDBFF。第二個WORD的取值范圍(二進制)是11011100 00000000到11011111 11111111,即0xDC00-0xDFFF。

為了將一個WORD的UTF-16編碼與兩個WORD的UTF-16編碼區(qū)分開來,Unicode編碼的設(shè)計者將0xD800-0xDFFF保留下來,并稱為代理區(qū)(Surrogate):

D800 DB7F High Surrogates 高位替代
DB80 DBFF High Private Use Surrogates 高位專用替代
DC00 DFFF Low Surrogates 低位替代

高位替代就是指這個范圍的碼位是兩個WORD的UTF-16編碼的第一個WORD。低位替代就是指這個范圍的碼位是 兩個WORD的UTF-16編碼的第二個WORD。那么,高位專用替代是什么意思?我們來解答這個問題,順便看看怎么由UTF-16編碼推導(dǎo) Unicode編碼。

解:如果一個字符的UTF-16編碼的第一個WORD在0xDB80到0xDBFF之間,那么它的Unicode編 碼在什么范圍內(nèi)?我們知道第二個WORD的取值范圍是0xDC00-0xDFFF,所以這個字符的UTF-16編碼范圍應(yīng)該是0xDB80 0xDC00到0xDBFF 0xDFFF。我們將這個范圍寫成二進制:

1101101110000000 11011100 00000000 - 1101101111111111 1101111111111111

按照編碼的相反步驟,取出高低WORD的后10位,并拼在一起,得到

1110 0000 0000 0000 0000 - 1111 1111 1111 1111 1111

即0xe0000-0xfffff,按照編碼的相反步驟再加上0x10000,得到 0xf0000-0x10ffff。這就是UTF-16編碼的第一個WORD在0xdb80到0xdbff之間的Unicode編碼范圍,即平面15和平 面16。因為Unicode標準將平面15和平面16都作為專用區(qū),所以0xDB80到0xDBFF之間的保留碼位被稱作高位專用替代。

4.2.3 UTF-32

UTF-32編碼以32位無符號整數(shù)為單位。Unicode的UTF-32編碼就是其對應(yīng)的32位無符號整數(shù)。

4.2.4 字節(jié)序

根據(jù)字節(jié)序的不同,UTF-16可以被實現(xiàn)為UTF-16LE或UTF-16BE,UTF-32可以被實現(xiàn)為UTF-32LE或UTF-32BE。例如:

字符 Unicode編碼 UTF-16LE UTF-16BE UTF32-LE UTF32-BE
0x6C49 49 6C 6C 49 49 6C 00 00 00 00 6C 49
0x20C30 43 D8 30 DC D8 43 DC 30 30 0C 02 00 00 02 0C 30
那么,怎么判斷字節(jié)流的字節(jié)序呢?

 

Unicode標準建議用BOM(Byte Order Mark)來區(qū)分字節(jié)序,即在傳輸字節(jié)流前,先傳輸被作為BOM的字符"零寬無中斷空格"。這個字符的編碼是FEFF,而反過來的FFFE(UTF- 16)和FFFE0000(UTF-32)在Unicode中都是未定義的碼位,不應(yīng)該出現(xiàn)在實際傳輸中。下表是各種UTF編碼的BOM:

UTF編碼 Byte Order Mark
UTF-8 EF BB BF
UTF-16LE FF FE
UTF-16BE FE FF
UTF-32LE FF FE 00 00
UTF-32BE 00 00 FE FF

5 結(jié)束語

程序員的工作就是將復(fù)雜的世界簡單地表達出來,希望這篇文章也能做到這一點。本文的初稿完成于2007年2月14日。我會在我的個人主頁http://www.維護這篇文章的最新版本。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    欧美成人黄色一级视频| 中文字幕高清不卡一区| 成年午夜在线免费视频| 日韩欧美三级中文字幕| 日本东京热视频一区二区三区| 日韩美女偷拍视频久久| 日本一二三区不卡免费| 东京不热免费观看日本| 日本淫片一区二区三区| 东京热加勒比一区二区| 91精品国产综合久久福利| 久久精品亚洲精品国产欧美| 久久青青草原中文字幕| 国产女同精品一区二区| 国产精品免费不卡视频| 黑鬼糟蹋少妇资源在线观看| 免费精品国产日韩热久久| 欧美又大又黄刺激视频| 亚洲国产成人久久99精品| 99秋霞在线观看视频| 日本东京热视频一区二区三区| 欧美日韩乱一区二区三区| 亚洲欧美国产网爆精品| 久久综合亚洲精品蜜桃| 中文字幕人妻av不卡| 久久免费精品拍拍一区二区| 精品香蕉国产一区二区三区| 久久精品国产在热久久| 久久大香蕉精品在线观看 | 在线观看免费视频你懂的| 国产日韩精品欧美综合区| 国产一区二区三区丝袜不卡| 青青草草免费在线视频| 五月婷婷六月丁香在线观看| 欧美一级黄片欧美精品| 日本 一区二区 在线| 九九热这里只有精品视频 | 偷拍洗澡一区二区三区| 亚洲欧美日韩国产成人| 国产盗摄精品一区二区视频| 国产福利在线播放麻豆|