正規表現、初心者向けのガイド。 パート1

正規表現(RV)は、基本的にPythonに組み込まれ、reモジュールでアクセス可能な小さなプログラミング言語です。 これを使用して、チェックする可能性のある一連の行のルールを指定します。 このセットには、英語のフレーズ、メールアドレス、TeXコマンドなどを含めることができます。 PBを使用すると、「この行はパターンに一致しますか?」、「パターンはこの行にどこかに一致しますか?」などの質問をすることができます。 正規表現を使用して、文字列を変更したり、さまざまな方法で文字列を分割したりすることもできます。

正規表現パターンは一連のバイトコードにコンパイルされ、Cで記述された対応するエンジンによって実行されます。高度な使用のために、エンジンがこの正規表現を実行する方法に注意を払い、機能するバイトコードを取得するように記述することが重要な場合がありますより速く。 このドキュメントでは、エンジンの内部詳細を十分に理解する必要があるため、最適化は考慮されていません。

正規表現言語は比較的小さく制限されているため、正規表現を使用してすべての可能な文字列処理タスクを実行できるわけではありません。 正規表現で実行できるタスクもありますが、式は複雑すぎます。 これらの場合、開発された正規表現よりも実行が遅くなる場合でも、通常のPythonコードを記述する方が良いかもしれませんが、より明確になります。

シンプルなパターン


最も単純な正規表現を調べることから始めます。 正規表現は文字列の処理に使用されるため、最も一般的なタスクである文字のマッチングから始めます。

正規表現(決定論的および非決定論的有限状態マシン)の技術面の詳細な説明については、コンパイラーの作成に関するほとんどすべてのテキストを参照できます。

キャラクターマッチング

ほとんどの文字と記号は自分自身に対応しています。 たとえば、正規表現testは文字列testと完全に一致します(大文字と小文字を区別しないモードをオンにすると、この正規表現がTestまたはTESTに一致しますが、これについては後で詳しく説明します)。

この規則には例外があります。 一部の文字は特殊なメタ文字であり、自分自身に対応していません。 代わりに、何らかの異常なものを見つけるか、意味を繰り返したり変更したりして、正規表現の他の部分に影響を与える必要があることを示します。 このチュートリアルのほとんどは、さまざまなメタキャラクターとその機能について説明することに専念しています。

メタキャラクターの完全なリストは次のとおりです。 これらの意味については、このHOWTOの残りの部分で説明します。

