- 正則指引(第2版)
- 余晟
- 9025字
- 2019-07-25 11:43:00
第1章 字符組
1.1 普通字符組
字符組(Character Class)[1]是正則表達式最基本的結構之一,要理解正則表達式的“靈活”,認識它是第一步。
顧名思義,字符組就是“一組”字符,在正則表達式中,它表示“在同一個位置可能出現(xiàn)的各種字符”,其寫法是在一對方括號[和]之間列出所有可能出現(xiàn)的字符,簡單的字符組包括[ab]、[314]、[#.?]等。在解決一些常見問題時,使用字符組可以大大簡化操作,下面舉“匹配數(shù)字字符”的例子來說明。
字符可以分為很多類,比如數(shù)字、字母、標點等。有時候要求“只出現(xiàn)一個數(shù)字字符”,換句話說,這個位置上的字符只能是0、1、2、…、8、9這10個字符之一。要進行這種判斷,通常的思路是:用10個條件分別判斷字符是否等于這10個字符,對10個結果取“或”,只要其中一個條件成立,就返回True,表示這是一個數(shù)字字符,其偽代碼如例1-1所示。
例1-1 判斷數(shù)字字符的偽代碼

注:因為正則表達式處理的都是“字符串”(String)而不是“字符”,所以這里假設變量charStr(雖然它只包含一個字符)也是字符串類型,使用了雙引號,但是在有些語言中字符串也用單引號表示。
這種解法的問題在于太煩瑣—如果要判斷一個小寫英文字母,就要用||連接26個判斷;如果還要兼容大寫字母,則要連接52個判斷,代碼長到幾乎無法閱讀。相反,用字符組解決起來卻異常簡單,具體思路是:列出可能出現(xiàn)的所有字符(在這個例子里就是10個數(shù)字字符),只要出現(xiàn)了其中任何一個,就返回True。例1-2給出了使用字符組判斷的例子,程序語言使用Python。
例1-2 用正則表達式判斷數(shù)字字符

re.search()是Python提供的正則表達式操作函數(shù),表示“進行正則表達式匹配”;charStr仍然是需要判斷的字符串,而[0123456789]則是以字符串形式給出的正則表達式,它是一個字符組,表示“這里可以是0、1、2、…、8、9中的任意一個字符。只要charStr與其中任何一個字符相同(或者說“charStr可以由[0123456789]匹配”),就會得到一個MatchObject對象(這個對象暫時不必關心,在第21頁會詳細講解);否則,返回None。所以判斷結果是否為None,就可以判斷charStr是否是數(shù)字字符。
當今流行的編程語言大多支持正則表達式,上面的例子在各種語言中的寫法大抵相同,唯一的區(qū)別在于如何調(diào)用正則表達式的功能,所以用法其實大同小異。例1-3列出了常見語言中的表示,如果你現(xiàn)在就希望知道語言的細節(jié),可以參考本書第三部分的具體章節(jié)。
例1-3 用正則表達式判斷數(shù)字字符在各種語言中的應用[2]

可以看到,不同語言使用正則表達式的方法也不相同。如果仔細觀察會發(fā)現(xiàn),Java、.NET、Python、PHP中的正則表達式,都要以字符串形式給出,兩端都有雙引號";而Ruby和JavaScript中的正則表達式則不必如此,只在首尾有兩個斜線字符/,這也是不同語言中使用正則表達式的不同之處。不過,這個問題現(xiàn)在不需要太關心,因為本書中大部分例子以Python程序來講解,下面講解關于Python的基礎知識,其他語言的細節(jié)留到后文會詳細介紹。
1.2 關于Python的基礎知識
本書選擇使用Python語言來演示實際的匹配結果,因為它能在多種操作系統(tǒng)中運行,安裝也很方便;另一方面,Python是解釋型語言,輸入代碼就能看到結果,方便動手實踐。考慮到不是所有人都熟悉Python,這里專門用一節(jié)的篇幅來介紹。
如果你的機器上沒有安裝Python,可以從http://python.org/download/下載,目前Python有2和3兩個版本,本書的例子以2版本為準。[3]請選擇自己平臺對應的程序下載并安裝(目前Mac OS、Linux的各種發(fā)行版一般帶有Python,具體可以在命令行下輸入python,看是否啟動對應的程序)。
然后可以啟動Python,在Mac OS和Linux下輸入python,會顯示出Python提示符,進入交互模式,如圖1-1所示(Linux下的提示符與Mac OS下的差不多,所以此處不列出);而在Windows下,需要在“開始”菜單的“程序”中,選擇Python 目錄下的Python(command line),如圖1-2所示。

圖1-1 Mac OS下的Python提示符

圖1-2 Windows下的Python提示符
Python中常用的關于正則表達式的函數(shù)是re.search(),使用它必須首先導入正則表達式對應的包(package),也就是輸入下面的代碼。

通常的用法是提供兩個參數(shù):re.search(pattern,string),其中pattern是字符串形式提供的正則表達式,string是需要匹配的字符串;如果能匹配,則返回一個MatchObject(詳細介紹請參考第254頁,暫時可以不必關心),這時提示符會顯示類似<_sre.SRE_Match object at 0x0000000001D8E578>之類的結果;如果不能匹配,結果是None(這是Python中的一個特殊值,類似其他某些語言中的Null),不會有任何顯示。圖1-3演示了運行Python語句的結果。

圖1-3 觀察re.search()匹配的返回值
注:>>>是等待輸入的提示符,以>>>開頭的行,之后文本是用戶輸入的語句;其他行是系統(tǒng)生成的,比如打印出語句的結果(在交互模式下,匹配結果會自動輸出,便于觀察;真正程序運行時不會如此)。
為講解清楚、形象、方便,本書中的程序部分需要做兩點修改。
第一,因為暫時還不需要關心匹配結果的細節(jié),只關心有沒有結果,所以在re.search()之后添加判斷返回值是否為None,如果為True,則表示匹配成功,否則返回False表示匹配失敗。為節(jié)省版面,盡可能用注釋表示這類匹配結果,如# => True或者 # => False,附在語句之后。
第二,目前我們關心的是整個字符串是否能由正則表達式匹配。但是,在默認情況下re.search(pattern,string)只判斷string的某個子串能否由pattern匹配,即便pattern只能匹配string的一部分,也不會返回None。為了測試整個string能否由pattern匹配,在pattern兩端加上^和$。^和$是正則表達式中的特殊字符,它們并不匹配任何字符,只是表示“定位到字符串的起始位置”和“定位到字符串的結束位置”(原理如圖1-4所示,如果你現(xiàn)在就希望詳細了解這兩個特殊字符,可以參考第62頁),這樣就保證,只有在整個string都可以由pattern匹配時,才算匹配成功,不返回None,如例1-4所示。

圖1-4 ^[0123456789]$的匹配
例1-4 使用^和$測試string由pattern完整匹配

1.3 普通字符組(續(xù))
介紹完關于Python的基礎知識,繼續(xù)講解字符組。字符組中的字符排列順序并不影響字符組的功能,出現(xiàn)重復字符也不會影響,所以[0123456789]完全等價于[9876543210]、[1029384756]、[9988876543210]。
不過,代碼總是要容易編寫、方便閱讀,正則表達式也是一樣的,所以一般并不推薦在字符組中出現(xiàn)重復字符。而且,還應該讓字符組中的字符排列更符合認知習慣,比如[0123456789]就好過[0192837465]。為此,正則表達式提供了-范圍表示法(range),它更直觀,能進一步簡化字符組。
所謂“-范圍表示法”,就是用[x-y]的形式表示x到y(tǒng)整個范圍內(nèi)的字符,省去一一列出的麻煩,這樣[0123456789]就可以表示為[0-9]。如果你覺得這么做看起來意義不大,那么[a-z]確實比[abcdefghijklmnopqrstuvwxyz]簡單太多了。
你可能會問,“-范圍表示法”的范圍是如何確定的?為什么要寫作[0-9],而不寫作[9-0]?
要回答這個問題,必須了解范圍表示法的實質。在字符組中,-表示的范圍,一般是根據(jù)字符對應的碼值(Code Point,也就是字符在對應編碼表中的編碼的數(shù)值)來確定的,碼值小的字符在前,碼值大的字符在后。在ASCII編碼中(包括各種兼容ASCII的編碼中),字符0的碼值是48(十進制),字符9的碼值是57(十進制),所以[0-9]等價于[0123456789];而[9-0]則是錯誤的范圍,因為9的碼值大于0,所以會報錯。程序代碼見例1-5。
例1-5 [0-9]是合法的,[9-0]會報錯

如果知道0~9的碼值是48~57,a~z的碼值是97~122,A~Z的碼值是65~90,能不能用[0-z]統(tǒng)一表示數(shù)字字符、小寫字母、大寫字母呢?
答案是:勉強可以,但不推薦這么做。根據(jù)慣例,字符組的范圍表示法用來表示一類字符(數(shù)字字符是一類,字母字符也是一類),所以雖然[0-9]、[a-z]都是很好理解的,但[0-z]卻很難理解,不熟悉ASCII編碼表的人甚至不知道這個字符組還能匹配大寫字母,更何況,在碼值48到122之間,除去數(shù)字字符(碼值48~57)、小寫字母(碼值97~122)、大寫字母(碼值65~90),還有不少標點符號(參見表1-1),從字符組[0-z]中卻很難看出來,使用時就容易引起誤會,例1-6所示的程序就很可能讓人感覺莫名其妙。
表1-1 ASCII編碼表(片段)

例1-6 [0-z]的奇怪匹配

在字符組中可以同時并列多個“-范圍表示法”,字符組[0-9a-zA-Z]可以匹配數(shù)字、大寫字母或小寫字母;字符組[0-9a-fA-F]可以匹配數(shù)字,大、小寫形式的a~f,它可以用來驗證十六進制字符,代碼見例1-7。
例1-7 [0-9a-fA-F]準確判斷十六進制字符

在不少語言中,還可以用轉義序列\(zhòng)xhex來表示一個字符,其中\(zhòng)x是固定前綴,表示轉義序列的開頭,num是字符對應的碼值(Code Point,詳見第127頁,下文用?127表示),是一個兩位的十六進制數(shù)值。比如字符A的碼值是41(十進制則為65),所以也可以用\x41表示。
字符組中有時會出現(xiàn)這種表示法,它可以表現(xiàn)一些難以輸入或者難以顯示的字符,比如\x7F;也可以用來方便地表示某個范圍,比如所有ASCII字符對應的字符組就是[\x00-\x7F],代碼見例1-8。這種表示法很重要,在第126頁還會講到它,依靠這種表示法可以很方便地匹配所有的中文字符。
例1-8 [\x00-\x7F]準確判斷ASCII字符

1.4 元字符與轉義
在上面的例子里,字符組中的橫線-并不能匹配橫線字符,而是用來表示范圍,這類字符叫作元字符(meta-character)。字符組的開方括號[、閉方括號]和之前出現(xiàn)的^、$都算元字符。在匹配中,它們有著特殊的意義。但是,有時候并不需要表示這些特殊意義,只需要表示普通字符(比如“我就想表示橫線字符-”),此時就必須做特殊處理。
先來看字符組中的-,如果它緊鄰著字符組中的開方括號[,那么它就是普通字符,其他情況下都是元字符;而對于其他元字符,取消特殊含義的做法都是轉義,也就是在正則表達式中的元字符前加上反斜線字符\。
如果要在字符組內(nèi)部使用橫線-,最好的辦法是將它排列在字符組的開頭。[-09]就是包含三個字符-、0、9的字符組;[0-9]是包含0~9這10個字符的字符組,[-0-9]則是由“-范圍表示法”0-9和橫線-共同組成的字符組,它可以匹配11個字符,例1-9說明了使用橫線-的各種情況。
例1-9 –出現(xiàn)在不同位置,含義不同

仔細觀察會發(fā)現(xiàn),在正文里說“在正則表達式中的元字符之前加上反斜線字符\”,而在代碼里寫的卻不是[0\-9],而是[0\\-9]。這并不是輸入錯誤。
因為在這段程序里,正則表達式是以字符串(String)的方式[4]提供的,而字符串本身也有關于轉義的規(guī)定(你或許記得,在字符串中有\(zhòng)n、\t之類的轉義序列)。上面說的“正則表達式”,其實是經(jīng)過“字符串轉義處理”之后的字符串的值,正則表達式[0\-9]包含6個字符:[、0、\、-、9、],在表達式中只需要使用這6個字符即可,但是在源代碼里,必須使用7個字符:\需要轉義成\\,因為處理字符串時,反斜線和它之后的字符會被認為是轉義序列(Escape Sequence),比如\n、\t都是合法的轉義序列,然而\-不是。
這個問題確實有點麻煩。正則表達式是用來處理字符串的,但它又不完全等于字符串,正則表達式中的每個反斜線字符\,在字符串中(也就是正則表達式之外)還必須轉義為\\。所以之前所說的是“正則表達式[0\-9]”,程序里寫的卻是[0\\-9],這確實有點麻煩,但需要注意。
不過,Python提供了原生字符串(Raw String),它非常適合于正則表達式:正則表達式是怎樣的,原生字符串就是怎樣的,完全不需要考慮正則表達式之外的轉義(只有雙引號字符是例外,原生字符串內(nèi)的雙引號字符必須轉義寫成\")。原生字符串的形式是r"string",也就是在普通字符串之前添加r,示例代碼如例1-10。
例1-10 原生字符串的使用

原生字符串清晰易懂,省去了轉義的麻煩,所以從現(xiàn)在開始,本書中的Python示范代碼都使用原生字符串來表示正則表達式。另外,.NET和Ruby中也有原生字符串,也有一些語言并沒有提供原生字符串(比如Java),所以在第6章(?95)會專門講解轉義問題。不過,現(xiàn)在只需要知道Python示范代碼中使用了原生字符串即可。
繼續(xù)看轉義,如果希望在字符組中列出閉方括號],比如[012]345],就必須在它之前使用反斜線轉義,寫成[012\]345];否則,結果就如例1-11所示,正則表達式將]與最近的[匹配,這個表達式就成了“字符組[012]加上4個字符345]”,它能匹配的是字符串0345]或1345]或2345],卻不能匹配]。
例1-11 ]出現(xiàn)在不同位置,含義不同

