JavaRush /Java Blog /Random-JA /RegEx: 正規表現をマスターするための 20 の短いステップ。パート 3
Artur
レベル 40
Tallinn

RegEx: 正規表現をマスターするための 20 の短いステップ。パート 3

Random-JA グループに公開済み
RegEx: 正規表現をマスターするための 20 の短いステップ。パート 1. RegEx: 正規表現をマスターするための 20 の短いステップ。パート 2: このパートでは、もう少し複雑な内容に進みます。しかし、以前と同様に、それらをマスターすることは難しくありません。繰り返しますが、RegEx は実際は一見したよりも簡単で、これをマスターして実際に使い始めるのにロケット科学者である必要はありません。この記事の英語原文はこちら です正規表現をマスターするための 20 の短いステップ。 パート 3 - 1

ステップ 11:()キャプチャ グループとしての括弧

正規表現をマスターするための 20 の短いステップ。 パート 3 - 2最後の問題では、さまざまな種類の整数値と浮動小数点 (ドット) 数値を探しました。しかし、すべてが 1 つの大きな正規表現に取り込まれていたため、正規表現エンジンはこれら 2 種類の値を区別しませんでした。ミニパターンを括弧で囲むと、正規表現エンジンにさまざまなタイプの一致を区別するように指示できます。
パターン: ([AZ])|([az])
文字列:  ボリビアの現在の大統領はエボ・モラレスです。
一致: ^^^ ^^^^^^^ ^^^^^^^^^ ^^ ^^^^^^^ ^^ ^^^ ^^^^^^^
グループ:    122 2222222 122222222 22 1222222 22 122 1222222  
() 上記の正規表現は、1 から始まるインデックスが付けられた 2 つのキャプチャ グループを定義します。最初のキャプチャ グループは任意の 1 つの大文字に一致し、2 番目のキャプチャ グループは任意の 1 つの小文字に一致します。「or」記号|と括弧を()キャプチャ グループとして使用することで、複数の種類の文字列に一致する単一の正規表現を定義できます。これを記事の前の部分のlong/float検索正規表現に適用すると、正規表現エンジンは適切なグループ内の対応する一致をキャプチャします。部分文字列がどのグループに一致するかをチェックすることで、それが float 値であるか long 値であるかをすぐに判断できます。
パターン: (\d*\.\d+[fF]|\d+\.\d*[fF]|\d+[fF])|(\d+[lL])
文字列:   42L 12 x 3.4f 6l 3.3 0F LF .2F0。
一致: ^^^ ^^^^ ^^ ^^ ^^^
グループ:    222 1111 22 11 111  
() この正規表現は非常に複雑なので、よりよく理解するために、分解して次の各パターンを見てみましょう。
( // 任意の「float」部分文字列と一致します
  \d*\.\d+[fF]
  |
  \d+\.\d*[fF]
  |
  \d+[fF]
)
| //または
( // 任意の「長い」部分文字列に一致します
  \d+[lL]
)
括弧内の記号|とキャプチャ グループを使用すると、()さまざまなタイプの部分文字列を照合できます。この場合、浮動小数点数「float」または長整数「long」のいずれかに一致します。
(
  \d*\.\d+[fF] // 小数点以下 1 桁以上
  |
  \d+\.\d*[fF] // 小数点の左側に 1 桁以上
  |
  \d+[fF] // ドットなし、1 桁以上のみ
)
|
(
  \d+[lL] // ドットなし、1 桁以上のみ
)
「float」キャプチャ グループには、小数点の右側に少なくとも 1 桁の数値、小数点の左側に少なくとも 1 桁の数値、および小数点のない数値の 3 つのオプションがあります。末尾に文字「f」または「F」が追加されている限り、それらはどれも「float」です。「長い」キャプチャ グループ内では、オプションが 1 つだけあります。1 つ以上の数字の後に文字「l」または「L」が続く必要があります。正規表現エンジンは、指定された文字列内でこれらの部分文字列を検索し、適切なキャプチャ グループにインデックスを付けます。 注記「l」、「L」、「f」、「F」のいずれも追加されていない数字は一致しないということです。これらの数値はどのように分類されるべきでしょうか? 小数点がある場合、Java 言語のデフォルトは「double」です。それ以外の場合は、「int」でなければなりません。

いくつかのパズルで学んだことを定着させましょう。