. ^ $ * + ? { [ ] \ | ( )

私たちが考える最初のメタキャラクターは[]です。 これらは、文字クラスを決定するために使用されます。これは、一致を探している文字のセットです。 文字は個別にリストすることも、 '-'で区切られた最初と最後の文字で示される文字の範囲としてリストすることもできます。 たとえば、 [abc]は文字a, bまたはcいずれかに一致します。 範囲を使用して同じ文字セットを指定する式[ac]と同じです。 小文字のみを照合する場合、PBは[az]ようになります。

メタキャラクターはクラス内ではアクティブではありません。 たとえば、 [akm$]は、文字'a', 'k', 'm'または'$'いずれかに一致します。 '$'記号は通常メタキャラクターです(上記の文字のリストからわかるように)が、キャラクタークラス内では特殊な性質を失います。

このクラス外の文字に一致させるために、文字'^'クラスの先頭に追加されます。 たとえば、式[^5] 「5」以外の任意の文字に一致します。

おそらく最も重要なのは、バックスラッシュのメタキャラクターです。 Python文字列リテラルと同様に、バックスラッシュの後には、さまざまな特別なシーケンスを表すさまざまな文字を続けることができます。 また、メタ文字をエスケープして、テンプレートで使用できるようにするためにも使用されます。 たとえば、メタ文字としての特別な役割を奪うために一致[または\を見つける必要がある場合、その前にバックスラッシュを置く必要があります: \[または\\

'\'始まる特別なシーケンスの一部は、数字のセット、文字のセット、またはスペース、タブなどではないすべてのセット(空白)など、多くの場合に便利な事前定義された文字セットを表します。 次の定義済みシーケンスは、それらのサブセットです。 クラスシーケンスの完全なリストおよびUnicode文字列の拡張クラス定義については、 正規表現構文の最後の部分を参照してください。

\d
任意の数字と一致します。 同等のクラス[0-9]
\D
数字以外の文字に一致します。 同等のクラス[^0-9]
\s
空白文字に一致します。 [ \t\n\r\f\v]と同等です。
\S
空白以外の文字に一致します。 [^ \t\n\r\f\v]と同等です。
\w
任意の文字または数字と一致します。 [a-zA-Z0-9_]と同等。
\W
それどころか; [^a-zA-Z0-9_]と同等。

これらのシーケンスは、文字クラスに含めることができます。 たとえば、[\ s、。]は、任意の空白記号、コンマ、またはドットに一致する文字クラスです。

このセクションの最後のメタキャラクターは'.' 。 改行文字を除くすべての文字に一致しますが、このセットに含まれる代替モード( re.DOTALL )があります。 '.' 「任意の文字」と一致させたい場所でよく使用されます。

重複するもの

異なる文字セットに一致する機能は、正規表現で最初に実行できることであり、文字列メソッドでは常に実行できるとは限りません。 ただし、これが唯一の追加の機会である場合、それらはそれほど面白くないでしょう。 別の可能性は、正規表現の一部を繰り返す回数を指定できることです。

繰り返す最初のメタキャラクターは*です。 これは、単一の比較ではなく、前の文字が0回以上一致することを示します。

たとえば、 ca*tct (0文字a )、cat(1文字a )、caaat(3文字a )などに一致します。 正規表現エンジンには、Cのint型のサイズに起因するさまざまな内部制限があり、20億文字を超える「a」との一致を防ぎます。 (これが必要ないことを願っています)。

*などの繰り返しは貪欲と呼ばれます。 エンジンは可能な限り何度もそれを繰り返します。 テンプレートの次の部分が一致しない場合、エンジンは戻って、キャラクターを数回繰り返して再試行します。

例の段階的な調査により、説明がより明確になります。 式a[bcd]*b見てみましょう。 これは、文字'a' 、クラス[bcd]ゼロ個以上の文字、最後に最終文字'b'に一致します。 ここで、この正規表現を文字列abcbd一致させることを想像してください。 比較は段階的に行われます。

1. a -'a'は正規表現に一致します
2. abcbdエンジンは[bcd]*をできるだけ多くの文字、つまり行の最後に一致させます(すべての文字が角括弧[]のクラスに対応するため)
3.失敗-エンジンは正規表現の最後の文字-文字bに一致しようとしますが、現在の位置はすでに文字のない行の終わりにあるため、失敗します。
4. [bcd]*[bcd]*との比較を1文字減らしました[bcd]*
5.失敗-再びbを見つけようとしますが、最終的にはd
6. [bcd]*もう一度戻る、今[bcd]* bcのみ
7. abcb再び、正規表現の最後の文字を探します-b。 今、彼は本当に正しい位置にあり、私たちは成功しています

それで、PBの終わりに達し、それとの比較はabcbを与えました。 この例は、エンジンが可能な限り最初に取得する方法を示し、一致するものが見つからない場合は、正規表現の残りの部分で何度も繰り返し動作します。 彼は[bcd]*一致がゼロになるまでこれを行い、一致しない場合は、文字列がPBパターンとまったく一致しないと結論付けます。

他の繰り返しメタ文字は+で、比較シーケンスを1回以上繰り返します。 *+違いに特に注意してください。 *必要な部分を0回以上一致させる必要があります。つまり、繰り返しがまったく存在しない場合がありますが、 +少なくとも1回の出現が必要です。 同様の例では、 ca+tcatまたは、たとえばcaaatと一致しますが、 ctとは一致しません。

さらに2つの繰り返し修飾子があります。 疑問符? 0回または1回の一致のチェック。 たとえば、 home-?brewhomebrewhome-brew両方に一致します。

最も完全な繰り返し指定子は{m,n}で、 mnは整数です。 この決定要因は、少なくともm回、 n回以下の繰り返しが必要であることを意味します。 たとえば、 a/{1,3}ba/ba//bおよびa///bます。 ab 、スラッシュがない行、またはa////bに4つある行は使用できません。

mまたはn指定することはできません。そうしないと、最も妥当な値が想定されます。 mを下げると下限が0になり、 n下げると上限が無限になりますが、前述のように、後者はメモリによって制限されます。

読者はすでに、他の3つの修飾子すべてが最後まで表現できることに気づくでしょう。 {0,}*と同じですか、{1、}は+と同等で、 {0,1}は記号を置き換えることができ?

正規表現を使用する


いくつかの単純な正規表現について説明したので、Pythonでそれらをどのように使用できますか? reモジュールは、正規表現用のインターフェースを提供します。これにより、正規表現をオブジェクトにコンパイルし、それらをマッピングできます。

正規表現のコンパイル

正規表現は、パターンエントリの検索や文字列置換の実行など、さまざまな操作のためのメソッドを持つパターンオブジェクトにコンパイルされます。

>>> インポート
>>> p = reコンパイル 'ab *'
>>> 印刷 p
< _sre。 0xのSRE_Pattern オブジェクト ... >


re.compile()は、さまざまな構文機能とバリエーションを含めるために使用されるオプションの引数も受け入れます。

>>> p = re.compile('ab*', re.IGNORECASE)

正規表現は、文字列としてre.compile()渡されます。 正規表現はPython言語の一部ではないため、文字列として扱われ、それらを表現するための特別な構文はありません。 (正規表現をまったく必要としないアプリケーションがあるので、それらを含む言語仕様を忘れる必要はありません。)代わりに、 socketまたはzlibモジュールのようなCモジュールのラッパーであるreモジュールがあります。

正規表現を文字列として渡すと、Pythonがよりシンプルになりますが、次のセクションのトピックである1つの欠点があります。

バクスレッシュ災害
(または逆斜めペスト:))



前述のように、正規表現では、バックスラッシュ文字( '\' )を使用して、特殊な形式を示したり、文字が特殊な役割を失うことを許可します。 これは、同じ目的でPython文字列リテラルで同じ文字を使用する競合につながります。

LaTeXファイルで見つける必要がある\section一致する正規表現を書きたいとします。 プログラムコードに何を記述するかを見つけるために、一致する必要がある行から始めます。 次に、バックスラッシュとその他のメタキャラクターをバックスラッシュでエスケープすることで回避する必要があります。その結果、 \\部分が文字列に表示されます。 次に、 re.compile ()渡される結果の文字列は\\section必要があります。 ただし、これをPython文字列リテラルとして表現するには、両方のバックスラッシュ、つまり"\\\\section" 再度エスケープする必要があり"\\\\section"

つまり、正規表現は\\である必要があり、各バックスラッシュは\\として正規文字列に変換する必要があるため、バックスラッシュと一致するには、正規表現文字列として'\\\\'を記述する必要があり'\\\\'

解決策は、正規表現に生の文字列を使用することです。 プレフィックス'r'文字列リテラルでは'r'スラッシュは処理されないため、 r"\n"は2文字の文字列( '\'と 'n')であり、 "\ n"は改行の1文字です。 したがって、正規表現は多くの場合、生の文字列を使用して記述されます。

レギュラーストリング生ストリング
「ab *」r'ab * '
「\\\\セクション」r '\\セクション*'
'\\ w + \\ s + \\ 1'r '\ w + \ s + \ 1'


マッチング


コンパイル済みの正規表現を表すオブジェクトを取得したら、それをどうしますか? テンプレートオブジェクトにはいくつかのメソッドと属性があります。 ここでは、それらのうち最も重要なもののみが考慮されます。 完全なリストについては、 reドキュメントを参照してください。

メソッド/属性目的
一致()行の先頭で正規表現が一致するかどうかを判断する
検索()すべての正規表現の一致について文字列全体をスキャンします
findall()正規表現に一致する部分文字列をすべて検索し、それらをリストとして返します
発見者()正規表現一致のすべての部分文字列を検索し、それらを反復子として返します


一致が見つからなかった場合、 match()およびsearch() None返します。 検索が成功すると、一致に関する情報を含むMatchObjectインスタンスがMatchObjectれます。開始と終了の場所、一致のサブストリングなどです。

これについては、 reモジュールをインタラクティブに試すことで確認できます。 Pythonディストリビューションに含まれているデモプログラムであるTools/scripts/redemo.py 。 正規表現と文字列を入力でき、正規表現と一致するかどうかを表示します。 redemo.pyは、複雑な正規表現のデバッグに非常に役立ちます。 Phil SchwartzのKodosは、PBモデルを開発およびテストするためのもう1つの対話型ツールです。

このチュートリアルでは、例として標準のPythonインタープリターを使用します。

>>> インポート
>>> p = reコンパイル '[az] +'
>>> p
< _sre。 0xのSRE_Pattern オブジェクト ... >


これで、正規表現[az]+の文字列を比較してみることができます。 +は「1回以上」の繰り返しを意味するため、空の行は一致しません。 この場合、 match()Noneを返すはずです。

>>> p。 一致 ""
>>> 印刷 p。 一致 ""
なし


次に、パターンに一致する行を試してください: 'tempo' 。 この場合、 match()MatchObjectを返します。これを将来使用する変数に配置できます。

>>> m = p。 一致 'tempo'
>>> 印刷 m
< _sre。 0xのSRE_Match オブジェクト ... >


これで、 MatchObjectを呼び出して、対応する行に関する情報を取得できます。 MatchObjectにはいくつかのメソッドと属性もありますが、最も重要なものは次のとおりです。

メソッド/属性目的
グループ()正規表現に一致する文字列を返します
開始()試合開始位置を返す
終了()マッチ終了位置を返す
スパン()一致する位置のタプル(開始、終了)を返します

>>> m。 グループ
「テンポ」
>>> m。 スタート 、m。 終了
0、5
>>> m。 スパン
0、5


match()メソッドは行の先頭からの一致のみをチェックするため、 start()は常に0を返します。ただし、 search()メソッドは行全体をスキャンするため、先頭は必ずしもゼロではありません。

>>> 印刷 p。 一致 ':::メッセージ'
なし
>>> m = p。 検索 ':::メッセージ' ; プリント m
< _sre。 0xのSRE_Match オブジェクト ... >
>>> m。 グループ
「メッセージ」
>>> m。 スパン
4、11


実際のプログラムでは、最も一般的なスタイルはMatchObjectを変数に保存し、 Noneチェックすることです。 通常、次のようになります。

p = reコンパイル ...
m = p。 一致 'string goes here'
mの場合
'Match found:'を出力します 、m。 グループ
その他
「一致なし」を 印刷


2つのメソッドは、テンプレートのすべての一致を返します。 findall()は、一致する部分文字列のリストを返します。

>>> p = reコンパイル ' \ d +'
>>> p。 findall 「12人のドラマーがドラミング、11人のパイパーが配管、10人の主が跳躍」
[ '12''11''10' ]


findall()メソッドは、結果として返される前に完全なリストを作成する必要があります。 finditer()メソッドは、 MatchObjectインスタンスのシーケンスをMatchObjectとして返します。

>>>イテレータ= p。 finditer '12ドラマーのドラミング、11 ... 10 ... '
>>>イテレータ
< 0x401833acのcallable-iterator オブジェクト >
>>>イテレータでの一致:
... プリントマッチ。 スパン
...
0、2
22、24
29、31


モジュールレベルの機能


テンプレートオブジェクトを作成してそのメソッドを呼び出す必要はありません。 reモジュールはmatch(), search(), findall(), sub()トップレベル関数も提供します。 これらの関数は、テンプレートの場合と同じ引数を取り、最初の引数として文字列PBを使用して、 NoneまたはMatchObjectます。

>>> 印刷 reマッチ r 'From \ s +''Fromage amk'
なし
>>> reマッチ r 'From \ s +''From amk Thu May 14 19:12:10 1998'
< _sre。 0xのSRE_Match オブジェクト ... >


これらの関数は、単にテンプレートオブジェクトを作成し、適切なメソッドを呼び出します。 また、オブジェクトをキャッシュに保存するため、同じ正規表現を使用した今後の呼び出しが高速になります。

メソッドでこれらの関数またはテンプレートを使用する必要がありますか? 正規表現が使用される頻度と個人のコーディングスタイルによって異なります。 正規表現がコード内の1か所でのみ使用される場合、そのような関数はおそらくより便利です。 プログラムに多くの正規表現が含まれている場合、または同じ表現を複数の場所で再利用する場合、すべての正規表現をプリコンパイルするコードのセクションで、すべての定義を1か所で収集することをお勧めします。 標準ライブラリの例として、 xmllib.py一部をxmllib.pyます。

ref = re.compile( ... )
entityref = re.compile( ... )
charref = re.compile( ... )
starttagopen = re.compile( ... )


私自身は、一度だけ使用する場合でもコンパイル済みオブジェクトを使用することを好みますが、私と同じ純粋主義者はほとんどいません。

コンパイルフラグ


コンパイルフラグを使用すると、正規表現の動作のいくつかの側面を変更できます。 フラグは、 IGNORECASEなどの長い名前と、 IなどのI文字の形式の短い名前の2つの名前でモジュールで使用できますI バイナリORの形式でいくつかのフラグを指定できます。 たとえば、 re.I | re.M re.I | re.MはフラグIとMを設定します。

Dotall s
一致は'.'と同じ'.' 、つまり、任意の文字を使用しますが、このフラグをオンにすると、考慮事項に改行文字が追加されます。

無視、私
大文字と小文字を区別しないマッチング。 たとえば、 [AZ]も小文字と一致するため、 SpamSpam, spam, spAMと一致します。

ロケール、L
\w, \W, \b, \Bローカライズに依存します。 たとえば、フランス語のテキストを操作し、単語を見つけるために\w+を書きたいが、 \w[A-Za-z]文字のみを検索し、 'é'または 'ç'を検索しない場合。 システムが正しく構成され、フランス語が選択されている場合、「é」も文字として扱われます。

マルチライン、M
(メタ文字^および$はまだ説明されていません。これらは、このマニュアルの第2部の冒頭で少し後に説明します。

通常、 ^は行の先頭でのみ一致を検索し、改行文字(存在する場合)の直前でのみ検索します。 このフラグが指定されている場合、 ^比較はすべての行、つまり先頭と各改行文字の直後で行われます。 $についても同様です。

ユニコード、U
\w, \W, \b, \B, \d, \D, \s, \S対応するUnicodeテーブルにします。

冗長、X
より明確かつ明確に編成できる冗長(冗長)正規表現が含まれます。 このフラグを指定すると、文字クラス内にあるか、エスケープされていないバックスラッシュが前にない限り、正規表現文字列内のスペースは無視されます。 これにより、より明確な方法で正規表現を整理できます。 このフラグを使用すると、エンジンで無視される'#'で始まる正規表現にコメントを挿入することもできます。

RVが読みやすくなる例:

charref = reコンパイル r "" "
&[#]#数値エンティティ参照の開始

0 [0-7] +#オクタル形式
| [0-9] +#10進数形式
| x [0-9a-fA-F] +#16進形式

; #末尾のセミコロン
"" "re。VERBOSE


詳細がなければ、次のようになります。

charref = reコンパイル "&#(0 [0-7] +"
「| [0-9] +」
「| x [0-9a-fA-F] +);」


上記の例では、Pythonの文字列リテラルの自動連結を使用してRVをより小さな部分に分割しましたが、それでも説明なしでは、この例を理解するのはを使用するバージョンよりも困難re.VERBOSEです。

この時点で、今のところレビューを完了します。後半の少し前にリラックスすることをお勧めします。他のメタキャラクター、文字列の分割、検索、置換の方法、および正規表現の使用例がたくさんあります。

継続

Source: https://habr.com/ru/post/J115825/


All Articles