除去字符組內(nèi)部的-,其他元字符的轉義都必須在字符之前添加反斜線,[的轉義也是如此。如果只希望匹配字符串[012],直接使用正則表達式[012]是不行的,因為這會被識別為一整個字符組,它只能匹配0、1、2這三個字符中的任意一個;所以必須轉義,把正則表達式寫作\[012],請注意,只有開方括號[需要轉義,閉方括號]不需要轉義,如例1-12所示。
例1-12 取消其他元字符的特殊含義

1.5 排除型字符組
在方括號[…]中列出希望匹配的所有字符,這種字符組可以叫作“普通字符組”,它的確非常方便。不過,也有些問題是普通字符組不能解決的。
給定一個由兩個字符構成的字符串str,要判斷這兩個字符是否都是數(shù)字字符,可以用[0-9][0-9]來匹配。但是,如果要求判斷的是這樣的字符串—第一個字符不是數(shù)字字符,第二個字符才是數(shù)字字符(比如A8、x6)[5]—應當如何處理?數(shù)字字符的匹配很好處理,用[0-9]即可;“不是數(shù)字”則很難辦—不是數(shù)字的字符太多了,全部列出幾乎不可能,這時就應當使用排除型字符組。
排除型字符組(Negated Character Class)非常類似普通字符組[…],只是在開方括號[之后緊跟一個脫字符^,寫作[^…],表示“在當前位置,匹配一個沒有列出的字符”。所以[^0-9]就表示“0~9之外的字符”,也就是“非數(shù)字字符”。那么,[^0-9][0-9]就可以解決問題了,如例1-13所示。
例1-13 使用排除型字符組

排除型字符組看起來很簡單,不過新手常常會犯一個錯誤,就是把“在當前位置,匹配一個沒有列出的字符”理解成“在當前位置不要匹配列出的字符”,兩者其實是不同的,后者暗示“這里不出現(xiàn)任何字符也可以”。例1-14很清楚地說明:排除型字符組必須匹配一個字符,這點一定要記住。
例1-14 排除型字符組必須匹配一個字符

除了開方括號[之后的^,排除型字符組的用法與普通字符組幾乎完全相同,唯一需要改動的是:在排除型字符組中,如果需要表示橫線字符-(而不是用于“-范圍表示法”),那么-應該緊跟在^之后;而在普通字符組中,作為普通字符的橫線-應該緊跟在開方括號之后,如例1-15所示。
例1-15 在排除型字符組中,緊跟在^之后的-不是元字符

在排除型字符組中,^是一個元字符,但只有它緊跟在[之后時才是元字符,如果想表示“這個字符組中可以出現(xiàn)^字符”,不要讓它緊挨著[即可,否則就要轉義。例1-16給出了三個正則表達式,后兩個表達式實質是一樣的,但第三種寫法很麻煩,理解起來也麻煩,不推薦使用。
例1-16 排除型字符組的轉義

1.6 字符組簡記法
用[0-9]、[a-z]等字符組,可以很方便地表示數(shù)字字符和小寫字母字符。對于這類常用的字符組,正則表達式提供了更簡單的記法,這就是字符組簡記法(shorthands)。
常見的字符組簡記法有\d、\w、\s。從表面上看,它們與[…]完全沒聯(lián)系,但效果其實是等價的。其中\d等價于[0-9],其中的d代表“數(shù)字(digit)”;\w等價于[0-9a-zA-Z_],其中的w代表“單詞字符(word)”;\s等價于[\t\r\n\v\f](第一個字符是空格),s表示“空白字符(space)”。例1-17說明了這幾個字符組簡記法的典型匹配。
例1-17 字符組簡記法\d、\w、\s

一般印象中,單詞字符似乎只包含大小寫字母,但是字符組簡記法中的“單詞字符”不只有大小寫單詞,還包括數(shù)字字符和下畫線_,其中的下畫線_尤其值得注意:在進行數(shù)據(jù)驗證時,有可能只允許輸入“數(shù)字和字母”,有人會偷懶用\w驗證,而忽略了\w能匹配下畫線,所以這種匹配并不嚴格,[0-9a-zA-Z]才是準確的選擇。
“空白字符”并不難定義,它可以是空格字符、制表符\t、回車符\r、換行符\n等各種“空白”字符,只是不方便展現(xiàn)(因為顯示和印刷出來都是空白)。不過這也提醒我們注意,匹配時看到的“空白”可能不是空格字符,因此,\s才是準確的選擇。
字符組簡記法可以單獨出現(xiàn),也可以使用在字符組中[6],比如[0-9a-zA-Z]也可以寫作[\da-zA-Z],所以匹配十六進制字符的字符組可以寫成[\da-fA-F]。字符組簡記法也可以用在排除型字符組中,比如[^0-9]就可以寫成[^\d],[^0-9a-zA-Z_]就可以寫成[^\w],代碼如例1-18。
例1-18 字符組簡記法與普通字符組混用

相對于\d、\w和\s這三個普通字符組簡記法,正則表達式也提供了對應排除型字符組的簡記法:\D、\W和\S—字母完全一樣,只是改為大寫。這些簡記法能匹配的字符是互補的:\s能匹配的字符,\S一定不能匹配;\w能匹配的字符,\W一定不能匹配;\d能匹配的字符,\D一定不能匹配。例1-19示范了這幾個字符組簡記法的應用。
例1-19 \D、\W、\S的使用

妥善利用這種互補的屬性,可以得到一些非常巧妙的效果,最簡單的應用就是字符組[\s\S]。初看起來,在同一個字符組中并列兩個互補的簡記法,這種做法有點奇怪,不過仔細想想就會明白,\s和\S組合在一起,匹配的就是“所有的字符”(或者叫“任意字符”)。許多語言中的正則表達式并沒有直接提供“任意字符”的表示法,所以[\s\S]、[\w\W]、[\d\D]雖然看起來有點古怪,但確實可以匹配任意字符。[7]
關于字符組簡記法,最后需要補充三點:第一,如果字符組中出現(xiàn)了字符組簡記法,最好不要出現(xiàn)單獨的-,否則可能引起錯誤,比如[\d-a]就很讓人迷惑,在有些語言中,-會被作為普通字符,而在有些語言中,這樣寫會報錯;第二,以上說的\d、\w、\s的匹配規(guī)則,都是針對ASCII編碼而言的,也叫ASCII匹配規(guī)則。但是,目前一些語言中的正則表達式已經(jīng)支持了Unicode字符,那么數(shù)字字符、單詞字符、空白字符的范圍,已經(jīng)不僅限于ASCII編碼中的字符。關于這個問題,具體情況在后文有詳細的介紹,如果你現(xiàn)在就想知道,可以翻到第120頁;第三,不同的語言可能有一些專屬的獨特的字符組簡記法,比如Java 8就提供了\h、\v兩種,前者匹配“任何水平方向的空白字符”,后者匹配“任何垂直方向的空白字符”。在某些具體場景下,它們確實很方便,只是不熟悉的讀者閱讀起來會有點困難。
1.7 字符組運算
以上介紹了字符組的基本功能,它們在常用的語言中都有提供;還有些語言為字符組提供了更強大的功能,比如Java、.NET、Objective-C就提供了字符組運算的功能,可以在字符組內(nèi)進行集合運算,在某些情況下這種功能非常實用。
如果要匹配所有的元音字母(為講解簡單考慮,暫時只考慮小寫字母的情況),可以用[aeiou],但是要匹配所有的輔音字母卻沒有什么方便的辦法,最直接的寫法是[b-df-hj-np-tv-z],不但煩瑣,而且難理解。換個角度看,從26個字母中“減去”元音字母,剩下的就是輔音字母,如果有辦法做這個“減法”,就方便多了。
Java語言中提供了這樣的字符組:[[a-z]&&[^aeiou]],雖然初看有點古怪,但仔細看看,也不難理解。[a-z]表示26個英文字母,[^aeiou]表示除元音字母之外的所有字符(還包括大寫字母、數(shù)字和各種符號),兩者取交集,就得到“26個英文字母中,除去5個元音字母,剩下的21個輔音字母”。
.NET中也有這樣的功能,只是寫法不一樣。同樣是匹配輔音字母的字符組,.NET中寫作[a-z-[aeiou]],其邏輯是:從[a-z]能匹配的26個字符中,“減去”[aeiou]能匹配的元音字母。相對于Java,這種邏輯更符合直覺,但寫法卻有點古怪—不是[[a-z]-[aeiou]],而是[a-z-[aeiou]]。例1-20集中演示了Java和.NET中的字符組運算。
例1-20 字符組運算

1.8 POSIX字符組
前面介紹了常用的字符組,但是在某些文檔中,你可能會發(fā)現(xiàn)類似[:digit:]、[:lower:]之類的字符組,看起來不難理解(digit就是“數(shù)字”,lower就是“小寫”),但又很奇怪,它們就是POSIX 字符組(POSIX Character Class)。因為某些語言的文檔中出現(xiàn)了這些字符組,為避免困惑,這里有必要做一個簡要介紹。如果只使用常用的編程語言,可以忽略文檔中的POSIX字符組,也可以忽略本節(jié);如果想了解POSIX字符組,或者需要在Linux/UNIX下的各種工具(sed、awk、grep等)中使用正則表達式,最好閱讀本節(jié)。
之前介紹的字符組,都屬于Perl衍生出來的正則表達式流派(Flavor),這個流派叫作PCRE(Per Compatible Regular Expression)。除此之外,正則表達式還有其他流派,比如POSIX(Portable Operating System Interface for uniX),它是一系列規(guī)范,定義了UNIX操作系統(tǒng)應當支持的功能,其中也包括關于正則表達式的規(guī)范,[:digit:]之類的字符組就是遵循POSIX規(guī)范的字符組。
常見的[a-z]形式的字符組,在POSIX規(guī)范中仍然獲得支持,它的準確名稱是POSIX方括號表達式(POSIX bracket expression),主要用在UNIX/Linux系統(tǒng)中。POSIX方括號表達式與之前所說的字符組最主要的差別在于:在POSIX字符組中,反斜線\不是用來轉義的。所以POSIX方括號表達式[\d]只能匹配\和d兩個字符,而不是[0-9]對應的數(shù)字字符。
為了解決字符組中特殊意義字符的轉義問題,POSIX方括號表達式規(guī)定:如果要在字符組中表達字符](而不是作為字符組的結束標記),應當讓它緊跟在字符組的開方括號之后,所以[]a]能匹配的字符就是]或a;如果要在字符組中標識字符-(而不是“-范圍表示法”),就必須將它放在字符組的閉方括號]之前,所以[a-]能匹配的字符就是a或-。
另一方面,POSIX規(guī)范還定義了POSIX字符組(POSIX character class),它大致等于之前介紹的字符組簡記法,都是使用類似[:digit:]、[:lower:]之類有明確意義的記號表示某類字符。
表1-2簡要介紹了POSIX字符組,注意表格中與其對應的是ASCII字符組,也就是能匹配的ASCII字符(ASCII編碼表中碼值在0~127之間的字符)。因為POSIX規(guī)范中有一個重要概念:locale(通常翻譯為“語言環(huán)境”),它是一組與語言和文化相關的設定,包括日期格式、貨幣幣值、字符編碼等。POSIX字符組的意義會根據(jù)locale的變化而變化,表1-2介紹的只是這些POSIX字符組在ASCII編碼中的意義;如果換用其他的locale(比如使用Unicode字符集),它們的意義可能會發(fā)生變化,具體請參考第135頁。
表1-2 POSIX字符組

(續(xù)表)

POSIX字符組的使用也與PCRE字符組簡記法的使用有所不同,主要區(qū)別在于,PCRE字符組簡記法可以脫離方括號直接出現(xiàn),而POSIX字符組必須出現(xiàn)在方括號內(nèi)。所以同樣是匹配數(shù)字字符,PCRE中可以直接寫\d,而POSIX字符組必須寫成[[:digit:]]。
在本書介紹的各種語言中,Java、PHP、Ruby、Golang支持使用POSIX字符組。
在PHP中可以直接使用POSIX字符組,但是PHP中的POSIX字符組只識別ASCII字符,也就是說,任何非ASCII字符(比如中文字符)都不能由任何一個POSIX字符組匹配。
Ruby的情況稍微復雜一點。Ruby 1.8中的POSIX字符組只能匹配ASCII字符,而且不支持[:word:]和[:ASCII:];Ruby 1.9中的POSIX字符組可以匹配Unicode字符,而且支持[:word:]和[:ASCII:]。
Java中的情況更加復雜。POSIX字符組[[:name:]]必須使用\p{name}的形式,其中name為POSIX字符組對應的名字,比如[:space:]就應當寫作\p{Space},請注意第一個字母要大寫,其他POSIX字符組都是這樣,只有[:xdigit:]要寫作\p{XDigit}。還需要指出的是,Java中的POSIX字符組只能匹配ASCII字符。
Golang的情況與PHP類似,POSIX字符組可以直接使用,但是都只能匹配ASCII字符。
[1]在有的資料中,寫作Character Set,所以也有人將其翻譯為“字符類”或者“字符集”。不過在計算機術語中,“類”是和“對象”相關的,“字符集”更適合用來翻譯Character Set(比如GBK、UCS之類),所以本書中沒有采用這兩個名字。
[2]傳統(tǒng)上,Perl是正則表達式處理最方便的編程語言,考慮到今天使用Perl的人數(shù),以及Perl程序員一般都熟練掌握正則表達式的現(xiàn)實,本書沒有給出Perl語言的例子。
[3]本書初版寫作時,2.x最新的版本為2.7.1,至修訂時最新版本為2.7.14,正則語法沒有明顯變化,所以示例仍然以初版為準。Python 3雖然已經(jīng)正式發(fā)行,但考慮到2.x的已有市場份額,以及3.x的正則語法變化不大,所以講解時仍然采用2.x版本。關于2.x和3.x的差別,在Python一章有詳細介紹。
[4]具體來說,在Java、PHP、Python、.NET等語言中,正則表達式都是以字符串的形式給出的,在Ruby和JavaScript中則不是這樣。詳細的說明,請參考第96頁。
[5]一般來說,計算機中的偏移值都是從0開始的。此處考慮到敘述自然,使用了“第一個字符”和“第二個字符”的說法,其中“第一個字符”指最左端,也就是偏移值為0的字符;“第二個字符”指緊跟在它右側,也就是偏移值為1的字符。
[6]只有Java 8引入的字符組簡記法是\R例外,但它并不是一個通用的字符組簡記法。
[7]許多關于正則表達式的文檔說:點號.能匹配“任意字符”。但在默認情況下,點號其實不能匹配換行符,具體請參考第86頁。
- WildFly:New Features
- Boost程序庫完全開發(fā)指南:深入C++”準”標準庫(第5版)
- Boost C++ Application Development Cookbook(Second Edition)
- MySQL數(shù)據(jù)庫應用與管理 第2版
- 算法基礎:打開程序設計之門
- Flask Web開發(fā)入門、進階與實戰(zhàn)
- Learning Informatica PowerCenter 10.x(Second Edition)
- 編譯系統(tǒng)透視:圖解編譯原理
- Effective Python Penetration Testing
- Flux Architecture
- Python機器學習經(jīng)典實例
- R大數(shù)據(jù)分析實用指南
- 從零開始學C#
- Python機器學習算法與應用
- Orleans:構建高性能分布式Actor服務