上記の正規表現にさらに 2 つのキャプチャ グループを追加して、double または int の数値も分類できるようにします。(これも難しい質問です。時間がかかっても落胆しないでください。最後の手段として私の解決策を参照してください。)
パターン:
文字列:   42L 12 x 3.4f 6l 3.3 0F LF .2F 0.
一致: ^^^ ^^ ^^^^ ^^ ^^^ ^^ ^^^
グループ:    333 44 1111 33 222 11 111 22
(解決策) 次の問題はもう少し簡単です。括弧で囲まれたキャプチャ グループ()、「または」記号、|および文字範囲を使用して、次の年齢を並べ替えます: 「米国で飲酒が合法」。(>= 21) および「米国では飲酒が許可されていない」(<21):
パターン:
文字列:   7 10 17 18 19 20 21 22 23 24 30 40 100 120
一致: ^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^ ^^^
グループ:    2 22 22 22 22 22 11 11 11 11 11 11 111 111 
解決

ステップ 12: まず、より具体的な一致を特定する

正規表現をマスターするための 20 の短いステップ。 パート 3 - 3「合法的飲酒者」を 2 番目ではなく最初の捕捉グループとして定義しようとした場合、最後のタスクで問題が発生した可能性があります。その理由を理解するために、別の例を見てみましょう。4 文字未満の姓と 4 文字以上の姓を別々に記録したいとします。最初のキャプチャ グループに短い名前を付けて、何が起こるかを見てみましょう。
パターン: ([AZ][az]?[az]?)|([AZ][az][az][az]+)
文字列:   Kim Job s Xu Clo yd Moh r Ngo Rock k。
一致: ^^^ ^^^ ^^ ^^^ ^^^ ^^^ ^^^
グループ:    111 111 11 111 111 111 111   
() デフォルトでは、ほとんどの正規表現エンジンは、これまで見てきた基本文字に対して貪欲一致を使用します。これは、正規表現エンジンが、提供された正規表現のできるだけ早い段階で定義された最長のグループを取得することを意味します。したがって、上記の 2 番目のグループは、たとえば「Jobs」や「Cloyd」などの名前のさらに多くの文字をキャプチャできますが、これらの名前の最初の 3 文字は最初のキャプチャ グループによってすでにキャプチャされているため、2 番目のグループで再度キャプチャすることはできません。 。次に、小さな修正を加えてみましょう。キャプチャ グループの順序を変更し、より具体的な (長い) グループを最初に配置します。
パターン: ([AZ][az][az][az]+)|([AZ][az]?[az]?)
文字列:   Kim Jobs Xu Cloyd Mohr Ngo Rock。
一致: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^
グループ:    222 1111 22 11111 1111 222 1111    

タスク...今回は 1 つだけです:)

「より具体的な」パターンは、ほとんどの場合「より長い」ことを意味します。2 種類の「単語」を検索したいとします。最初は母音で始まる単語 (具体的には)、次に母音で始まらない単語(その他の単語) です。これら 2 つのグループに一致する文字列を取得して識別する正規表現を作成してみてください。(以下のグループには番号ではなく文字が付いています。どのグループが最初のグループに対応し、どのグループが 2 番目のグループに対応するかを決定する必要があります。)
パターン:
文字列:   pds6f uub 24r2gp ewqrty l ui_op
一致: ^^^^^ ^^^ ^^^^^^ ^^^^^^ ^ ^^^^^
グループ:    NNNNN VVV NNNNNN VVVVVV N VVVVV
(解決策) 一般に、正規表現が正確であればあるほど、結果的に長くなります。そして、精度が高ければ高いほど、不要なものをキャプチャする可能性が低くなります。したがって、怖く見えるかもしれませんが、長い正規表現 ~= より良い正規表現です。残念ながら

{}ステップ 13:特定の回数繰り返すための中括弧

