正則表達式(三):Unicode諸問題(下)

來源: infoq  發布時間: 2011-03-10 17:37  閱讀: 2868 次  推薦: 0   原文鏈接   [收藏]  

  上一篇文章我們介紹了和Unicode有關的匹配問題,這篇文章我們主要講述一下Unicode編碼本身的特性,以便更好地運用正則表達式解決與Unicode相關的問題。

  Unicode Code Point

  Unicode字符多種多樣,除去ascii中的字母、數字、標點和中文字符,還包括其它多種語言和多種符號,有些符號甚至很難打出來(比如表示商標注冊的?),這時候該如何表示呢?再說遠一點,如果我們想用一個字符組匹配所有中文字符,能不能像『[a-z]』那樣呢?

  所幸,每一個Unicode字符都對應自己的Unicode編碼,也就是Unicode編碼表中的一個代碼點(Code Point),所以在正則表達式中的Unicode字符往往采用Unicode代碼點來指定。

  一般來說,指定代碼點的形式有3種:『\uxxxx』、『\u{xxxx}』、『\x{xxxx}』(其中的xxxx為編碼的值,\u之后必須有4位16進制數字)。.NET、Java、JavaScript和Python使用第一種方式,而PHP和Ruby使用第二種方式(Ruby 1.9以上版本才支持這種表示法),PHP使用第三種方式。

  比如“發表”的“發”字對應的Unicode編碼是53 d1,它在不同語言中的表示法如下:所以我們可以在.NET、Java、JavaScript的正則表達式中這樣表示“發”字:“\u53d1”,Python稍有不同,必須使用u”\u53d1”(之前的u表示這是一個Unicode字符串); Ruby中,“發”則必須寫作”\u{53d1}”。

  現在以“發”字為例,介紹不同語言中的Unicode表示法:

編碼

語言

表示法

備注

53 d1

.NET

\u53d1

 

Java

\u53d1

 

JavaScript

\u53d1

 

Python

\u53d1

必須使用Unicode字符串,Python 2.x中,要在字符串之前加u

Ruby

\u{53d1}

限Ruby 1.9以上版本,且必須顯式指定Unicode模式

PHP

\x{53d1}

必須指定Unicode模式

  既然可以這樣指定Unicode字符,自然也可以在字符組中用范圍表示法指定一個Unicode編碼范圍了。比如,我們查詢Unicode編碼表可知,中文的編碼一般在4e00到9fff之間,所以可以用這樣的字符組匹配中文字符(Unicode編碼4e00-9fff歸類為“CJK 統一表意符號”CJK Unified Ideographs[1],涵蓋了絕大多數中文字符):

語言

字符組

備注

.NET

[\u4e00-\u9fff]

 

Java

[\u4e00-\u9fff]

 

JavaScript

[\u4e00-\u9fff]

 

Python

[\u4e00-\u9fff]

必須使用Unicode字符串,Python 2.x中,要在字符串之前加u

Ruby

[\x{4e00}-\x{9fff}]

限Ruby 1.9以上版本,且必須顯式指定Unicode模式

PHP

[\u{4e00}-\u{9fff}]

必須指定Unicode模式

  根據Unicode規范,每一個Unicode字符除了有唯一代碼點對應,還具有其它屬性,現在詳細介紹三種屬性,它們是:Unicode Property、Unicode Block、Unicode Script,下面的圖粗略說明了這三者的關系。

  Unicode Property

  Unicode Property的記法類似『\p{L}』、『\p{Lo}』,它按照字符的功能分類Unicode字符,而不關心字符屬于哪種語言,每個Unicode字符只能屬于唯一Unicode Property。

  舉例來說,『\p{Z}』表示任意的空白字符或不可見的分隔符;『\p{P}』表示任何標點字符,等等。遇到中英文混排、全角半角同時出現的情況,我們就可以用『\p{Z}』匹配所有的空白字符(而不用關心空格到底是全角空格還是半角空格),用『\p{P}』匹配所有的標點字符(而不用關心逗號到底是中文逗號還是英文逗號),而不用費心細節。

  如果我們把Unicode Property理解為一個“字符組”,那么它一定能對應某個排除型字符組,此排除型字符組的通行記法是將『\p{xx}』中的小寫p改為大寫P,寫作『\P{xx}』。這樣,『\P{Z}』對應『\p{Z}』無法匹配的字符,『\p{P}』對應『\p{P}』無法匹配的字符。Unicode Block和Unicode Script對應的排除型字符組也是這樣標記,下面不再贅述。

  支持Unicode Property的語言有.NET、Java、PHP和Ruby(限1.9以上版本),在PHP和Ruby中使用Unicode Property時,必須要開啟Unicode模式,下面以『\p{P}』的匹配為例:

  .NET

