regexp small note

常用工具所使用的正規引擎


































引擎類型程式
DFAawk(大多數)、egrep(大多數)、flex、lex、MySQL、Procmail
NFAGNU Emacs、Java、grep、less、more、.NET、Python、Ruby、PHP、sed








\x #以十六進制表示



[0-9] == [\x30-\x39]

當用來判斷中文時很好用

找特殊字元的兩種方法



一般找'-'字元時是輸入"\-"來找,但因為大部份的程式語言中re是以字串的方式來處理的,所以要輸入"\\-"(程式語言碰到"\\"時會逸出成'\'),所以最後送出的是"\-"或是使用原生字串(Raw String)(並不是每個程式語言都支援) r"^[0\-a]$" == "^[0\\-a]$"

'['當一個字元時必需要用'\'。']'則可用可不用'\'(用了比較不會跟'['造成配對問題),用比較好。





re.search(r"^[abc$","[abc") != None #程式出錯,只有'['沒有對應的']'

re.search(r"^\[abc$","[abc") != None#True 必需用'\ '

re.search(r"^abc\]$","abc]") != None#True 可用'\'

re.search(r"^abc]$","abc]") != None #True 可不用'\'


關於逸出




正規表達的方式


















































































語言原生字串(Raw String)正規文字一般字串(兩次逸出)
Pythonr"regex"XO
Ruby'regex'/regex/O
C#@"regex"XO
VB.NET"regex"XO
JavaScriptX/regex/O




萬用字元逸出


































































































































結構記法逸出說明
字元組[]\[]只要對'['逸出,因為'['視為普通字元的話,']'就沒有其他特殊意義。

而在"[]"中的']'一定要逸出,不然會有不確定性,同理括號類都一樣。->"[\]]"
.\.
-\-
量詞* + ?\* \+ \?
*? +? ??\*\? \+\? \?\?
{m,n}\{m,n}只要對'{'逸出就好
括號(...)\(...\)'('、')'括號都要逸出
多選|\|
(...|...)\(...\|...\)全都要
斷言^ $\^ \$
取代參考$num\$ 或 $$








字元組內部幾乎沒有萬用字元

re.search(r"[*]", "*") != None #True

re.search(r"[?]", "?") != None #True

re.search(r"[(]", "(") != None #True


字元組內必需逸出的字元

[...\]...] , [\^...], [...\-...]












抓檔名目錄


Linux


re.search(r"^.*/", path).group(0) #目錄

re.search(r"[^/]*$", path).group(0) #檔名


Windows


re.search(r"^.*\\", path).group(0) #目錄

re.search(r"[^\\]*$", path).group(0) #檔名


字元組簡記法\d == [0-9] #數字字元

\D == ^\d

\w == [0-9a-zA-z_] #單字字元

\W == ^\w

\s == [ \t\r\n\v\f] #空白字元

\S == ^\s

\b == 單字邊界 Ex: r"\bat" 即 at->True, eat->False, attention->True

\B == ^\b

\A == "整個"字串中的"最起始位置"(類似'^'但忽視換行字元('\n'))

\Z == 相當於非多行模式中的$

\z == "整個"字串中的"最後面位置"(類似'$'但忽視換行字元('\n')) Ps:Python中沒有\z,但是Python中的\Z等於其他語言中的\z





JavaScript沒有'\A','\z','\Z'但是他的'^','$'會無視換行字元('\n')


常用量詞(緊接在字元後面)

* == {0,}

+ == {1,}

? == {0.1}比對優先量詞(盡量含概多一點)





re.search(r"<.*>", "testtest").group(0) #''

re.search(r"<.*>", "test456>test").group(0) #'456>'

re.search(r"<.*>", "test<12456>test").group(0) #'<12456>'


忽略優先量詞(在量詞後面再加一個'?')(盡量含概少一點(只要一符合就輸出)(非貪婪表示))


re.search(r"<.*?>", "testtest").group(0) #''

re.search(r"<.*?>", "test456>test").group(0) #''

re.search(r"<.*?>", "test<12456>test").group(0) #'<12'


參考分組(補捉分補)(用括號包起來的正規表達式稱為參考分組)


re.search(r"(\d{4})-(\d{2})-(\d{2})", "2014-08-27").group(1) #2014

re.search(r"(\d{4})-(\d{2})-(\d{2})", "2014-08-27").group(2) #08

re.search(r"(\d{4})-(\d{2})-(\d{2})", "2014-08-27").group(3) #27

反向參考(反回去參考前面抓到分組內容)(用'\num'的方式)