正規表現をマスターするための 20 の短いステップ。 パート 3 ~ 4前のステップの姓の例では、1 つのパターンでほぼ繰り返される 2 つのグループがありました。
パターン: ([AZ][az][az][az]+)|([AZ][az]?[az]?)
文字列:   Kim Jobs Xu Cloyd Mohr Ngo Rock。
一致: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^
グループ:    222 1111 22 11111 1111 222 1111    
最初のグループでは、4 文字以上の姓が必要でした。2 番目のグループは、3 文字以下の姓をキャプチャする必要がありました。[a-z]これらのグループを何度も繰り返すより簡単にこれを書く方法はあるでしょうか? これに中かっこを使用すると存在します{}。中括弧を使用すると、{}前の文字またはキャプチャ グループの一致の最小数と (オプションで) 最大数を指定できます。使用例は 3 つあります{}
{X} // X 回正確に一致します
{X,} // X 回以上一致
{X,Y} // >= X かつ <= Y 回一致
これら 3 つの異なる構文の例を次に示します。
パターン: [az]{11}
文字列:  フムフムヌクウヌクアプアア。
一致: ^^^^^^^^^^^   
パターン: [az]{18,}
文字列:  フムフムヌクヌクアプア'a.
一致: ^^^^^^^^^^^^^^^^^^^^^    
パターン: [az]{11,18}
文字列:  フムフムヌクヌクアプ ウアア。
一致: ^^^^^^^^^^^^^^^^^^    
() 上記の例ではいくつか注意すべき点があります。注記:。まず、{X} 表記を使用すると、前の文字またはグループがその回数 (X) 回正確に一致します。(最初の例に示すように) パターンに一致する可能性のある「単語」内に (数字 X よりも) 多くの文字がある場合、それらは一致には含まれません。文字数が X 未満の場合、完全一致は失敗します (最初の例で 11 を 99 に変更してみてください)。第 2 に、表記法 {X,} と {X,Y} は貪欲です。指定された正規表現を満たしながら、できるだけ多くの文字と一致しようとします。{3,7} を指定すると、3 ~ 7 文字が一致し、次の 7 文字が有効であれば、7 文字すべてが一致します。{1,} を指定し、次の 14,000 文字がすべて一致する場合、それらの 14,000 文字すべてが対応する文字列に含まれます。この知識を使って上記の式を書き直すにはどうすればよいでしょうか? [a-z]最も簡単な改善は、隣接するグループを に置き換えることです[a-z]{N}。ここで、N はそれに応じて選択されます。
パターン: ([AZ][az]{2}[az]+)|([AZ][az]?[az]?)  
...しかし、それは状況をあまり良くしません。最初のキャプチャ グループを見てください。[a-z]{2}(正確に 2 つの小文字に一致する) の後に[a-z]+(1 つ以上の小文字に一致する) があります。中括弧を使用して 3 つ以上の小文字を要求することで、これを簡略化できます。
パターン: ([AZ][az]{3,})|([AZ][az]?[az]?) 
2 番目のキャプチャ グループは異なります。これらの姓には 3 文字以内が必要です。つまり、上限はありますが、下限は 0 です。
パターン: ([AZ][az]{3,})|([AZ][az]{0,2}) 
正規表現を使用すると特異性が常に向上するため、ここで終了するのが賢明ですが、これら 2 つの文字範囲 ([AZ]と) が隣り合っていると、ほとんど「単語文字」クラス( )[az]のように見えることに気づかずにはいられません。。データに適切にフォーマットされた姓のみが含まれていると確信できる場合は、正規表現を簡素化し、次のように単純に書くことができます。 \w[A-Za-z0-9_]
パターン: (\w{4,})|(\w{1,3}) 
最初のグループは 4 つ以上の「単語文字」 ( ) のシーケンスをキャプチャし[A-Za-z0-9_]、2 番目のグループは 1 ~ 3 つの「単語文字」 (両端を含む) のシーケンスをキャプチャします。これはうまくいきますか?
パターン: (\w{4,})|(\w{1,3})
文字列:   Kim Jobs Xu Cloyd Mohr Ngo Rock。
一致: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^
グループ:    222 1111 22 11111 1111 222 1111    
() うまくいきました!このアプローチはどうでしょうか?これは、前の例よりもはるかにクリーンです。最初のキャプチャ グループは 4 文字以上のすべての姓と一致するため、2 番目のキャプチャ グループを単に に変更することもできます\w+。これにより、残りのすべての姓 (1、2、または 3 文字) をキャプチャできるようになります。
パターン: (\w{4,})|(\w+)
文字列:   Kim Jobs Xu Cloyd Mohr Ngo Rock。
一致: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^
グループ:    222 1111 22 11111 1111 222 1111    

脳がこれを学習できるようにして、次の 2 つの問題を解決しましょう。

