Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

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つ以上の数字を一致させます

かなりきちんとしていますね


  1. Rubyでのラムダの使用

    ブロックはRubyの非常に重要な部分であり、ブロックなしで言語を想像するのは難しいです。しかし、ラムダ?ラムダが好きなのは誰ですか?あなたはそれを使わずに何年も行くことができます。まるで過ぎ去った時代の遺物のようです。 ...しかし、それは完全に真実ではありません。ラムダを少し調べてみると、ラムダにはいくつかの興味深いトリックがあります。 この記事では、ラムダの使用法の基本から始めて、さらに興味深い高度な使用法に移ります。したがって、ラムダを毎日使用していて、それらについてすべて知っている場合は、下にスクロールするだけです。 Lambdasについて覚えておくべき主なことは、それらが関数の

  2. Rubyの正規表現をマスターする

    Rubyの正規表現( Ruby regex 略して)さらに処理するためにデータを抽出する目的で、文字列内の特定のパターンを見つけるのに役立ちます。 正規表現の2つの一般的な使用例には、検証と解析が含まれます。 例 : ruby正規表現を含むメールアドレスについて考えてみてください 有効な電子メールアドレスがどのように見えるかを定義できます。つまり、プログラムは有効なメールアドレスと無効なメールアドレスを区別できるようになります。 Rubyの正規表現は2つのスラッシュの間に定義されます それらを他の言語構文と区別するため。最も単純な表現は、単語または1文字にさえ一致します。 例