反向參考不會抓取錨點的資訊EX:(\beat\b)如果用\num反向參考來抓取的話並不會抓到兩個\b。


print re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\2.\3.\1", "2014-08-27") #08.27.2014

print re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\1年\2月\3日", "2014-08-27") #2014年08月27日













































































語言運算式中取代中
.NET\num$num
Java\num$num
JavaScript$num$num
PHP\num\num或$num (PHP4.0.4以上)
Python\num\num
Ruby\num\num




分組命名(參考分組原本是用num(數字)來命名,除此之外再用字母來命名)


















































































語言分組記法運算式中取代中
.NET(?<name>...)\k<name>$<name>
JAVA7(?<name>...)\k<name>$<name>
PHP(?P<name>...)(?P=name)只能用\${num}
Python(?P<name>...)(?P=name)\g<name>
Ruby1.9(?<name>...)\k<name>\k<name>




非參考分組(非補捉分組)('?:'緊接在參考分組左括號後方,不會儲存符合的文字(可加強效能)


re.search(r"(\d{4})-(\d{2})-(\d{2})", "2014-08-29").group(1) #2014

re.search(r"(?:\d{4})-(\d{2})-(\d{2})", "2014-08-29").group(1) #08


環視(緊接著左括號)(旁邊的文字必需符合條件才能成立,但本身不比對)(原地判斷)(不影響參考分組)(各種語言支援反向環視的程度不同,但都支正向環視)(少在反向環視中用復雜的運算式(為了效能))


否定順序環視(否定正向環視)(否定右看看)'(?!...)'


(?!...)的右方不能是...


否定逆序環視(否定反向環視)(否定左看看)'(?<!...)'


(?<!...)的左方不能是...


一定順序環視(一定正向環視)(一定右看看)'(?=...)'


(?=...)的右方一定是...


一定逆序環視(一定反向環視)(一定左看看)'(?<=...)'


(?<=...)的左方一定是...


Ex:數字三位數分號


print re.sub(r"(?<=\d)(?=(\d{3})+(?!\d))", ",", "12345678") # 12,345,678


萬用字元'^'(起始錨點)





定位字串起始位置(放在表達式之最前方時)


re.search(r"123","01234") != None #True 有符合123

re.search(r"^123","01234") != None#False 第一個字元必需要是1

re.search(r"123","1234") != None #True 第一個字元是1且符合123

多行模式'(?m)'也會比對結束字元('\n')之"後"的位置


print re.findall(r"^\w+", "one two three\nfour\five") # ['one']

print re.findall(r"(?m)^\w+", "one two three\nfour\nfive")# ['one', 'four', 'five']


排除型字元組(緊接著'['時)


[0-9] #表示 0~9

[^0-9] #表示非0~9


'$'(結束錨點)


定位字串結束位置(放在表達式之最後面時)


re.search(r"123","01234") != None #True 有符合123

re.search(r"123$","01234") != None#False 最後的字元必需要是1

re.search(r"123$","0123") != None #True 最後的字元是1且符合123

多行模式'(?m)'也會比對結束字元('\n')之"前"的位置


print re.findall(r"(?m)\w+$", "one two three four five") # ['five']

print re.findall(r"(?m)\w+$", "one two three\nfour\nfive") # ['three', 'four', 'five']

print re.findall(r"(?m)\w+$", "one two three\nfour\nfive\n") # ['three', 'four', 'five']


'-'


緊接在'^'之後的'-'不是萬用字元


re.search(r"^[^-09]$","-") !=None #False

re.search(r"^[^-09]$","8") !=None #True


'.'


萬用字元,可當任一字元,但是不能用跨行符號(Ex:\n),可以用其他方法代替

[\d\D][\w\W][\s\S]




比對模式(match mode)


模式的種類


不分大小寫模(Case-Insensitive)(?i)


不區分字母的大小寫。


單行模式(Single line)(?s)


所有文字似乎只在一行裡面,換行符號只是這一行中的"普通字元"(固小數點在單行模式可以比對換行符號)。


多行模式(Mulitiline)(?m)


影響'^'和'$'的比對規則,讓他們不僅代表整段文字的開頭結尾,也代表每一行的開頭結尾。


mulString="1 Fist Line\nSomething~~\n2 Second Line"


>>> print re.sub(r"$", ".", mulString)

1 Fist Line

Something~~

2 Second Line.


>>> print re.sub(r"(?m)$", ".", mulString) #多行模式

1 Fist Line.

Something~~.

2 Second Line.


註釋模式(free-spacing mode | extended mode)(?x)


會自動忽略表示法內的空白字元,註釋則是以#開頭到末行特束。


模式的指定方式






  1. 使用時塡入需要的模式 (JavaScript不支援)

    (?modifier) # 加在表達式開頭,modifier 為模式的修飾符號。

    Ex: re.search(r"(?i)the", "The")!=None # True, i 不區分大小寫模式


  2. 預先定義需要的模式









































































































    語言不分大小寫模(Case-Insensitive)(?i)單行模式(Single line)(?s)多行模式(Mulitiline)(?m)註釋模式(free-spacing mode | extended mode)(?x)
    .NETRegexOptions.IgonreCaseRegexOptions.SinglelineRegexOptions.MultilineRegexOptions.IgnorePatteernWhitespace
    JavaPattern.CASE_INSENSITIVEPattern.DOTALLPattern.MULTILINEPattern.COMMENTS
    JavaScirpt/regex/i不支援/regex/m/regex/x
    PHP/regex/i/regex/s/regex/m/regex/x
    Pythonre.I

    re.IGNORECASE
    re.S

    re.DOTALL
    re.M

    re.MULTILINE
    re.X

    re.VERBOSE
    RubyRegexp::IGNORECASE

    /regex/i
    Regexp::MULTILINE

    /regex/m
    預設就是多行模式Regexp::EXTENDED

    /regex/x








完全消除萬用字元(通常用在處理使用者輸入)


\Q...\E (Q -> 引用(quote ), E -> 引用結束(End))

在\Q和\E之間的任合字元將全部被當成普通字元,不具任合意義。完全消除萬用字元的方法列表。



































































語言函數\Q...\E
.NETRegex.Escape(text)?
JAVAPattern.quote(text)O
PHPpreg_quote(text)X
Pythonre.escape(text)?
RubyRegexp.quote(text)

Regexp.escape(text)
?




Unicode


顯性指定Unicode模式 == 直接指定Unicode模式 (通常是用 'u"字串"' 來指定Unicode字串,用 'u"regex"' 來顯性指定Unicode模式。)

開頭模式修飾符號也可以指定Unicode模式 r"(?u)...." 。小數點對Unicode字元的比對(UTF-8)











































































































































程式語言regex顯性指定Unicode模式能否比對
.Net^.$NO
PHP/^.$/NX
PHP/^.$/uYO
Java^.$N (不用指定)O
JavaScript^.$N (不能指定)看瀏覽器
Python 2^.$NX(如果顯性指定+Unicode字串的話則可以)
Python 3^.$NO
Ruby 1.8/^.$/NX
Ruby 1.8/^.$/uYO
Ruby 1.9^.$NO


簡記法























































































































































































































































POSIX說明ASCIIUnicode Property簡記法Java
[:alnum:]字母&數字[a-zA-Z0-9][\p{L&}\p{Nd}]\p{Alnum}
[:alpha:]字母[a-zA-Z]\p{L&}\p{Alpha}
[:ascii:]ASCII字元[\x00-\x7F]\p{InBasicLatin}\p{ASCII}
[:blank:]空白&定位[ \t][\p{Zs}\t]\h\p{Blank}
[:cntrl:]控制字元[\x00-\x1F\x7F]\p{Cc}\p{Cntrl}
[:digit:]數字字元[0-9]\p{Nd}\d\p{Digit}
[:graph:]除空白以外字元[\x21-\x7E][^\p{Z}\p{C}]\p{Graph}
[:lower:]小寫字元[a-z]\p{Ll}\p{Lower}
[:print:]所有可見字元[\x20-\x7E]\P{C}\p{Print}
[:punct:]標點符號[!"#$%&'()*+,

\-./:;<=>?@

[\\\]^_`{|}~]
[\p{P}\p{S}]\p{Punct}
[:space:]空白字元(抱括換行)[ \t\r\n\v\f][\p{Z}\t\r\n\v\f]\s\p{Space}
[:upper:]大寫字元[A-Z]\p{Lu}\p{Upper}
[:word:]單字字元[A-Za-z0-9_][\p{L}\p{N}\p{Pc}]\w
[:xdigit:]十六進制[A-Fa-f0-9][A-Fa-f0-9]\p{XDigit}






 單字邊界

\b比較的是非\w字元

所以如果是非Unicode模式,中文字不屬於\w所以無法分辯中文字。

而Uincdoe模式中,中英文皆是\w所以中英文無法分開"正規regex"將無法被r"(?u)\bregex\b"抓出來。








 Unicode字元表示法













































































語言表示法備註
.NET\uxxxx
Java\uxxxx
JavaScript\uxxxx
Python\uxxxxPython2中指定Unicode字串才能用。
PHP\x{xxxx}要指定Unicode模式。
Ruby\u{xxxx}Ruby1.9以上,要顯性指定Unicode模式。




 中文字元的範圍在4e00~9fa5之間,也可以用\p{Han}來表示。

Unicode Property的排除型字元不是加'^'在前面,而是把\p{...}的p改成大寫\P{...}。


超長的Unicode Property












































  • \p{C} #不可見的控制字元和未使用的碼值。



    • \p{Cc} #ASCII中0x00~0x1F的不可控制字元。


    • \p{Cf} #不可見的格式字元。


    • \p{Co} #留著作私用的碼值。


    • \p{Cs} #UTF-16中surrogate pair的一半。


    • \p{Cn} #未指定的碼值。












  • \p{L} #各種言語中的字母。



    • \p{Ll} #能夠大寫字母的小寫形式。


    • \p{Lt} #只有在單字首才大寫的字元。


    • \p{L&} #等於Li, Lu, Lt 的組合。


    • \p{Lo} # 沒有大小寫形態的字元。


    • \p{Lu} #能夠小寫字器的大寫形式。












  • \p{M} #用來與其他字元結合的字元 (聲調、母音變化等等)



    • \p{Mc} #與其他字元組合並且會佔用空間的字元。


    • \p{Me} #需要成對出現的字元,{} [] () ... 。


    • \p{Mn} #與其他字元組合但不會佔用額外空間的字元。












  • \p{N} #各種撰寫系統中的數字。



    • \p{Nd} #各種撰寫系統中0~9的字元。


    • \p{Nl} #像是字元的數字,Ex:羅馬字元。


    • \p{No} #上標或索引數字,或是0~9之外的數字(不含撰寫系統中的數字。












  •  \p{P} #各種標點符號。



    • \p{Pd} #各種連字號。


    • \p{Ps} #成對但不同的符號前半('(', '[', '「' ...)。


    • \p{Pe} #成對但不同的符號後半(')', ']', '」' ...)。


    • \p{Pi} #成對且相同的符號前半(''', '"')。


    • \p{Pf} #成對且相同的符號後半(''', '"')。


    • \p{Pc} #類似底線類的標點符號。


    • \p{Po} #除橫線,括號,引號,連接子之外所有標點符號。












  •  \p{S} #數位記號、貨幣符號。



    • \p{Sm} #數位記號。


    • \p{Sc} #貨幣符號。


    • \p{Sk} #由多個字元組成的組合字元。


    • \p{So} #數位記號、貨幣符號和組合字元以外的符號字元。












  • \p{Z} #空白字元或不可見的分隔符號。



    • \p{Zs} #不可見但佔用空間的空白字元。


    • \p{Zl} #分行符號 U+2028。


    • \p{Zp}  #分段符號 U+2029。












常用語言中的分析API方法
































































































































語言方法備註
.NETRegex.math(string)逐次進行分析
Regex.mathes(string,regex)一次性全部分析
JavaMatcher.find()逐次進行分析
JavaScriptRegExp.exec(string)一次性全部分析
string.math(RegExp)一次性全部分析
PHPpreg_math(pattern, subject, matches)逐次進行分析
preg_math_all(pattern, subject, matches)一次性全部分析
Pythonre.find(pattern, string)逐次進行分析
re.finditer(pattern, string)逐次進行分析
RubyRegexp.match(str)只能找到第一次比對
string.index(Regexp, int)逐次進行分析
string.scan(Regexp)一次性全部分析




 常用語言中取代的方法
































































































































語言方法備註
‧NETRegex.Replace(input, pattern, replacment)靜態方法
Regex.Replace(input, replacement)Regex實例事先產生
JavaString.replaceAll(regex, replacement)靜態方法
String.replaceFirst(regex, replacement)靜態方法,只進行第一次
Matcher.replaceAll(replacement)Matcher實例事先根據Pattern和String產生
Matcher.replaceFirst(replacement)同上,只進行第一次
JavaScriptstring.replace(RegExp, replacement)replacement為普通函數或回傳函數
PHPpreg_replace(pattern, replacement, subject)
preg_replace_callback(pattern, callback, subject)callback為回傳函數
Pythonre.sub(pattern, replacement, string)replacement為回傳函數
Rubystring.sub(Regexp, String)只比對第一次
string.gsub(Regexp, String)取代所有比對


















標點符號

留言

這個網誌中的熱門文章

成人剪舌繫帶聽過嗎?我剪了!!

Scp - ssh 的遠端檔案傳輸指令

睡覺使你更有效率