中括弧を使用して、{}ステップ 7 の社会保障番号検索の正規表現を書き換えます。
パターン:
文字列: 113-25=1902 182-82-0192 H23-_3-9982 1I1-O0-E38B
一致:              ^^^^^^^^^^^
(解決策) Web サイトのパスワード強度チェッカーでは、ユーザーのパスワードが 6 ~ 12 文字であることが要求されているとします。以下のリストにある無効なパスワードにフラグを付ける正規表現を記述します。各パスワードは()簡単に照合できるようにかっこで囲まれているため、正規表現の先頭と末尾がリテラル文字()記号文字であることを確認してください。ヒント: または類似のパスワードではリテラル括弧を禁止してください[^()]。そうしないと、文字列全体が一致することになります。
パターン:
文字列:   (12345) (私のパスワード) (Xanadu.2112) (su_do) (OfSalesmen!)
一致: ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^  
解決

ステップ 14:\b幅ゼロの境界線シンボル

正規表現をマスターするための 20 の短いステップ。 パート 3 ~ 5最後の作業はかなり難しかったです。""しかし、パスワードを括弧ではなく引用符で囲んで、もう少し複雑にしてみたらどうなるでしょうか()? すべての括弧文字を引用符文字に置き換えるだけで、同様の解決策を作成できますか?
パターン: \"[^"]{0.5}\"|\"[^"]+\s[^"]*\"
文字列:   "12345" "私のパスワード" "Xanadu.2112 " " su_do" " のセールスマン! 」
一致: ^^^^^^^ ^^^^^^^^^^^^^ ^^^ ^^^  
() あまり印象に残りませんでした。その理由はもうわかりましたか? 問題は、ここで間違ったパスワードを探していることです。「Xanadu.2112」は適切なパスワードであるため、このシーケンスにスペースやリテラル文字が含まれていないことを正規表現が認識すると、右側のパスワードを修飾する"文字の直前が返されます。"("を使用してパスワード内で文字が見つからないこと[^"]を指定したためです。) 正規表現エンジンは、それらの文字が特定の正規表現に一致しないことを確認すると、中断した場所 (文字があった場所) で再実行されます。"これにより、制限が課されます。右はザナドゥ.2112」。そこから、スペース文字が 1 つ、そして別の文字が見えます"。彼にとって、これは間違ったパスワードです。基本的に、彼はこのシーケンスを見つけて" "先に進みます。これは私たちが取得したいものではありません...パスワードの最初の文字をスペースにしないように指定できれば素晴らしいのですが。これを行う方法はありますか? (もうお気づきかと思いますが、私のすべての修辞的質問に対する答えは「はい」です。) はい! そんな方法があるんだ!多くの正規表現エンジンは、「単語境界」などのエスケープ シーケンスを提供します\b。「単語境界」は\b、奇妙なことに単語境界と一致するゼロ幅のエスケープ シーケンスです。「単語」というときは、クラス内の文字のシーケンスまたは のいずれかを意味することに注意して\wください[A-Za-z0-9_]。単語境界一致とは、シーケンスの直前または直後の文字が単語文字で\bある必要があることを意味します。неただし、照合する場合、キャプチャされた部分文字列にはこの文字は含まれません。これはゼロ幅です。これがどのように機能するかを確認するために、小さな例を見てみましょう。
パターン: \b[^ ]+\b
文字列:  まだ金が欲しいリボウスキー。
一致します: ^^ ^^^^^ ^^^^ ^^ ^^^^^ ^^^^^^^^  
() シーケンスは、[^ ]リテラルのスペース文字ではない任意の文字と一致する必要があります。,では、なぜこれが、money の後のコンマやLebowski の後のピリオドと一致しないのでしょうか.?これは、コンマ,とピリオドが.単語文字ではないため、単語文字と単語以外の文字の間に境界が作成されるためです。これらは、単語のy終わりの間に表示されます。 「money」という単語と,それに続くコンマ、および「 iLebowski」という単語と.それに続くピリオド (ピリオド) の間。正規表現は、これらの単語の境界で一致します (ただし、単語を定義するだけの非単語文字では一致しません)。\bしかし、テンプレートに 一貫性を含めないとどうなるでしょうか?
パターン: [^ ]+
文字列:  まだ金が欲しい、リボウスキー。
一致します: ^^ ^^^^^ ^^^^ ^^ ^^^^^^ ^^^^^^^^^  
() はい、これらの句読点も見つかりました。次に、単語の境界を使用して、引用符で囲まれたパスワードの正規表現を修正しましょう。
パターン: \"\b[^"]{0.5}\b\"|\"\b[^"]+\s[^"]*\b\"
文字列:   "12345" "私のパスワード" "ザナドゥ。 2112" "su_do" "セールスマンの!"
一致: ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^  
() 単語の境界を引用符 ("\b ... \b") で囲むことにより、一致するパスワードの最初と最後の文字が "単語文字" である必要があることを事実上示します。したがって、これはここではうまく機能しますが、ユーザーのパスワードの最初または最後の文字が単語文字でない場合は、うまく機能しません。
パターン: \"\b[^"]{0.5}\b\"|\"\b[^"]+\s[^"]*\b\"
文字列: "次のパスワードは短すぎます" "C++"
一致します:   
() 2 番目のパスワードが明らかに短すぎるにもかかわらず、「無効」としてマークされない様子を確認してください。あなたはきっとそうでしょう注意深いシーケンスの場合は\b、文字間の境界にのみ一致し、文字\w間の境界には一致しないためです\w。上の例では、パスワードに許可されていない文字を許可しているため\w、パスワードの最初/最後の文字の間の境界\が単語境界であることは保証されません\b

このステップを完了するには、単純な問題を 1 つだけ解決します。

単語の境界は、特定の文字シーケンスと一致させたいが、単語の先頭または末尾 (または単独で) のみに出現することを確認したい場合に、構文強調表示エンジンで役立ちます。構文の強調表示を作成していて、var という単語を強調表示したいとします。ただし、それが単独で出現する場合にのみ (単語内の他の文字に触れることはありません)。これの正規表現を書いてもらえますか?もちろんできます、とても簡単な作業です ;)
パターン:
文字列:   var varx _var ( var j) barvarcar * var var -> { var }
一致: ^^^ ^^^ ^^^ ^^^ ^^^  
解決

