Ruby的正則表達式
2015/12/03 23:57
瀏覽511
迴響0
推薦0
引用0
Regexp 類:
1、使用 /.../ 或者 %r{} 創建,或者 Regexp.new
/hay/ =~ 'haystack' #=> 0 # 返回值為匹配字符所在位置,或者 nil
/y/.match('haystack') #=> # # 返回值為 MatchData 或者 nil
2、元字符有: (, ), [, ], {, }, .,?, +, *.
3、模板的行為類似於雙引號,可以加入轉義符
/\s\u{6771 4eac 90fd}/.match("Go to 東京都")
#=> #
同樣可以嵌入 #{...}
place = "東京都"
/#{place}/.match("Go to 東京都")
#=> #
4、[0-9a-f] 、支持 && 操作符:取兩個表達式的交集
/[a-w&&[^c-g]z]/ # ([a-w] AND ([^c-g] OR z))
# 等級於如下表達式
/[abh-w]/
5、
/./ - 任意字符,不包括回車換行
/./m - 任意字符,包括回車換行,類似於 perl 的 s ( m 修飾符啟用多行模式,就是將多行當一行處理)
/\w/ - 等價於 ([a-zA-Z0-9_])
/\W/ - 同 \w 正相反 ([^a-zA-Z0-9_])
/\d/ - 數字 ([0-9])
/\D/ - 非數字 ([^0-9])
/\h/ - 16進制,等價於 ([0-9a-fA-F])
/\H/ - 非16進制 ([^0-9a-fA-F])
/\s/ - 空白字符,包括回車換行: /[ \t\n\f]/
/\S/ - 非空白字符: /[^ \t\n\f]/
6、
/[[:alnum:]]/ - 等價於 [0-9a-zA-z]
/[[:alpha:]]/ - 等價於 [a-zA-Z]
/[[:blank:]]/ - 空格或 tab
/[[:cntrl:]]/ - ctrl
/[[:digit:]]/ - [0-9]
/[[:graph:]]/ - 非空白字符 (excludes spaces, control characters, and similar)
/[[:lower:]]/ - 等價於 [a-z]
/[[:print:]]/ - Like [:graph:], but includes the space character
/[[:punct:]]/ - Punctuation character
/[[:space:]]/ - 空白字符 ([:blank:], 換行,回車, 等.)
/[[:upper:]]/ - 大寫字符,[A-Z]
/[[:xdigit:]]/ - 16進制數,等價於 [0-9a-fA-F] (i.e., 0-9a-fA-F)
/[[:word:]]/ - A character in one of the following Unicode general categories Letter, Mark, Number, Connector_Punctuation
/[[:ascii:]]/ - A character in the ASCII
7、匹配次數
* - >= 0
+ - >= 1
? - 0 or 1
{n} - = n
{n,} - >=n
{,m} - <=m
{n,m} - n < ... < m
8、結果捕獲,使用 (..)
a、
# 'at' is captured by the first group of parentheses, then referred to
# later with \1
/[csh](..) [csh]\1 in/.match("The cat sat in the hat")
#=> #
# Regexp#match returns a MatchData object which makes the captured
# text available with its #[] method.
/[csh](..) [csh]\1 in/.match("The cat sat in the hat")[1] #=> 'at'
b、
對捕獲結果命名,使用 (?) 或者 (?'name')
/\$(?\d+)\.(?\d+)/.match("$3.67")
=> #
/\$(?\d+)\.(?\d+)/.match("$3.67")[:dollars] #=> "3"
c、
引用上述匹配結果,使用 \k
/(?[aeiou]).\k.\k/.match('ototomy')
#=> #
註意:不能同時使用命名引用和數字引用,即不能同時使用 \k 和 $1 等方式
d、如果 regexp 位於表達式,或者 =~ 操作符左側,ruby 會生成一個本地變量,保存結果,可以直接使用
/\$(?\d+)\.(?\d+)/ =~ "$3.67" #=> 0
dollars #=> "3"
9、分組
a、
(..) 即分組,其後可跟重復量詞
# The pattern below matches a vowel followed by 2 word characters:
# 'aen'
/[aeiou]\w{2}/.match("Caenorhabditis elegans") #=> #
# Whereas the following pattern matches a vowel followed by a word
# character, twice, i.e. [aeiou]\w[aeiou]\w: 'enor'.
/([aeiou]\w){2}/.match("Caenorhabditis elegans")
#=> #
b、
(?:…) 表示分組,但不捕獲結果。
# The group of parentheses captures 'n' and the second 'ti'. The
# second group is referred to later with the backreference \2
/I(n)ves(ti)ga\2ons/.match("Investigations")
#=> #
# The first group of parentheses is now made non-capturing with '?:',
# so it still matches 'n', but doesn't create the backreference. Thus,
# the backreference \1 now refers to 'ti'.
/I(?:n)ves(ti)ga\1ons/.match("Investigations")
#=> #
c、原子分組 ....
沒怎麽看懂....
# The " in the pattern below matches the first character of
# the string, then .* matches Quote". This causes the
# overall match to fail, so the text matched by .* is
# backtracked by one position, which leaves the final character of the
# string available to match "
/".*"/.match('"Quote"') #=> #"Quote"">
# If .* is grouped atomically, it refuses to backtrack
# Quote", even though this means that the overall match fails
/"(?>.*)"/.match('"Quote"') #=> nil
這篇文章講得很清楚: 簡單的說,Atomic Grouping的主要功能便是取消回溯,提高效率——如果匹配成功,它與普通的grouping並無區別,但是如果匹配失敗,所有位於Atomic Grouping中的狀態會全部失效。
一般正則表達式為貪婪匹配,或者非貪婪匹配。在匹配不成功時,會進行回溯,不斷測試各個分支。原子分組就是將 (?> pat) 中的 pat 匹配作為一個原子操作,(不管貪婪還是非貪婪),要麽成功,要麽失敗,一錘子買賣,不做回溯。
10、子表達式引用 : Subexpression Calls
通過 \g 語法 對 (?) 匹配到的內容進行反向引用。也可以通過數字來進行,類似於前面的 $1。
# Matches a ( character and assigns it to the paren
# group, tries to call that the paren sub-expression again
# but fails, then matches a literal ).
/\A(?\(\g*\))*\z/ =~ '()'
/\A(?\(\g*\))*\z/ =~ '(())' #=> 0
# ^1
# ^2
# ^3
# ^4
# ^5
# ^6
# ^7
# ^8
# ^9
# ^10
Matches at the beginning of the string, i.e. before the first character.
Enters a named capture group called paren
Matches a literal (, the first character in the string
Calls the paren group again, i.e. recurses back to the second step
Re-enters the paren group
Matches a literal (, the second character in the string
Try to call paren a third time, but fail because doing so would prevent an overall successful match
Match a literal ), the third character in the string. Marks the end of the second recursive call
Match a literal ), the fourth character in the string
Match the end of the string
11 、選擇性匹配
兩個表達式通過 | 關聯,表示任意匹配其中一個即可,例子:
/\w(and|or)\w/.match("Feliformia") #=> #
/\w(and|or)\w/.match("furandi") #=> #
/\w(and|or)\w/.match("dissemblance") #=> nil
12、字符屬性 : Character Properties
東西太多,只舉幾個例子:
/\p{Alnum}/ - Alphabetic and numeric character
/\p{Alpha}/ - Alphabetic character
/\p{Blank}/ - Space or tab
/\p{Cntrl}/ - Control character
/\p{Digit}/ - Digit
/\p{Graph}/ - Non-blank character (excludes spaces, control characters, and similar)
/\p{Lower}/ - Lowercase alphabetical character
/\p{Print}/ - Like \p{Graph}, but includes the space character
/\p{Punct}/ - Punctuation character
/\p{Space}/ - Whitespace character ([:blank:], newline, carriage return, etc.)
/\p{Upper}/ - Uppercase alphabetical
/\p{XDigit}/ - Digit allowed in a hexadecimal number (i.e., 0-9a-fA-F)
/\p{Word}/ - A member of one of the following Unicode general category Letter, Mark,
13、錨
^ - 匹配行首
$ - 匹配行尾
\A - 匹配字符串的開頭
\Z - 匹配字符串的結尾. 如果字符串結尾是回車換行,只匹配回車換行前。
\z - 匹配字符串的結尾
\G - Matches point where last match finished
\b - Matches word boundaries when outside brackets; backspace (0x08) when inside brackets
\B - Matches non-word boundaries
(?=pat) - Positive lookahead assertion: ensures that the following characters match pat, but doesn't include those characters in the matched text
(?!pat) - Negative lookahead assertion: ensures that the following characters do not match pat, but doesn't include those characters in the matched text
(?<=pat) - Positive lookbehind assertion: ensures that the preceding characters match pat, but doesn't include those characters in the matched text
(?
# If a pattern isn't anchored it can begin at any point in the string
/real/.match("surrealist") #=> #
# Anchoring the pattern to the beginning of the string forces the
# match to start there. 'real' doesn't occur at the beginning of the
# string, so now the match fails
/\Areal/.match("surrealist") #=> nil
# The match below fails because although 'Demand' contains 'and', the
pattern does not occur at a word boundary.
/\band/.match("Demand")
# Whereas in the following example 'and' has been anchored to a
# non-word boundary so instead of matching the first 'and' it matches
# from the fourth letter of 'demand' instead
/\Band.+/.match("Supply and demand curve") #=> #
# The pattern below uses positive lookahead and positive lookbehind to
# match text appearing in tags without including the tags in the
# match
/(?<=)\w+(?=<\/b>)/.match("Fortune favours the bold")
#=> #
14、修飾符
/pat/i - 忽略大小寫
/pat/m - 允許 . 匹配回車換行;同 perl 的 s 修飾符類似。
/pat/x - 忽略空白字符和註釋;模板可以寫的較為優美,易讀。
/pat/o - 僅對 #{} 做一次解析;具體用法還沒搞清楚。
i, m, 和x 修飾符可以用在子表達式中。通過 (?) 語法進行開關。
例子如下:這裏 (?i:b) 表示對字符 b 忽略大小寫
/a(?i:b)c/.match('aBc') #=> #
/a(?i:b)c/.match('abc') #=> #
使用 x 修飾符的例子:模板中的空白字符和 # 註釋都會被忽略,因此可以寫出較為優美的正則。
# A contrived pattern to match a number with optional decimal places
float_pat = /\A
[[:digit:]]+ # 1 or more digits before the decimal point
(\. # Decimal point
[[:digit:]]+ # 1 or more digits after the decimal point
)? # The decimal point and following digits are optional
\Z/x
float_pat.match('3.14') #=> #
註意:在 x 修飾符作用下,模板如果匹配空白字符,需要使用 \s 或者 \p{Space}.
不使用 x 修飾符,添加註釋使用 (?#comment)
另:模式匹配使用的字符編碼 encoding 同你的源文件一致,但也可通過以下修飾符修改:
/pat/u - UTF-8
/pat/e - EUC-JP
/pat/s - Windows-31J
/pat/n - ASCII-8BIT
正則表達式可以解析的字符串,兩者編碼或者一致,或者正則使用 US-ASCII 編碼,字符串使用 ASCII 兼容的編碼。
如果編碼不同,會引發 Encoding::CompatibilityError 異常。
可以使用 Regexp#fixed_encoding? 強行指定編碼:
r = Regexp.new("a".force_encoding("iso-8859-1"),Regexp::FIXEDENCODING)
r =~"a\u3042"
#=> Encoding::CompatibilityError: incompatible encoding regexp match
(ISO-8859-1 regexp with UTF-8 string)
15、性能
一些變態的寫法會導致性能極差:
s = 'a' * 25 + 'd' 'a' * 4 + 'c'
#=> "aaaaaaaaaaaaaaaaaaaaaaaaadadadadac"
# 下面幾句完成相同的匹配
/(b|a)/ =~ s #=> 0
/(b|a+)/ =~ s #=> 0
/(b|a+)*\/ =~ s #=> 0
# 很明顯下面這句耗時更長
/(b|a+)*c/ =~ s #=> 32
This happens because an atom in the regexp is quantified by both an immediate + and an enclosing * with nothing to differentiate which is in control of any particular character. The nondeterminism that results produces super-linear performance. (Consult Mastering Regular Expressions (3rd ed.), pp 222, by Jeffery Friedl, for an in-depth analysis). This particular case can be fixed by use of atomic grouping, which prevents the unnecessary backtracking:
(start = Time.now) && /(b|a+)*c/ =~ s && (Time.now - start)
#=> 24.702736882
(start = Time.now) && /(?>b|a+)*c/ =~ s && (Time.now - start)
#=> 0.000166571
另一個糟糕的例子,運行它足足花了60秒:
# Match a string of 29 as against a pattern of 29 optional
# as followed by 29 mandatory as.
Regexp.new('a?' * 29 + 'a' * 29) =~ 'a' * 29
The 29 optional as match the string, but this prevents the 29 mandatory as that follow from matching. Ruby must then backtrack repeatedly so as to satisfy as many of the optional matches as it can while still matching the mandatory 29. It is plain to us that none of the optional matches can succeed, but this fact unfortunately eludes Ruby.
One approach for improving performance is to anchor the match to the beginning of the string, thus significantly reducing the amount of backtracking needed.
Regexp.new('\A' 'a?' * 29 + 'a' * 29).match('a' * 29)
#=> #
限會員,要發表迴響,請先登入


