regexp small note
常用工具所使用的正規引擎
引擎類型 | 程式 |
DFA | awk(大多數)、egrep(大多數)、flex、lex、MySQL、Procmail |
NFA | GNU 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 可不用'\'
re.search(r"^\[abc$","[abc") != None#True 必需用'\ '
re.search(r"^abc\]$","abc]") != None#True 可用'\'
re.search(r"^abc]$","abc]") != None #True 可不用'\'
關於逸出
正規表達的方式
語言 | 原生字串(Raw String) | 正規文字 | 一般字串(兩次逸出) |
Python | r"regex" | X | O |
Ruby | 'regex' | /regex/ | O |
C# | @"regex" | X | O |
VB.NET | "regex" | X | O |
JavaScript | X | /regex/ | O |
萬用字元逸出
結構 | 記法 | 逸出 | 說明 |
字元組 | [] | \[] | 只要對'['逸出,因為'['視為普通字元的話,']'就沒有其他特殊意義。 而在"[]"中的']'一定要逸出,不然會有不確定性,同理括號類都一樣。->"[\]]" |
. | \. | ||
- | \- | ||
量詞 | * + ? | \* \+ \? | |
*? +? ?? | \*\? \+\? \?\? | ||
{m,n} | \{m,n} | 只要對'{'逸出就好 | |
括號 | (...) | \(...\) | '('、')'括號都要逸出 |
多選 | | | \| | |
(...|...) | \(...\|...\) | 全都要 | |
斷言 | ^ $ | \^ \$ | |
取代參考 | $num | \$ 或 $$ |
字元組內部幾乎沒有萬用字元
re.search(r"[*]", "*") != None #True
re.search(r"[?]", "?") != None #True
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) #檔名
re.search(r"[^/]*$", path).group(0) #檔名
Windows
re.search(r"^.*\\", path).group(0) #目錄
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
\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}比對優先量詞(盡量含概多一點)
* == {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"<.*>", "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"<.*?>", "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'的方式)
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日
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
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')之"後"的位置
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']
print re.findall(r"(?m)^\w+", "one two three\nfour\nfive")# ['one', 'four', 'five']
排除型字元組(緊接著'['時)
[0-9] #表示 0~9
[^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')之"前"的位置
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']
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
re.search(r"^[^-09]$","8") !=None #True
'.'
萬用字元,可當任一字元,但是不能用跨行符號(Ex:\n),可以用其他方法代替
[\d\D][\w\W][\s\S]
[\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.
1 Fist Line
Something~~
2 Second Line.
>>> print re.sub(r"(?m)$", ".", mulString) #多行模式
1 Fist Line.
Something~~.
2 Second Line.
1 Fist Line.
Something~~.
2 Second Line.
註釋模式(free-spacing mode | extended mode)(?x)
會自動忽略表示法內的空白字元,註釋則是以#開頭到末行特束。
模式的指定方式
- 使用時塡入需要的模式 (JavaScript不支援)
(?modifier) # 加在表達式開頭,modifier 為模式的修飾符號。
Ex: re.search(r"(?i)the", "The")!=None # True, i 不區分大小寫模式 - 預先定義需要的模式
語言 不分大小寫模(Case-Insensitive)(?i) 單行模式(Single line)(?s) 多行模式(Mulitiline)(?m) 註釋模式(free-spacing mode | extended mode)(?x) .NET RegexOptions.IgonreCase RegexOptions.Singleline RegexOptions.Multiline RegexOptions.IgnorePatteernWhitespace Java Pattern.CASE_INSENSITIVE Pattern.DOTALL Pattern.MULTILINE Pattern.COMMENTS JavaScirpt /regex/i 不支援 /regex/m /regex/x PHP /regex/i /regex/s /regex/m /regex/x Python re.I
re.IGNORECASEre.S
re.DOTALLre.M
re.MULTILINEre.X
re.VERBOSERuby Regexp::IGNORECASE
/regex/iRegexp::MULTILINE
/regex/m預設就是多行模式 Regexp::EXTENDED
/regex/x
完全消除萬用字元(通常用在處理使用者輸入)
\Q...\E (Q -> 引用(quote ), E -> 引用結束(End))
在\Q和\E之間的任合字元將全部被當成普通字元,不具任合意義。完全消除萬用字元的方法列表。
在\Q和\E之間的任合字元將全部被當成普通字元,不具任合意義。完全消除萬用字元的方法列表。
語言 | 函數 | \Q...\E |
.NET | Regex.Escape(text) | ? |
JAVA | Pattern.quote(text) | O |
PHP | preg_quote(text) | X |
Python | re.escape(text) | ? |
Ruby | Regexp.quote(text) Regexp.escape(text) | ? |
Unicode
顯性指定Unicode模式 == 直接指定Unicode模式 (通常是用 'u"字串"' 來指定Unicode字串,用 'u"regex"' 來顯性指定Unicode模式。)
開頭模式修飾符號也可以指定Unicode模式 r"(?u)...." 。小數點對Unicode字元的比對(UTF-8)
簡記法
開頭模式修飾符號也可以指定Unicode模式 r"(?u)...." 。小數點對Unicode字元的比對(UTF-8)
程式語言 | regex | 顯性指定Unicode模式 | 能否比對 |
.Net | ^.$ | N | O |
PHP | /^.$/ | N | X |
PHP | /^.$/u | Y | O |
Java | ^.$ | N (不用指定) | O |
JavaScript | ^.$ | N (不能指定) | 看瀏覽器 |
Python 2 | ^.$ | N | X(如果顯性指定+Unicode字串的話則可以) |
Python 3 | ^.$ | N | O |
Ruby 1.8 | /^.$/ | N | X |
Ruby 1.8 | /^.$/u | Y | O |
Ruby 1.9 | ^.$ | N | O |
簡記法
POSIX | 說明 | ASCII | Unicode 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"抓出來。
\b比較的是非\w字元
所以如果是非Unicode模式,中文字不屬於\w所以無法分辯中文字。
而Uincdoe模式中,中英文皆是\w所以中英文無法分開"正規regex"將無法被r"(?u)\bregex\b"抓出來。
Unicode字元表示法
語言 | 表示法 | 備註 |
.NET | \uxxxx | |
Java | \uxxxx | |
JavaScript | \uxxxx | |
Python | \uxxxx | Python2中指定Unicode字串才能用。 |
PHP | \x{xxxx} | 要指定Unicode模式。 |
Ruby | \u{xxxx} | Ruby1.9以上,要顯性指定Unicode模式。 |
中文字元的範圍在4e00~9fa5之間,也可以用\p{Han}來表示。
Unicode Property的排除型字元不是加'^'在前面,而是把\p{...}的p改成大寫\P{...}。
Unicode Property的排除型字元不是加'^'在前面,而是把\p{...}的p改成大寫\P{...}。
超長的Unicode Property
|
|
|
|
|
|
|
常用語言中的分析API方法
語言 | 方法 | 備註 |
.NET | Regex.math(string) | 逐次進行分析 |
Regex.mathes(string,regex) | 一次性全部分析 | |
Java | Matcher.find() | 逐次進行分析 |
JavaScript | RegExp.exec(string) | 一次性全部分析 |
string.math(RegExp) | 一次性全部分析 | |
PHP | preg_math(pattern, subject, matches) | 逐次進行分析 |
preg_math_all(pattern, subject, matches) | 一次性全部分析 | |
Python | re.find(pattern, string) | 逐次進行分析 |
re.finditer(pattern, string) | 逐次進行分析 | |
Ruby | Regexp.match(str) | 只能找到第一次比對 |
string.index(Regexp, int) | 逐次進行分析 | |
string.scan(Regexp) | 一次性全部分析 |
常用語言中取代的方法
語言 | 方法 | 備註 |
‧NET | Regex.Replace(input, pattern, replacment) | 靜態方法 |
Regex.Replace(input, replacement) | Regex實例事先產生 | |
Java | String.replaceAll(regex, replacement) | 靜態方法 |
String.replaceFirst(regex, replacement) | 靜態方法,只進行第一次 | |
Matcher.replaceAll(replacement) | Matcher實例事先根據Pattern和String產生 | |
Matcher.replaceFirst(replacement) | 同上,只進行第一次 | |
JavaScript | string.replace(RegExp, replacement) | replacement為普通函數或回傳函數 |
PHP | preg_replace(pattern, replacement, subject) | |
preg_replace_callback(pattern, callback, subject) | callback為回傳函數 | |
Python | re.sub(pattern, replacement, string) | replacement為回傳函數 |
Ruby | string.sub(Regexp, String) | 只比對第一次 |
string.gsub(Regexp, String) | 取代所有比對 |
標點符號
留言
張貼留言