ステップ 15: 「キャレット」を^「行の先頭」として、ドル記号を$「行の末尾」として指定します。

正規表現をマスターするための 20 の短いステップ。 パート 3 ~ 6\b正規表現で使用できる特殊なゼロ幅シーケンスは、単語境界シーケンス(この記事の前の部分の最後のステップから) だけではありません。最も一般的な 2 つは、「キャレット」^(行頭) とドル記号$(行末) です。正規表現にこれらのいずれかを含めるということは、ソース文字列の先頭または末尾に一致が出現する必要があることを意味します。
パターン: ^start|end$
文字列:   start end start end start end start end
一致: ^^^^^ ^^^  
() 文字列に改行が含まれている場合、^start任意の行の先頭のシーケンス「start」と一致し、end$任意の行の末尾のシーケンス「end」と一致します (ただし、これをここで示すのは困難です)。これらの記号は、区切り文字を含むデータを操作する場合に特に便利です。^「行頭」を使用してステップ 9 の「ファイル サイズ」の問題に戻りましょう。この例では、ファイル サイズはスペース「 」で区切られています。したがって、各ファイル サイズは数字で始まり、その前にスペース文字または行の先頭が続くようにします。
パターン: (^| )(\d+|\d+\.\d+)[KMGT]B
文字列:   6.6KB 1..3KB 12KB 5G 3.3MB KB .6.2TB 9MB。
一致: ^^^^^ ^^^^^ ^^^^^^ ^^^^
グループ:    222 122 1222 12    
() もうゴールに近づいています!ただし、まだ小さな問題が 1 つあることに気づくかもしれません。それは、有効なファイル サイズの前にスペース文字を一致させているということです。これで、正規表現エンジンがこのキャプチャ グループ (1) を見つけたときに単純に無視することも、次のステップで説明する非キャプチャ グループを使用することもできます。

それまでに、トーンに関するさらに 2 つの問題を解決しましょう。

前のステップの構文強調表示の例を続けると、一部の構文強調表示では末尾のスペース、つまり空白以外の文字と行末の間にあるスペースをマークします。末尾のスペースのみを強調表示する正規表現を作成できますか?
パターン:
文字列: myvec <- c(1, 2, 3, 4, 5)  
一致:                          ^^^^^^^  
(解決策) 単純なカンマ区切り値 (CSV) パーサーは、カンマで区切られた「トークン」を検索します。一般に、スペースは引用符で囲まれない限り意味を持ちません""カンマ間のトークンに一致するが、引用符の間にない空白は無視する (キャプチャしない) 単純な CSV 解析正規表現を作成します。
パターン:
文字列:   a、"b"、"c d"、e、f、"g h"、dfgi、、k、""、l の
一致: ^^ ^^^^ ^^^^^^^^^^^^^^ ^^^ ^^^^^^ ^^ ^^^ ^
グループ:    21 2221 2222212121 222221 222211 21 221 2    
(解決策) RegEx: 正規表現をマスターするための 20 の短いステップ。パート4。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION