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

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

Random-JA グループに公開済み
RegEx: 正規表現をマスターするための 20 の短いステップ。パート 1 RegEx: 正規表現をマスターするための 20 の短いステップ。パート 2 正規表現をマスターするための 20 の短いステップ。パート 3 最後のパートでは、主に正規表現の達人が使用するものについて触れます。でも、前のパートの内容は簡単でしたね? つまり、この素材も同じように簡単に扱うことができます。原文はこちら RegEx: 正規表現をマスターするための 20 の短いステップ。 パート 4 - 1 <h2>ステップ 16: キャプチャせずにグループ化する(?:)</h2> RegEx: 正規表現をマスターするための 20 の短いステップ。 パート4-2前のステップの 2 つの例では、実際には必要のないテキストをキャプチャしていました。ファイル サイズ タスクでは、ファイル サイズの最初の桁の前のスペースをキャプチャし、CSV タスクでは、各トークン間のカンマをキャプチャしました。これらの文字をキャプチャする必要はありませんが、正規表現を構成するために使用する必要があります。これらは、キャプチャせずにグループを使用するための理想的なオプションです(?:)。非キャプチャ グループはその名の通りの動作をします。文字をグループ化して正規表現で使用できますが、番号付きグループでキャプチャすることはできません。
パターン: (?:")([^"]+)(?:")文字列: 「これらの引用符内のテキスト」
のみが必要です。
一致:             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
グループ:                 1111111111111111111111111111    
() 正規表現は引用符で囲まれたテキストだけでなく引用符文字自体とも一致しますが、キャプチャ グループは引用符で囲まれたテキストのみをキャプチャしました。なぜこれを行う必要があるのでしょうか? 重要なのは、ほとんどの正規表現エンジンでは、正規表現で定義されたキャプチャ グループからテキストを復元できるということです。キャプチャ グループに含めずに不要な余分な文字をトリミングできれば、後でテキストを解析して操作することが容易になります。前のステップの CSV パーサーをクリーンアップする方法は次のとおりです。
パターン: (?:^|,)\s*(?:\"([^",]*)\"|([^", ]*))
文字列:   a、 " b "、 " cd "、ef、 " gh "、dfgi、、k、 ""、l
一致: ^ ^ ^^^ ^ ^ ^^^ ^^^^ ^ ^
グループ:    2 1 111 2 2 111 2222 2 2    
() <mark>注意すべき点がいくつかあります:</mark> まず、キャプチャ グループを(^|,)非キャプチャ グループに変更したため、コンマはキャプチャされなくなりました(?:^|,)。次に、非キャプチャ グループ内にキャプチャ グループをネストしました。これは、たとえば、文字のグループを特定の順序で表示する必要があるが、それらの文字のサブセットのみを考慮する必要がある場合に便利です。私たちの場合、引用符の中に引用文字とコンマ[^",]*を含める必要がありましたが、実際には引用文字自体は必要なかったので、それらをキャプチャする必要はありませんでした。最後に、上記の例では、文字kと の間に長さゼロの一致も存在することに<mark>注意</mark>してくださいl。引用符は""検索された部分文字列ですが、引用符の間に文字がないため、一致する部分文字列には文字が含まれません (長さはゼロ)。<h3>知識を統合してみませんか? これに役立つ 2 つ半のタスクを次に示します。</h3> 非キャプチャ グループ (およびキャプチャ グループ、文字クラスなど) を使用して、行に適切にフォーマットされたファイル サイズのみをキャプチャする正規表現を記述します。下に :
パターン:
文字列:   6.6KB 1..3KB 12KB 5G 3.3MB KB .6.2TB 9MB。
一致: ^^^^^ ^^^^^ ^^^^^^ ^^^^
グループ:    11111 1111 11111 111    
(解決策) HTML の開始タグは で始まり<で終わります>。HTML の終了タグは、一連の文字で始まり</、 という文字で終わります>。タグ名はこれらの文字の間に含まれます。次のタグ内の名前のみを取得する正規表現を作成できますか? (非キャプチャ グループを使用しなくても、この問題を解決できる場合があります。この 2 つの方法で解決してみてください。グループを使用して 1 回、グループを使用せずに 1 回ずつ解決してください。)
パターン:
文字列:   <p> </span> <div> </kbd> <link>
一致: ^^^ ^^^^^^ ^^^^^ ^^^^^^ ^^^^^^
グループ:    1 1111 111 111 1111    
(非キャプチャ グループを使用した解決策) (非キャプチャ グループを使用しない解決策) <h2>ステップ 17: バックリンク\Nと名前付きキャプチャ グループ</h2> RegEx: 正規表現をマスターするための 20 の短いステップ。 パート 4 - 3冒頭で警告しましたが、通常は正規表現を使用して HTML パーサーを作成しようとします。この最後の例は、ほとんどの正規表現の別の (場合によっては) 便利な機能である後方参照への素晴らしい続きです。バックリンクは、同じテキストを 2 回キャプチャできる繰り返しグループのようなものです。ただし、これらは 1 つの重要な点で異なります。つまり、同じテキストを 1 文字ずつキャプチャするだけです。一方、繰り返しグループを使用すると、次のようなものをキャプチャできます。
パターン: (he(?:[az])+)
文字列:   heyabcdefg hey heyo he yellow heyyyyyyyyy
一致: ^^^^^^^^^^ ^^^ ^^^^ ^^^^^^^^ ^^^ ^^^^^^^^
グループ:    1111111111 111 1111 11111111 11111111111    
() ...この場合、バックリンクはこれのみに一致します。
パターン: (he([az])(\2+))
文字列: heyabcdefg hey heyo he yellow heyyyyyyyyy
一致:                              ^^^^^^^^^^^
グループ:                                 11233333333    
() 同じパターンを繰り返し一致させたい場合は、繰り返しキャプチャ グループが便利です。一方、同じテキストを一致させたい場合は、バックリンクが適しています。たとえば、バックリンクを使用して、一致する開始 HTML タグと終了 HTML タグを検索できます。
パターン: <(\w+)[^>]*>[^<]+<\/\1>
文字列:   <span style="color: red">hey</span>
一致: ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
グループ:    1111    
() <mark>注意してください</mark> これは非常に単純化された例であり、正規表現ベースの HTML パーサーを作成しないことを強くお勧めします。これは非常に複雑な構文なので、おそらく気分が悪くなるでしょう。 名前付きキャプチャ グループはバックリンクと非常に似ているため、ここで簡単に説明します。後方参照と名前付きキャプチャ グループの唯一の違いは、名前付きキャプチャ グループには名前があることです。
パターン: <(?<tag>\w+)[^>]*>[^<]+<\/(?P=tag)></tag>
文字列:   <span style="color: red">ねえ< /span>
一致: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
グループ:    1111    
() (?<name>...) または (?'name'...) 構文 (.NET 互換の正規表現)、またはこの構文 (?P<name>. ..) または (?P'name'...) (Python 互換の正規表現)。両方のバージョンをサポートする PCRE (Perl 互換正規表現) を使用しているため、ここではどちらかを使用できます。(Java 7 は .NET 構文をコピーしましたが、山括弧のバージョンのみをコピーしました。翻訳者注) 名前付きキャプチャ グループを後から正規表現で繰り返すには、\<kname> または \k'name' (.NET) または (? P= 名前) (Python)。繰り返しになりますが、PCRE はこれらのさまざまなオプションをすべてサポートしています。名前付きキャプチャ グループの詳細については、ここで読むことができますが、これは、名前付きキャプチャ グループについて本当に知っておく必要があることのほとんどです。<h3>私たちを助けるためのタスク:</h3> バックリンクを使用して、...うーん...この人の名前を思い出しやすくします。
パターン:
文字列: 「こんにちは、私の名前はジョーです。」[後で] 「あの男の名前は何ですか? ジョー?」
一致:        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^
グループ:                  111    
(解決策) <h2>ステップ 18: 先読みと後読み</h2> RegEx: 正規表現をマスターするための 20 の短いステップ。 パート 4 - 4次に、正規表現の高度な機能のいくつかについて詳しく説明します。ステップ16まではよく使います。ただし、これらの最後のいくつかの手順は、非常に複雑な式に一致させるために正規表現を真剣に使用する人のみを対象としています。つまり、正規表現の達人です。「将来を見据える」と「過去を振り返る」という言葉は非常に複雑に思えるかもしれませんが、実際はそれほど複雑ではありません。これらを使用すると、以前に非キャプチャ グループで行ったことと同様のことを行うことができます。つまり、一致させたい実際のテキストの直前または直後にテキストがあるかどうかを確認します。たとえば、人々が好きなものの名前だけを照合したいとします。ただし、その人がそのことに熱中している場合に限ります (文の最後に感嘆符が付いている場合に限ります)。次のようなことができます。
パターン: (\w+)(?=!)
文字列: 机が好きです。ホッチキスはありがたいです。私はランプが大好きです!
一致数:                                           ^^^^
グループ:                                              1111    
((\w+)) 上記のキャプチャ グループは、通常は文章内のいずれかの単語に一致しますが、単語 Lamp にのみ一致することがわかります。肯定的な先読みとは、(?=!)で終わるシーケンスのみを照合できます!が、実際には感嘆符文字自体は照合できないことを意味します。非キャプチャ グループでは、文字は一致しますが、キャプチャはされないため、これは重要な違いです。先読みと後読みでは、文字を使用して正規表現を作成しますが、文字自体との照合すら行いません。後から正規表現で照合することができます。先読みと後読みには、正の先読み (?=...)、負の先読み (?!...)、正の先読み (?<=...)、および負の先読み (?<!. ..) の 4 つのタイプがあります。 。これらは、その名の通りの動作をします。ポジティブ先読みと後読みにより、正規表現エンジンは、先読み/後読みに含まれるテキストが実際に一致する場合にのみマッチングを継続できます。否定先読みと後読みはその逆で、先読み/後読みに含まれるテキストが一致しない場合にのみ正規表現の一致を許可します。たとえば、操作対象のオブジェクトではなく、メソッド シーケンスのチェーン内でのみメソッド名を照合したいとします。この場合、各メソッド名の前に . を付ける必要があります.。ここでは、単純なルックバックを使用した正規表現が役に立ちます。
パターン: (?<=\.)(\w+)
文字列: myArray. flatMap.aggregate.summarise.print !
一致:         ^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^
グループ:            1111111 111111111 111111111 11111    
(\w+) 上記のテキストでは、単語の前に文字が付いている場合に限り、任意の単語文字のシーケンスと一致します.。非キャプチャ グループを使用しても同様のことを達成できますが、結果は少し複雑になります。
パターン: (?:\.)(\w+)
文字列: myArray . flatMap.aggregate.summarise.print !
一致:        ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^
グループ:            1111111 111111111 111111111 11111    
() 短くても不要な文字と一致します。この例は些細なことのように思えるかもしれませんが、先読みと後読みは正規表現をクリーンアップするのに非常に役立ちます。<h3>終了まで残りわずかです。次の 2 つのタスクにより、これに 1 歩近づくことができます。</h3> 負の後読み (?<!...) を使用すると、負の後読み内に含まれるテキストが一致しない場合にのみ、正規表現エンジンが一致の検索を続行できます。テキストの残りの部分まで表示され、一致するものを見つける必要があります。たとえば、正規表現を使用して、会議に出席している女性の姓のみを照合することができます。これを行うには、その人の姓の前に がつかないようにしたいと考えていますMr.。これの正規表現を書いてもらえますか?(姓は少なくとも 4 文字の長さであると想定できます。)
パターン:
文字列:Mr. ブラウンさん、スミスさん、夫人 ジョーンズデイジー先生、Mr. 緑
一致:                ^^^^^ ^^^^^ ^^^^^
グループ:                   11111 11111 11111    
(解決策) データベースをクリアしていて、パーセンテージを表す情報の列があるとします。残念なことに、数値を [0.0, 1.0] の範囲の小数値として書いた人もいれば、[0.0%, 100.0%] の範囲でパーセンテージを書いた人もいれば、パーセント値を書いた人もいましたが、文字通りのパーセント記号を忘れていました%。負の先読み (?!...) を使用して、パーセンテージである必要があるが桁が欠落している値のみをマークできますか%? これらは厳密に 1.00 より大きい値である必要がありますが、末尾に%. (小数点の前後に 2 桁を超える数字を含めることはできません。) この解決策は非常に難しいことに<mark>注意</mark> してください。私の答えを見ずにこの問題を解決できるのであれば、あなたはすでに正規表現に関する大きなスキルを持っていることになります。
パターン:
文字列: 0.32 100.00 5.6 0.27 98% 12.2% 1.01 0.99% 0.99 13.13 1.10
一致:      ^^^^^^ ^^^ ^^^^ ^^^^^ ^^^^
グループ:         111111 111 1111 11111 1111    
(解決策) <h2>ステップ 19: 正規表現の条件</h2> RegEx: 正規表現をマスターするための 20 の短いステップ。 パート 4 ~ 5これで、ほとんどの人が正規表現を使用しなくなります。単純な正規表現のユースケースのおそらく 95% をカバーしました。ステップ 19 と 20 で実行されるすべての作業は、通常、awk や sed などのよりフル機能のテキスト操作言語 (または汎用プログラミング言語) によって実行されます。とはいえ、正規表現で実際に何ができるかを理解するために先に進みましょう。正規表現はチューリング完全ではありませんが、一部の正規表現エンジンは、完全なプログラミング言語と非常によく似た機能を提供します。そのような機能の 1 つは「条件」です。正規表現の条件文では、if-then-else ステートメントが使用できます。このステートメントでは、選択された分岐が、前のステップで学習した「前方参照」または「後方参照」のいずれかによって決定されます。たとえば、日付のリスト内の有効なエントリのみを照合したい場合があります。
パターン: (?<=Feb )([1-2][0-9])|(?<=Mar )([1-2][0-9]|3[0-1])
文字列: 勤務日: 2月28、 2月29、 2月 30 、 3月30、 3月31 
試合:                   ^^ ^^ ^^ ^^
グループ:                      11 11 22 22    
() <mark>注意</mark> 上記のグループは月ごとのインデックスも作成されます。12 か月すべての正規表現を作成し、有効な日付のみを取得して、その日付を月ごとにインデックス付けされたグループに結合することができます。上記では、「Feb」が数字の前にある場合 (2 番目のグループについても同様)、最初のグループで一致するもののみを検索する、ある種の if 構造を使用しています。しかし、2 月にのみ特別な処理を使用したい場合はどうなるでしょうか? 「番号の前に「2月」がある場合はこれを行い、そうでない場合は別のことを行う」のようなものです。条件文がどのように実行されるかは次のとおりです。
パターン: (?(?<=Feb )([1-2][0-9])|([1-2][0-9]|3[0-1]))
文字列: 作業日: 2 月28 日、 2 月29 日、2 月 30 日、3 月30 日、3 月31 日 
の試合:                   ^^ ^^ ^^ ^^
グループ:                      11 11 22 22    
() if-then-else 構造は (?(If)then|else) のようになります。(if) は「look forward」または「look back」に置き換えられます。上記の例では、(if) は と書かれています(?<=Feb)。29 より大きい日付が一致したことがわかりますが、それは「2 月」以降でない場合に限られます。条件式で後読みを使用すると、一致の前に何らかのテキストが来るようにする場合に便利です。肯定的な先読み条件は、条件自体がどのテキストとも一致しないため、混乱を招く可能性があります。したがって、if 条件に値を持たせたい場合は、以下のような先読みに相当する必要があります。
パターン: (?(?=exact)exact|else)wo
文字列: 正確な else正確な 2 つ 
一致するもの:            ^^^^^^^ ^^^^^^
() これは、肯定的な先読み条件文が役に立たないことを意味します。そのテキストが先頭にあるかどうかを確認し、先頭にある場合に従う一致パターンを提供します。ここでは条件式はまったく役に立ちません。上記をより単純な正規表現に置き換えることもできます。
パターン: (?:exact|else)wo
文字列: 正確な else正確な 2 つ、その他の 
一致:            ^^^^^^^ ^^^^^^
() したがって、条件式の経験則は、テスト、テスト、そして再度テストです。そうしないと、明白だと思っているソリューションが、最もエキサイティングで予想外の方法で失敗することになります :) <h3>ここで、最後の 20 番目のステップから区切るタスクの最後のブロックに進みます。</h3> 次の正規表現を書きます。負の先読み条件式を使用して、次の単語が大文字で始まるかどうかをテストします。その場合は、大文字を 1 つだけ取得し、次に小文字を取得します。そうでない場合は、単語の文字を取得します。
パターン:
文字列:   Jones Smith 9sfjn Hobbes 23r4tgr9h CSV Csv vVv
一致: ^^^^^ ^^^^^ ^^^^^ ^^^^^^ ^^^^^^^^^ ^^^ ^^^
グループ:    22222 22222 11111 222222 111111111 222 111    
(解決策owns)先頭にテキストがない場合にのみテキストをキャプチャしclouds先頭にテキストがある場合にのみテキストをキャプチャする負の後読み条件式を作成しますcl。(少し不自然な例ですが、何ができるでしょうか...)
パターン:
文字列: これらのピエロはいくつかのクラウド所有しています。ウード。
一致:              ^^^^ ^^^^   
(解決策) <h2>ステップ 20: 再帰とさらなる学習</h2> RegEx: 正規表現をマスターするための 20 の短いステップ。 パート 4 ~ 6実際、どんなトピックでも 20 ステップの入門に詰め込めることがたくさんあり、正規表現も例外ではありません。正規表現のさまざまな実装と標準がインターネット上で見つかります。さらに詳しく知りたい場合は、素晴らしいサイトRegularexpressions.infoをチェックすることをお勧めします。これは素晴らしいリファレンスであり、私はそこから正規表現について多くのことを学びました。作品のテストと公開にはregex101.comと同様に、これを強くお勧めします。この最後のステップでは、正規表現、つまり再帰式の書き方についてもう少し詳しく説明します。単純な再帰は非常に単純ですが、正規表現のコンテキストでそれが何を意味するかを考えてみましょう。正規表現での単純な再帰の構文は(?R)?次のように記述されます。ただし、もちろん、この構文は式自体の中に出現する必要があります。ここで行うことは、式をその中に任意の回数ネストすることです。例えば:
パターン: (hey(?R)?oh)
文字列:   heyoh heyyoh heyheyohoh hey oh heyhey hey heyheyohoh 
一致: ^^^^^ ^^^^^^^^^^ ^^^^^^^^^^
グループ:    11111 1111111111 1111111111    
() ネストされた式はオプション ( (?R)follow ?) であるため、最も単純な一致は再帰を完全に無視することです。したがって、hey、およびその後はoh( heyoh) と一致します。これより複雑な式に一致するには、式内のシーケンスを挿入した位置で、一致する部分文字列がその内部にネストされている必要があります(?R)。言い換えれば、ヘイヘイヨホホやヘイヘイヘヨホホなどを見つけることができます。これらのネストされた式の優れた点の 1 つは、後方参照や名前付きキャプチャ グループとは異なり、以前に一致した正確なテキストを 1 文字ずつ制限しないことです。例えば:
パターン: ([Hh][Ee][Yy](?R)?oh)
文字列:   heyoh heyyoh hEyHeYohoh hey oh heyhey hEyHeYHEyohohoh 
一致: ^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^
グループ:    11111 1111111111 111111111111111    
() 正規表現エンジンが文字通り正規表現を任意の回数コピーして自身に貼り付けると想像できます。もちろん、これは、期待どおりに動作しない場合があることを意味します。
パターン: ((?:\(\*)[^*)]*(?R)?(?:\*\)))
文字列: (* コメント(* ネストされた *)ではありません *)
一致:            ^^^^^^^^^^^^
グループ:               111111111111    
() この正規表現がなぜ外側のコメントではなく、ネストされたコメントのみをキャプチャしたのか分かりますか? 1 つ確かなことは、複雑な正規表現を作成するときは、必ずテストして、想定どおりに動作することを確認することです。正規表現の道を巡るこの高速ラリーは終わりを迎えました。この旅を楽しんでいただければ幸いです。さて、最後に、最初に約束したように、この資料をさらに詳しく調べるために役立ついくつかのリンクをここに残しておきます。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION