<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    paulwong

    Reqular Expressions

    雖然Reqular Expressions(以下簡(jiǎn)稱REs)在這個(gè)論壇或是其他網(wǎng)站都可以找到相當(dāng)多的資料,但是當(dāng)我自己要學(xué)的時(shí)候才發(fā)現(xiàn)有很多小地方還是看不懂,所以才 以java API裡面的說明為主,把每個(gè)符號(hào)的解釋一一弄懂,終於對(duì)REs有了初步的認(rèn)識(shí)。
    所以這份文件是以java API提到的符號(hào)解釋,加上我自己的心得及範(fàn)例所整理出來的,我用"字元"和"字元組成",這兩大部分來解釋REs的符號(hào),大部分的解釋都有範(fàn)例,這樣比 較容易瞭解,沒有範(fàn)例的部分不是太簡(jiǎn)單,就是我找不到例子,不過對(duì)於認(rèn)識(shí)REs應(yīng)該沒有多大影響。

    因?yàn)槲宜闶荝Es的初學(xué)者,所以難免有觀念上的錯(cuò)誤,加上很多"冷門"的符號(hào),要找到正確的解釋都很困難,因此我沒把握所有的資料都是正確的,如果各位有發(fā)現(xiàn)錯(cuò)誤,還請(qǐng)加以指正。

    首先是字元,REs的基本元素就是字元,所以對(duì)於字元有相當(dāng)細(xì)膩的描述方式,而且很多時(shí)候描述方式都不是唯一的,所以不必拘泥於找到最完美的寫法。
    字元分兩部分來解釋。
    1.一般字元,在還沒做字元組合時(shí),下面這些都只是"一個(gè)"字元,先要有這個(gè)觀念,才不容易弄不清楚字串和字元的區(qū)別。
    x : 一個(gè)字元,例如 "a" 表示要找含有這個(gè)字元的部分。
    [abc] : 這個(gè)字元可能是a或b或c
    [^abc] : 這個(gè)字元是除了"a" "b" "c"以外的。
    [a-zA-Z] : 這個(gè)字元是a到z或是A到Z。
    [a-d[m-p]] : 這個(gè)字元是a到d或是m到p (聯(lián)集)。
    [a-z&&[def]] : 這個(gè)字元是"d", "e", or "f" (交集)。
    [a-z&&[^bc]] : 這個(gè)字元是a-z但b c 除外,等價(jià)於 [ad-z] (差集)。
    [a-z&&[^m-p]] : 這個(gè)字元是a-z但 m-p 除外,等價(jià)於 [a-lq-z] (差集)。
    除了放在一開始的"^"以及兩個(gè)"&&"以外,放在[]裡面的都當(dāng)一般字元。
    可是\比較特殊,要\\才能當(dāng)一個(gè)\,所以程式裡面必須寫\\\\,例如,想查"\"用[]包起來要寫成"[\\]",可是java在字串裡面要寫String pattern="[\\\\]"。

    2.特殊字元. : 所有的字元,不一定包含換行。java REs的"."預(yù)設(shè)是不包含"\r"和"\n"的,但是可以用(?s)來讓"."等於所有字元,(?s)的用法下面還有說明。
    \r : Carriage return。
    \t : TAB。
    \n : 換行。
    \f : 換頁(yè)。
    \e : escape。
    \d : 數(shù)字0-9。
    \D : 非數(shù)字,數(shù)字的除外集合。
    \s : 會(huì)產(chǎn)生空白的字元也就是 [ \t\n\x0B\f\r],也就是 " "(空白)、"\t"、"\n"、"\x0B"、"\f"、"\r"。
    \S : 非會(huì)產(chǎn)生空白的字元,上面的除外集合。
    \w : 文字 a-z A-Z _ 0-9,所有的英文大小寫,數(shù)字和底線。
    \W : 非文字,文字的除外集合。
    \ : 把之後的特殊字元當(dāng)作是一般是字元,例如"\\"等於一個(gè)"\","\["等於"["。
    \Q \E:\Q到\E符號(hào)之間的特殊符號(hào)都當(dāng)一般字元處理,例如"\Q(?:X)\E",符合的字串是"(?:X)"
    ^ : 行首,例如"^e"會(huì)找出所有在行首的"e",在[]裡面如果是第一個(gè)代表反相,如果不是第一個(gè)也當(dāng)一般字元。
    $ : 行尾,例如"e$"會(huì)找出所有在結(jié)尾的"e"。
    \b : 符合文字邊界(word boundary)。也就是說在字與空格之間的位置 例如,'re\b' 符合"are" 裡的're'但是不符合"area"裡的're'
    \B : 符合非文字邊界 例如,'re\B' 符合"area"裡的're'但是不符合"are" 裡的're'
    \A : 輸入的開始。
    \G : 前一個(gè)符合的結(jié)尾的地方。
    \Z : 輸入的結(jié)尾去掉結(jié)尾符號(hào)的部分。例如字串"ABC\n",pattern "ABC\Z"就可以取得"ABC"。
    \z : 輸入的結(jié)尾。例如字串"ABC\n",pattern "ABC\z"就不相符。
    所謂的輸入,是指一次的處理資料,例如String s="ABC\nABC\tABC",就算一個(gè)輸入(input sequence)。

    到這裡為止是對(duì)字元的描述,字元的所有的可能情況應(yīng)該都可以涵蓋了,可是光是字元是沒辦法構(gòu)成字串,所以接下來是把字元組成字串的方法。

    字元組成,字元組成分三部分來解釋,下面符號(hào)的X和Y可以是一個(gè)字元也可以是一個(gè)Group。
    1.簡(jiǎn)單組合,就是把字元排在一起。
    XY : 單純排列在一起,例如"ab"就是找"ab"這個(gè)字串,和"[ab]"不一樣,"[ab]"是代表"一個(gè)字元"可能是a或b。
    X|Y : X or Y,例如 a|b,而以字元而言a|b就等於[ab],所以對(duì)單一字元效果不大,主要是用於字元範(fàn)圍的[]|[]或者群組()|()比較有意義。
    例如[a-z]|[0-9]表示不是小寫就是數(shù)字,(abc)|(123)表示是"abc"或是"123"。
    實(shí)例 "c|Car"(等同於"[cC]ar")相符字串是"car"或"Car"。<-錯(cuò)了,下面有被糾正的說明,所以就不改這裡。
    另外abc|def是指"abc"或"def",而不是"ab[cd]ef"。
    (X) : 群組,將多個(gè)字元包裝成一個(gè)群組,和單純排列不同的地方是,群組可以參照,也可以對(duì)群組設(shè)定出現(xiàn)次數(shù),
    例如(abc)+是指"abc"出現(xiàn)一次以上,abc+是指ab和一次以上的c。
    群組參照舉例來說比較容易懂,例如"(.)(.)(.).?\3\2\1",可以找出3個(gè)字的回文。如"abccba"、"xcdfdcx"。
    群組還有一個(gè)值得注意的是,群組0是留給整體的比對(duì)的結(jié)果,例如上面的例子group 0是"abccba",group 1是"a"、group 2是"b"、group 3是"c"。
    有了群組參照的觀念,後面的non-capturing group就會(huì)比較容易瞭解。。

    Group的另一個(gè)對(duì)應(yīng)符號(hào)是
    \m : m是數(shù)字,表示參照前面的group,如上述的範(fàn)例。

    2.重複次數(shù)
    出現(xiàn)次數(shù)接在字元之後,表示這個(gè)字元出現(xiàn)的次數(shù),接在Group之後就表示group的出現(xiàn)次數(shù)。
    次數(shù)描述有三種quantifiers,
    Greedy quantifiers
    X?: X出現(xiàn)0或一次
    X+: X出現(xiàn)一次以上
    X*: X出現(xiàn)0或一次以上
    X{n,}: X出現(xiàn)至少n次
    X{n,m}: X出現(xiàn)n到m次
    X{n}: X出現(xiàn)n次

    Reluctant quantifiers
    X??: X出現(xiàn)0或一次
    X+?: X出現(xiàn)一次以上
    X*?: X出現(xiàn)0或一次以上
    X{n,}?: X出現(xiàn)至少n次
    X{n,m}?: X出現(xiàn)n到m次
    X{n}?: X出現(xiàn)n次

    Possessive quantifiers
    X?+: X出現(xiàn)0或一次
    X++: X出現(xiàn)一次以上
    X*+: X出現(xiàn)0或一次以上
    X{n,}+: X出現(xiàn)至少n次
    X{n,m}+: X出現(xiàn)n到m次
    X{n}+: X出現(xiàn)n次

    光看這樣的說明是無法分出三者不同,以下舉例說明。
    Greedy quantifiers
    字串 "xfooxxxxxxfoo"
    pattern ".*foo"
    結(jié)果 xfooxxxxxxfoo
    Greedy字面翻譯是貪婪,也就是盡可能的取字串,其實(shí)最貪婪的是第三種方法,因?yàn)镚reedy還會(huì)把之後相符的資料留下來,Possessive吃的連骨頭都不剩。

    Reluctant quantifiers
    字串 "xfooxxxxxxfoo"
    pattern ".*?foo"
    結(jié)果 xfoo 和 xxxxxxfoo
    Reluctant字面翻譯是勉強(qiáng),也就是抓最小可能,像這個(gè)例子,第一次抓一個(gè)x之後發(fā)現(xiàn)後面和foo相符,就得第一個(gè)結(jié)果,然後一直到最後又得到第二個(gè)結(jié)果。

    Possessive quantifiers
    字串 "xfooxxxxxxfoo"
    pattern ".*+foo"
    結(jié)果 沒有相符合資料,因?yàn)樗械馁Y料都與"."比較相符,最後沒有剩下的字串可以和foo做比較,所以沒有符合資料。

    3.Special constructs (non-capturing)
    所謂的non-capturing就是說這個(gè)group會(huì)被比對(duì),但是不會(huì)暫存在group裡面,就是最後得到的Group裡面不會(huì)有這組資料。
    (?:X) :X會(huì)取得,但不會(huì)被保留,當(dāng)之後有用\m的時(shí)候,這個(gè)Group會(huì)不算在內(nèi),這樣的處理效能會(huì)比較好。
    (?i d m s u x) : 特別設(shè)定的flag設(shè)為on。
    (?-i -d -m -s -u -x) : 特別設(shè)定的flag設(shè)為off。
    i d m s u x的說明如下:
    i CASE_INSENSITIVE : 就是不分大小寫。(?i)
    例如
    字串 "ABC"
    pattern 用"abc"會(huì)找不到,用"(?i)abc"就會(huì)找到"ABC"。

    d UNIX_LINES : \n當(dāng)作換行,當(dāng)文件是UNIX的換行格式時(shí),要處理?yè)Q行就可以打開這個(gè)模式。(?d)
    m MULTILINE :多行模式下,^和$是以指每一行,不然是用整個(gè)字串的頭尾當(dāng)^和$。(?m)
    例如
    字串 "ABC\nABC\nABC";
    pattern "^ABC$"會(huì)找不到, "(?m)^ABC$"才會(huì)找到三個(gè)"ABC";

    s DOTALL : 預(yù)設(shè)java的.不含\n \r,這個(gè)模式可以讓.等於所有字元包含\r \n。(?s)
    例如
    字串 "htm\nhtm\nhtm"
    pattern 用".htm"會(huì)找不到,用"(?s).htm"就會(huì)找到後面兩個(gè)"\nhtm"

    u UNICODE_CASE : unicode模式。(?u)
    x COMMENTS :可以在pattern裡面使用註解,會(huì)忽略pattern裡面的whitespace,以及"#"一直到結(jié)尾。(?x)
    例如
    字串 "ABC"
    pattern 用"A B C #找字串ABC" 會(huì)找不到,用"(?x)A B C #找字串ABC",就會(huì)找到"ABC"。

    (?idmsux-idmsux:X) :X是non-capturing group並且設(shè)定flags on -off。
    X(?=X) : lookahead在要取得的字串右邊,接著X但X不被算在內(nèi)。例如Jack(?=Sprat) 則只有JackSprat的Jack會(huì)被取得,Jack(?=Sprat|Frost),則只有JackSprat和JackFrost的Jack都符合。
    X(?!X) : lookahead在要取得的字串右邊,和上面相反,例如Jack(?!Sprat) 則後面是Sprat的Jack不會(huì)被取得。
    (?<=X)X : lookbehind在要取得的字串左邊,例如"(?<=foo)bar",找接在foo之後的"bar"。還有裡面的文字必須已知長(zhǎng)度,也就是不 能用"(?<=foo+)" "(?<=foo*)" "(?<=foo{1,})",但是可以用"(?<=foo?)" "(?<=foo{1,2})" "(?<=foo{1})"。
    (?<!X)X : lookbehind在要取得的字串左邊,例如"(?<!foo)bar",找不是接在foo之後的"bar"。關(guān)於長(zhǎng)度和上面符號(hào)有相同限制。
    (?>X) : X, as an independent, non-capturing group。

    因?yàn)檫@幾個(gè)符號(hào)都是non-capturing的,所以會(huì)有一個(gè)現(xiàn)象,直接看下面範(fàn)例會(huì)比較容易瞭解
    字串 "abc"
    pattern "a(?:b)c"
    結(jié)果 "abc" 但是b沒有變成group 1,這也就是non-capturing。
    如果用 "a(b)c" 會(huì)得到 group 0是"abc",group 1是b。

    字串 "abc"
    pattern "a(?=b)c"
    結(jié)果 抓不到,因?yàn)閎並沒有被取出,要"a(?=b).c"才抓的到,而且"a(?=b).c"只會(huì)與"abc"相符,不會(huì)與"acc"等等相符。

    字串 "abc"
    pattern "a(?<=b)c"
    結(jié)果 抓不到,因?yàn)閎並沒有被取出,要"a.(?<=b)c"才抓的到,而且"a(?<=b).c"只會(huì)與"abc"相符,不會(huì)與"acc"等等相符。
    由上面例子可知,這幾個(gè)符號(hào)都不會(huì)把符合的字串取出,也就是會(huì)比對(duì)但是不會(huì)算到結(jié)果裡面(non-capturing)。
    所以lookahead和lookbehind那四個(gè)符號(hào),不適合放在字串中間,另外,因?yàn)檫@幾個(gè)符號(hào)都是non-capturing,所以在後 面加上大於0的次數(shù)都和一次是一樣的,例如字串"XB",pattern"(?<=X){9}B"一樣可以取得B,而pattern""(? <=A){0}B"也可以取得"B"。

    整個(gè)java的REs大概就這些了,只是看完這些解釋其實(shí)離可以運(yùn)用還有一小段距離,因?yàn)镽Es需要的是分析pattern的能力,而這種能力要多練習(xí)才會(huì)。
    在自己能寫出pattern之前,可以拿別人寫好的pattern來測(cè)試體會(huì)一下,下面是一個(gè)簡(jiǎn)單的測(cè)試程式。
    這個(gè)程式是我小修改網(wǎng)路上找到的範(fàn)例,會(huì)回傳符合的group,方便測(cè)試結(jié)果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    ?
    publicclass TestRegular {
    publicstaticvoid main(String[] args) {
    String inputStr = "ABC\nABC\nABC";
    String patternStr = "(?d)ABC";
    Pattern pattern = Pattern.compile(patternStr);
    Matcher matcher = pattern.matcher(inputStr);
    boolean matchFound = matcher.find();
    while(matchFound) {
    System.out.println(matcher.start() + "-" + matcher.end());
    for(int i = 0; i <= matcher.groupCount(); i++) {
    String groupStr = matcher.group(i);
    System.out.println(i + ":" + groupStr);
    }
    if(matcher.end() + 1 <= inputStr.length()) {
    matchFound = matcher.find(matcher.end());
    ?? }else{
    ?? ??break;
    ?? }
    ?? }
    ??}
    }

    測(cè)試REs也可以使用一外部工具,例如eclipse的plugin Regex tester,我很多範(fàn)例跟觀念都是用這個(gè)工具去測(cè)試的。

    最後"反組譯"幾個(gè)例子來練習(xí),就是把別人寫好的pattern試著解釋出來。

    HTML TAG
    </?[a-z][a-z0-9]*[^<>]*>
    開始是<,接著有0或1個(gè)/,接著是一個(gè)英文字,再接著是不限次數(shù)的英文或數(shù)字,之後是非"<"或">"的字元不限次數(shù)個(gè),最後以">"結(jié)尾。
    相符的是"<html>" "</html>" "<h0>"等。
    不相符字串 "<123>"

    HTML TAG 之二
    <([A-Z][A-Z0-9]*)[^>]*>(.*?)</\1>
    開始是<,然後由一個(gè)大寫英文字,和不限定個(gè)數(shù)的數(shù)字或大寫字母,構(gòu)成一個(gè)group,接著不限個(gè)數(shù)的非">"字元,然後是一個(gè) ">",不限定個(gè)數(shù)的字元(用*?才不會(huì)一直取到之後的tag去),然後是tag結(jié)束的"</",要和第一個(gè)Group的值match,最後 以">"結(jié)束。
    相符的有 "<A HREF="www.google.com.tw">Test</A>" "<P></P> "<PRE>Test</PRE>" "<H0></H0>"
    不相符字串 "<abc></def>" "<123></123>"

    IP
    \b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b
    前後都是"\b"表示在獨(dú)立的一個(gè)字的單元,用"?:"是不算group而已,250-255或200-249或0-199,接著一個(gè)".",這樣的group有3次,最後再一次0-255的group。
    這樣就是一個(gè)0.0.0.0 - 255.255.255.255的IP的pattern,而[0-9][0-9]?也可以改成[0-9]{1,2}。
    相符的字串 "140.115.83.240" "255.255.0.0"
    不相符字串 "256.1.1.2" "-1.300.1.2"

    IP之二
    \b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b

    IP之三
    [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}
    這是0.0.0.0 - 999.999.999.999的格式

    其他例子
    "[hc]+at" 相符的有 "hat", "cat", "hhat", "chat", "hcat", "ccchat" etc.
    "[hc]?at" 相符的有 "hat", "cat" and "at"
    "([cC]at)|([dD]og)" 相符的有 "cat", "Cat", "dog" and "Dog"
    "/\*.*\*/" 相符的有 /* Second comment */

    以下幾個(gè)(都是抄的)適合檢查輸入的值,因?yàn)槎紡腲到$,從字串開始到結(jié)束。
    1、非負(fù)整數(shù):”^\d+$”
    2、正整數(shù):”^[0-9]*[1-9][0-9]*$”
    3、非正整數(shù):”^((-\d+)|(0+))$”
    4、負(fù)整數(shù):”^-[0-9]*[1-9][0-9]*$”
    5、整數(shù):”^-?\d+$”
    6、非負(fù)浮點(diǎn)數(shù):”^\d+(\.\d+)?$”
    7、正浮點(diǎn)數(shù):”^((0-9)+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$”
    8、非正浮點(diǎn)數(shù):”^((-\d+\.\d+)?)|(0+(\.0+)?))$”
    9、負(fù)浮點(diǎn)數(shù):”^(-((正浮點(diǎn)數(shù)正則式)))$”
    10、英文字符串:”^[A-Za-z]+$”
    11、英文大寫串:”^[A-Z]+$”
    12、英文小寫串:”^[a-z]+$”
    13、英文字符數(shù)字串:”^[A-Za-z0-9]+$”
    14、英數(shù)字加下劃線串:”^\w+$”
    15、E-mail地址:”^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$”
    16、URL:”^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$”

    -------

    學(xué)習(xí)REs最重要是學(xué)會(huì)找到字串出現(xiàn)的特徵,特徵有時(shí)候是出現(xiàn)的位置,例如位置是行首(^),是行尾($),單字開始(\b),單字裡面(\ B),有時(shí)候是前後出現(xiàn)的字串,例如要找html tag的屬性,都會(huì)由"<"當(dāng)開始,到">"結(jié)束。而要抓"<a>"的url就可以從"href"開始。但有時(shí)候要找的字並沒有 特殊的位置,也沒關(guān)係,就把要找的字描述出來也就可以了,例如要抓日期就做一個(gè)"(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)[0-9]{2}"的pattern就可以了。

    而其實(shí)真正困難的地方在於很多pattern都要看到結(jié)果才會(huì)瞭解哪裡錯(cuò)了。
    看這個(gè)例子
    字串 "ABCDC"
    pattern "([A-Z]*).\1"
    預(yù)期要抓到CDC的,結(jié)果卻是"A" "B" "CDC",因?yàn)?是0~n所以沒抓也算,而後面的\1如果前面group沒抓到,他也跟著什麼都沒有,最後只有"."抓到第一個(gè)結(jié)果的"A",同理抓到 第二個(gè)結(jié)果的"B",然後才抓到預(yù)期的"CDC",而改成"([A-Z]+).\1"就只會(huì)抓到"CDC"了。

    最後誰能幫忙解釋一下"(?>X)",因?yàn)槲覍?shí)在分不出來他和(?:X)有什麼差別....

    posted on 2006-08-23 21:45 paulwong 閱讀(365) 評(píng)論(0)  編輯  收藏 所屬分類: J2SE

    主站蜘蛛池模板: 亚洲AV无码成人专区片在线观看| 中文字幕亚洲综合久久菠萝蜜 | 国产精品亚洲片在线观看不卡| 亚洲国产精品一区二区成人片国内| 久久精品国产亚洲AV无码偷窥| 亚洲av无码久久忘忧草| 亚洲成av人片在线天堂无| 全部在线播放免费毛片| 久久精品免费网站网| 18禁男女爽爽爽午夜网站免费| 免费看AV毛片一区二区三区| 亚洲高清无码综合性爱视频| 午夜亚洲国产理论秋霞| 亚洲欧洲免费无码| 日本精品久久久久久久久免费| 国产成人久久AV免费| 最好免费观看韩国+日本| 国产精品亚洲玖玖玖在线观看| 亚洲成a人片77777老司机| 国产精品亚洲综合五月天| 免费国产黄网站在线观看动图| 日韩中文字幕免费视频| 国产成人免费a在线资源| 亚洲va在线va天堂va不卡下载 | 国产精品成人免费观看| 在线免费观看亚洲| 四虎影视精品永久免费网站| 亚洲av日韩av不卡在线观看| 亚洲国产精品久久久久秋霞小| 99精品全国免费观看视频..| 免费a级毛片无码a∨蜜芽试看| 国产亚洲精品无码专区| 国产成人精品亚洲2020| 成av免费大片黄在线观看| 成人无码区免费视频观看 | 亚洲人成7777影视在线观看| 一级A毛片免费观看久久精品 | 18禁超污无遮挡无码免费网站 | 亚洲AV无码一区二区乱子伦| 亚洲爆乳精品无码一区二区| 日韩av无码久久精品免费|