Ruby正規表現内での条件の使用
2013年にRuby2.0が出荷した多くの新機能の中で、私が最も注意を払わなかったのは、新しい正規表現エンジンであるOnigmoでした。結局のところ、正規表現は正規表現です-なぜRubyがそれらを実装する方法を気にする必要があるのですか?
結局のところ、Onigmo regexエンジンには、正規表現内で条件を使用する機能など、いくつかの巧妙なトリックがあります。
この投稿では、正規表現の条件に飛び込み、Rubyによるそれらの実装の癖について学び、Rubyの制限を回避するためのいくつかの秘訣について説明します。始めましょう!
正規表現の条件を理解するには、最初にグループ化とキャプチャを理解する必要があります。
米国の引用のリストがあると想像してください:
Fayetteville, AR
Seattle, WA
都市名を州の略語から分離したいとします。これを行う1つの方法は、複数の一致を実行することです。
PLACE = "Fayetteville, AR"
# City: Match any char that's not a comma
PLACE.match(/[^,]+/)
# => #<MatchData "Fayetteville">
# Separator: Match a comma and optional spaces
PLACE.match(/, */)
# => #<MatchData ", ">
# State: Match a 2-letter code at the end of the string.
PLACE.match(/[A-Z]{2}$/)
# => #<MatchData "AR">
これは機能しますが、冗長すぎます。グループを使用すると、1つの正規表現だけで都市と州の両方をキャプチャできます。
それでは、上記の正規表現を組み合わせて、各セクションを括弧で囲んでみましょう。括弧は、正規表現で物事をグループ化する方法です。
PLACE = "Fayetteville, AR"
m = PLACE.match(/([^,]+)(, *)([A-Z]{2})/)
# => #<MatchData "Fayetteville, AR" 1:"Fayetteville" 2:", " 3:"AR">
ご覧のとおり、上記の式は都市と州の両方を表しています。 MatchData
を処理してアクセスします 配列のように:
m[1]
# => "Fayetteville"
m[3]
# => "AR"
上記で行ったように、グループ化の問題は、キャプチャされたデータが配列に入れられることです。配列内での位置が変わった場合は、コードを更新する必要があります。そうしないと、バグが発生します。
たとえば、", "
をキャプチャするのはばかげていると判断する場合があります。 文字。したがって、正規表現のその部分の周りの括弧を削除します:
m = PLACE.match(/([^,]+), *([A-Z]{2})/)
# => #<MatchData "Fayetteville, AR" 1:"Fayetteville" 2:"AR">
m[3]
# => nil
しかし今はm[3]
状態が含まれなくなりました-バグ都市。
正規表現グループに名前を付けることで、よりセマンティックにすることができます。構文は、今使用したものと非常によく似ています。正規表現を括弧で囲み、次のように名前を指定します:
/(?<groupname>regex)/
これを市/州の正規表現に適用すると、次のようになります。
m = PLACE.match(/(?<city>[^,]+), *(?<state>[A-Z]{2})/)
# => #<MatchData "Fayetteville, AR" city:"Fayetteville" state:"AR">
また、MatchData
を処理することで、キャプチャされたデータにアクセスできます。 ハッシュのように:
m[:city]
# => "Fayetteville"
正規表現の条件文は、/(?(A)X|Y)/
の形式を取ります。 。それらを使用するいくつかの有効な方法は次のとおりです。
# If A is true, then evaluate the expression X, else evaluate Y
/(?(A)X|Y)/
# If A is true, then X
/(?(A)X)/
# If A is false, then Y
/(?(A)|Y)/
あなたの状態のための最も一般的な2つのオプション、A
は:
- 名前付きまたは番号付きのグループがキャプチャされましたか?
- 見回しは真と評価されますか?
それらの使用方法を見てみましょう:
グループはキャプチャされましたか?
グループの存在を確認するには、?(n)
を使用します 構文。nは整数、または<>
で囲まれたグループ名です。 または''
。
# Has group number 1 been captured?
/(?(1)foo|bar)/
# Has a group named "mygroup" been captured?
/(?(<mygroup>)foo|bar)/
米国の電話番号を解析していると想像してください。これらの番号には3桁の市外局番があり、番号が1で始まらない限りオプションです。
1-800-555-1212 # Valid
800-555-1212 # Valid
555-1212 # Valid
1-555-1212 # INVALID!!
条件を使用して、番号が1で始まる場合にのみ、市外局番を要件にすることができます。
# This regular expression looks complex, but it's made of simple pieces
# `^(1-)?` Does the string start with "1-"? If so, capture it as group 1
# `(?(1)` Was anything captured in group one?
# `\d{3}-` if so, do a required match of three digits and a dash (the area code)
# `|(\d{3}-)?` if not, do an optional match of three digits and a dash (area code)
# `\d{3}-\d{4}` match the rest of the phone number, which is always required.
re = /^(1-)?(?(1)\d{3}-|(\d{3}-)?)\d{3}-\d{4}/
"1-800-555-1212".match(re)
#=> #<MatchData "1-800-555-1212" 1:"1-" 2:nil>
"800-555-1212".match(re)
#=> #<MatchData "800-555-1212" 1:nil 2:"800-">
"555-1212".match(re)
#=> #<MatchData "555-1212" 1:nil 2:nil>
"1-555-1212".match(re)
=> nil
グループベースの条件を使用する際の問題の1つは、グループを照合すると、文字列内のそれらの文字が「消費」されることです。その場合、これらの文字は条件付きでは使用できません。
たとえば、次のコードは、テキスト「USD」が存在する場合、100との一致を試みて失敗します。
"100USD".match(/(USD)(?(1)\d+)/) # nil
Perlおよびその他のいくつかの言語では、条件に先読みステートメントを追加できます。これにより、文字列内の任意の場所のテキストに基づいて条件をトリガーできます。しかし、Rubyにはこれがないので、少しクリエイティブにする必要があります。
幸い、ルックアラウンド式を悪用することで、Rubyの正規表現条件の制限を回避できます。
ルックアラウンドとは何ですか?
通常、正規表現パーサーは文字列を最初から最後までステップ実行して、一致するものを探します。これは、ワードプロセッサでカーソルを左から右に移動するようなものです。
先読み式と後読み式の動作は少し異なります。文字を消費せずに文字列を検査できます。完了すると、カーソルは最初とまったく同じ場所に置かれます。
ルックアラウンドの優れた入門書については、Rexeggのマスタリングガイドをチェックして、先読みと後見を確認してください
構文は次のようになります:
タイプ | 構文 | 例 |
---|---|---|
先読み | (?=query) | \d+(?= dollars) 「100ドル」で100に一致 |
ネガティブルックアヘッド | (?!query) | \d+(?! dollars) 「ドル」という単語が後に続かない場合は100に一致します |
後ろを見る | (?<=query) | (?<=lucky )\d 「ラッキー7」の7に一致 |
ネガティブルックビハインド | (?<!query) | (?<!furious )\d 「ラッキー7」の7に一致 |
条件付きでは、すでに設定されているグループの存在のみを照会できます。通常、これは、グループのコンテンツが消費されており、条件付きで利用できないことを意味します。
ただし、先読みを使用して、文字を消費せずにグループを設定できます。あなたの心はもう吹き飛ばされていますか?
動作しなかったこのコードを覚えていますか?
"100USD".match(/(USD)(?(1)\d+)/) # nil
先読みでグループをキャプチャするように変更すると、突然正常に機能します:
"100USD".match(/(?=.*(USD))(?(1)\d+)/)
=> #<MatchData "100" 1:"USD">
そのクエリを分解して、何が起こっているかを見てみましょう:
-
(?=.*(USD))
先読みを使用して、テキストをスキャンして「USD」を探し、グループ1にキャプチャします -
(?(1)
グループ1が存在する場合 -
\d+
次に、1つ以上の数字を一致させます
かなりきちんとしていますね
-
Rubyでのラムダの使用
ブロックはRubyの非常に重要な部分であり、ブロックなしで言語を想像するのは難しいです。しかし、ラムダ?ラムダが好きなのは誰ですか?あなたはそれを使わずに何年も行くことができます。まるで過ぎ去った時代の遺物のようです。 ...しかし、それは完全に真実ではありません。ラムダを少し調べてみると、ラムダにはいくつかの興味深いトリックがあります。 この記事では、ラムダの使用法の基本から始めて、さらに興味深い高度な使用法に移ります。したがって、ラムダを毎日使用していて、それらについてすべて知っている場合は、下にスクロールするだけです。 Lambdasについて覚えておくべき主なことは、それらが関数の
-
Rubyの正規表現をマスターする
Rubyの正規表現( Ruby regex 略して)さらに処理するためにデータを抽出する目的で、文字列内の特定のパターンを見つけるのに役立ちます。 正規表現の2つの一般的な使用例には、検証と解析が含まれます。 例 : ruby正規表現を含むメールアドレスについて考えてみてください 有効な電子メールアドレスがどのように見えるかを定義できます。つまり、プログラムは有効なメールアドレスと無効なメールアドレスを区別できるようになります。 Rubyの正規表現は2つのスラッシュの間に定義されます それらを他の言語構文と区別するため。最も単純な表現は、単語または1文字にさえ一致します。 例