Regex.IsMatch(‘,’, “\\p{P}”); //true 

  Java

“,”.matches(“\\p{P}”); //true 

  PHP

preg_match(‘/\p{P}/u’, ‘,’); //1 

  Ruby 1.9

‘/\p{P}/u’ =~ ‘,’ # 0 

  Unicode Property的完整信息,可參考http://www.regular-expressions.info/unicode.html

  Unicode Block

  Unicode Block則不同于Unicode Property,它按照編碼區間劃分Unicode字符,每個Unicode Block中的字符編碼都是落在同一個連續區間的。因為Unicode編碼表中,某種語言的字符通常是落在同一區間的,所以它也可以粗略表示某類語言的字符,比如\p{InHebrew}表示希伯萊語字符,『\p{InCJK_Compatibility}』表示兼容CJK(漢語、韓語、日本語)的字符。如果你細心觀察,會發現Unicod Block的名字雖然類似某種語言的名字,但都有“In”(Java風格)或者“Is”(.NET風格)前綴,這表明它其實對應的還是“落在某個區間的Unicode字符”。

  本書介紹的語言中,只有Java和.NET支持Unicode Block,它們的寫法不相同:

  Java:『\p{ InCJK_Compatibility_Ideographs }』

  .NET:『\p{ IsCJK_Compatibility_Ideographs }』

  我們可以在Java中用\p{InCJK_Compatibility}或者在.NET中用\p{IsCJK_Compatibility}粗略匹配中文字符,雖然它們可能匹配日文或者韓文字符,尚不夠精確,但許多情況下確實夠用了。

  Java

“我”.matches(“\\p{InCJK_Compatibility_Ideographs}”); //true 

  .NET

Regex.IsMatch(‘我’, “\\p{IsCJK_Compatibility_Ideographs}”); //true 

  Unicode Block的完整信息,可參考http://www.regular-expressions.info/unicode.html

  Unicode Script

  Unicode Script按照字符所屬的書寫系統來劃分Unicode字符,比如\p{Greek}表示希臘語字符,\p{Han}表示漢語(中文字符)。它的寫法類似Unicode Block,只是名字的開頭沒有“Is”或者“In”。

  在本書介紹的語言中,只有PHP和Ruby(限1.9以上版本)支持Unicode Script,PHP在使用Unicode Script時,必須開啟Unicode模式(詳見xx頁)。在這兩種語言中,我們可以很方便地用\p{Han}來匹配中文字符。

  PHP

preg_match(‘/\p{Han}/u’, ‘我’); //1 

  Ruby 1.9

/\p{Han}/u =~ '我' #0 

  Unicode Script的完整信息,請參考http://www.regular-expressions.info/unicode.html

  小結

  用正則表達式處理包含多字節字符(比如中文)的字符串時,最好使用Unicode編碼,否則多字節字符很可能被割裂為多個字節,導致匹配錯誤,最好的辦法是使用Unicode編碼。

  如果實在不能使用Unicode編碼,使用字符組時要尤其小心,普通字符組可以用多選分支取代,繞過隱患,但這種辦法對排除型字符組行不通。

  如果設定了Unicode模式,可以指定Unicode Code Point來表示某個字符,但不同語言中的記法不同,可能是\uxxxx、\u{xxxx}、\x{xxxx}。

  Unicode Property描述Unicode字符的屬性,.NET、Java、PHP和Ruby(限1.9以上版本)中可以使用Unicode Property。

  Unicode Block描述Unicode字符所在的區間,其特征是名稱以In或Is為前綴,.NET和Java中可以使用Unicode Block。

  Unicode Script描述Unicode字符所在的書寫系統,名字類似Unicode Block,但沒有In或Is前綴,PHP和Ruby(限1.9以上版本)中可以使用Unicode Script。

  無論Unicode Property、Unicode Block、Unicode Script,其對應的排除型字符組都是將開頭的\p改為\P,其它不變。

  無論Unicode Property、Unicode Block、Unicode Script,都可以視為普通字符組,在字符組[…]中進行拼接組合,比如在PHP和Ruby中,[\p{Han}\p{Po}]既可以匹配漢字字符,又可以匹配任何標點字符。

0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()