Groovy正規表現

8127 ワード

記事の目次
  • 背景
  • Grovy正規表現文法
  • 元の文字問題
  • PatternとMatch
  • Pattern.matchesとPattern.matcher
  • Matchにおけるcapturing group概念
  • Matchリセット
  • 小結
  • 背景
    プロジェクトはGraadleを自動化構築のツールとして使っています。暇な時にこのツールの使い方とその配置ファイルに依存するGrouvy文法を強化しました。Grouvy文法を勉強している時に面白いものを見つけました。Grouvyの正規表現です。そこで本稿ではGroovyの正規表現の特徴とGrovy正規表現とJava正則表現の違いをまとめました。
    Groovy正規表現文法
    GrouvyはJava言語の拡張の一つであり、JavaのJDKをシームレスに使用することができます。そして、自身はSDKでJavaを拡張しました。Grouvyの正規表現は本質的にJDKのjava.lang.regexカバンの中のclassに使用されます。この部分は、個人的には「シンタックスキャンディー」と見なされます。ただし、Grouvyでは正規表現を使って、最も簡単な正規表現を見ることができます。
    def reg1 = ~'he*llo'
    def reg2 = /he*llo/
    println "reg1 type is ${reg1.class}"
    println "reg2 type is ${reg2.class}"
    println "hello".matches(reg1)
    println "hello".matches(reg2)
    
    
    実行結果:
    reg1 type is class java.util.regex.Pattern
    reg2 type is class java.lang.String
    true
    true
    
    上記の式では、~+文字列(および二重斜線セパレータモード)を使用して、正規表現Grouvyでは~を使用して正規表現を定義することをサポートしています。印刷されたregタイプは、文字列ではなくPatternタイプです。注意すべき点は、上記の例の~=の間にスペースがあります。Groovyには=~操作記号が存在するので、このオペレータはオペレータを問い合わせるために文字列を使用した後、正規表現を返すことを要求します。java.util.regex.Matcherオブジェクトを返します。また、オペレータ==~も紛らわしいです。このオペレータはオペレータとマッチしています。後は正規表現と同じです。Booleanタイプが返されます。このオペレータは、前に与えられた文字列と後の正規表現とが完全に一致していることを要求します。以下の列のようなtrueに戻ります。
    def val1 = "hello" =~ "he*llo"
    println val1.class
    print val1.matches()
    
    実行結果
    class java.util.regex.Matcher
    true
    
    Groovy中のマッチングオペレータを使用すると、上述の操作を簡略化することができる。
    def val1 = "hello" ==~ "he*llo"
    println val1.class
    print val1
    
    実行結果:
    class java.lang.Boolean
    true
    
    元の文字の問題
    正規表現には、\wといったような特殊な文字があることを知っています。これらの文字は、一般的に[a-zA-Z0-9]で始まるので、この場所は変換文字問題に関連しています。例を挙げると、
    def val1 = "test value"
    println 'value is ${val1}'
    println "value is ${val1}"
    
    実行結果:
    value is ${val1}
    value is test value
    
    正規表現文字列を構築するには、二重引用符を使って文字列を表現する必要があります。
    def reg1 = "hello \\w*"
    def reg2 = /hello \w*/
    println "hello world" ==~ reg1
    println "hello world" ==~ reg2
    
    実行結果は\です。もちろん、二重斜線文字列を使用すれば、追加の斜線を転送する必要はありません。groovyのシングルクォーテーションマークの文字列は、元の文字の形で存在します。つまり文字列自体がその表示の意味です。シングルクォーテーションマークの元の文字を使って正則マッチングを試みることを知っています。
    def reg1 = 'hello \w*' //     'hello \\w*'      
    println "hello world" ==~ reg1
    
    しかし、最終的には\\のシングルクォーテーションマークを使用しても転送が必要です。よく考えてみると、Grouvyのシングルクォーテーションシーンはパラメータ解析のシーンです。ここでは、斜線を含む正則式文字のマッチング問題です。二つの問題は違っています。したがって、シングルクォーテーションマークを使うかダブルクォーテーションマークを使うかどうかに関わらず、正規表現の場合は、斜線を含む特殊な文字を変換します。変換したくない場合は、斜線を使って二重引用符を置換します。
    PatternとMatch
    Groovyでは、正規表現に関連しているのは依然としてこの二つのJava類です。依然としてJavaに回帰しているこの二つの種類です。JDK 1.8でtrueパッケージの中で最も核心となるのはこの二つの種類です。Patternは正規表現の「モード」を表しています。これは抽象的な概念です。プログラミングの過程で使用している文字列は正規表現であり、それは抽象的なパターンです。実際には、文字列表現の抽象的なパターンをこのクラスにコンパイルすることが必要であり、Grouvyではerrorオペレータを使用して文字列を一つのPatternオブジェクトにコンパイルすると理解できます。このクラスのいくつかの重要な概念と方法を振り返ってみます。
    Pattern.matchesとPattern.matcherjava.util.regexこの関数は、与えられた入力とモードとを一致させるマッチアップマッチオブジェクトを返す。
    def reg = ~/^hello \w*world$/
    def str = "hello world"
    def matcher = reg.matcher(str)
    println matcher.class
    
    出力のタイプは~であるが、上述のMatchオブジェクトは、groovyではMatcher matcher(charsequence input)でシンボルを操作して、ステップで完了することができる。
    def matcher = "hello world"=~/^hello \w*world$/
    println matcher.class
    
    java.util.regex.Matcherこの関数は与えられた正規表現をコンパイルし、与えられた入力にマッチするように試みる。これはJavaにおいて静的な関数であり、与えられた正規表現モードにマッチする文字列があるかどうかを迅速に判断するツールとして理解され、同様にGrovyにも簡単な実施形態がある。
    println "hello world"==~/^hello \w*world$/
    
    実行結果は、=~であり、Grouvyにおいて、2つの操作シンボルstatic boolean matches(string regex, charsequence input)trueを使用して、Matchマッチングオブジェクトの構築が完了し、与えられた文字列が与えられた正規表現モードと一致するかどうかを迅速に検証する機能が見られ得る。
    マッチの中のcapturing group概念
    まずMatchの概念はPatternを説明することです。正規表現を使うのは簡単な検証文字列とモードが一致するかどうかだけではなく、より柔軟で高度な操作が必要であることを理解しています。このMatchオブジェクトが必要です。JavaではPatternの中のmatcher方法を呼び出してこのオブジェクトに戻ります。groovyでは=~操作記号を使うだけでこのようなオブジェクトを作成できます。抽象的には、このオブジェクトは正規表現モードを格納し、与えられた入力文字列のすべての整合に関する情報です。capturing groupという概念は、正規表現の==~に対して導入されたもので、正規表現の括弧はgroupを表し、撮像グループは左から右に計算された括弧から番号付けされています。(括弧の入れ子がある場合、括弧のレベルが高いほど、グループ番号が自然に小さい)の場合、0は式全体を表します。
    (A (BC))
    group 0: (A(BC))
    group 1: (A(BC))
    group 2: (BC)
    
    表式のグループを計算すると、左括弧から左括弧のグループnumberに1を加算します。グループoupを使用して、入力文字列とモードマッチングの部分に対応するグループの位置のサブ文字列をキャプチャします。
    def str = "hello wrold hello"
    def reg = /((el)(l))o/
    def matcher = str=~reg
    def num = 0
    while(matcher.find()){
        println "the ${num} match sub sequenc"
        num++
        groupnum = matcher.groupCount()
        println "group count ${matcher.groupCount()}"
        println "group string ${matcher.group()}"
        println "group 0 string ${matcher.group(0)}"
        for(id in 1..groupnum){
            println "group ${id} string ${matcher.group(id)}"
            println "start index is ${matcher.start(id)} and end index is ${matcher.end(id)}"
        }
    }
    
    実行結果:
    the 0 match sub sequenc
    group count 3
    group string ello
    group 0 string ello
    group 1 string ell
    start index is 1 and end index is 4
    group 2 string el
    start index is 1 and end index is 3
    group 3 string l
    start index is 3 and end index is 4
    the 1 match sub sequenc
    group count 3
    group string ello
    group 0 string ello
    group 1 string ell
    start index is 13 and end index is 16
    group 2 string el
    start index is 13 and end index is 15
    group 3 string l
    start index is 15 and end index is 16
    
    
    重要な点があります。グループの数はグループ0を計算しないことです。グループの数は括弧の数と一致しています。その次はmatcher.group()方法とmatcher.group(0)です。方法は、内容が同じであり、いずれもモードマッチング済みのサブシーケンスであり、パラメータの伝達時には該当番号のTrapグループが取得したサブシーケンスが返されます。もちろん、start endなどを使用して、マッチした文字列(またはTrapグループにマッチした文字列)のオフセット量(endのオフセット量位置は最後の文字の位置に1を加算します)を取得することができます。GDKによる=~方法がgroovyにおいて実現されているので、実際には、インデックスによって獲得グループ内のコンテンツにアクセスすることができ、以下の例が挙げられる。
    def reg = ~/h(el)(lo)/
    def str = 'hello world hello nihao'
    def matcher = str=~reg
    println "first matched substring"
    println matcher[0]
    println matcher[0][0]
    println matcher[0][1]
    println matcher[0][2]
    println "second matched substring"
    println matcher[0]
    println matcher[0][0]
    println matcher[0][1]
    println matcher[0][2]
    
    実行結果
    first matched substring
    [hello, el, lo]
    hello
    el
    lo
    second matched substring
    [hello, el, lo]
    hello
    el
    lo
    
    インデックスによる捕捉文字列へのアクセスの法則により、返されたmatcherオブジェクトの第1次元インデックスは、サブ文字列のインデックスを表し、戻り値がArayListに含まれるコンテンツはすべてのサブ文字列と、獲得グループ番号に対応するサブ文字列とが見られます。これらの等価な動作は、()です。
    マッチリセット
    マッチング器のリセットは、2つの方法getAtおよびmatcher.group(index)に関連し、find()の方法は、どの位置からパターンマッチングを探し始める文字列を指定することができますか?
    def reg = /el/
    def str = "hello world hello"
    def matcher = str=~reg
    while(matcher.find()){
        println matcher.group()
    }
    matcher.find(0) //   matcher            
    //                    ,     find            
    println "reset the matcher"
    while(matcher.find()){
        println matcher.group()
    }
    
    結果:
    el
    el
    reset the matcher
    el
    
    もちろんresetを使ってこのプロセスを完成したほうがいいです。
    def reg = /el/
    def str = "hello world hello"
    def matcher = str=~reg
    while(matcher.find()){
        println matcher.group()
    }
    matcher.reset()
    println "reset the matcher"
    while(matcher.find()){
        println matcher.group()
    }
    
    出力結果:
    el
    el
    reset the matcher
    el
    el
    
    結び目
    以上はGrooovyの正規表現のいくつかの比較的によく見られる知識点であり、主にGrouvyに特有な動作記号を構築して正規表現を使用する過程で依存する各種のオブジェクトを構築しています。本質的にはやはりjavaの正規表現に関連する二つのコアクラス(PatternとMatch)に回帰します。Grouvyは時々「シンタックスキャンディー」のように表現しています。スクリプトでJavaのプログラミングを完了します。その後、Grouvyの正則に関連した多くのコンテンツが続きます。