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

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

Rubyの正規表現( Ruby regex 略して)さらに処理するためにデータを抽出する目的で、文字列内の特定のパターンを見つけるのに役立ちます。

正規表現の2つの一般的な使用例には、検証と解析が含まれます。

ruby正規表現を含むメールアドレスについて考えてみてください 有効な電子メールアドレスがどのように見えるかを定義できます。つまり、プログラムは有効なメールアドレスと無効なメールアドレスを区別できるようになります。

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

Rubyの正規表現は2つのスラッシュの間に定義されます それらを他の言語構文と区別するため。最も単純な表現は、単語または1文字にさえ一致します。

# Find the word 'like'
"Do you like cats?" =~ /like/

これは、単語が見つかった場合(一致が成功した場合)またはnilの場合に、単語の最初の出現のインデックスを返します。 それ以外は。インデックスを気にしない場合は、String#includeを使用できますか?メソッド。

文字列が正規表現と一致するかどうかを確認する別の方法は、matchを使用することです。 方法:

if "Do you like cats?".match(/like/)
  puts "Match found!"
end

今:

日付、電話番号、メール、URLなどを照合、キャプチャ、置換できるように、より高度なパターンを構築する方法を学習します。

キャラクタークラス

文字クラスを使用すると、一致する文字の範囲またはリストを定義できます。例:[aeiou] 任意の母音に一致します。

:文字列は含む 母音?

def contains_vowel(str)
  str =~ /[aeiou]/
end

contains_vowel("test") # returns 1
contains_vowel("sky")  # returns nil

これは金額を考慮しません キャラクターの数については、すぐにそれを行う方法を見ていきます。

範囲

範囲を使用して、すべてを入力しなくても、複数の文字や数字を照合できます。つまり、[2-5]のような範囲 [2345]と同じです 。

いくつかの有用な範囲:

  • [0-9] 0から9までの任意の数値に一致します
  • [a-z] aからzまでの任意の文字に一致します(キャップ​​なし)
  • [^ a-z] 否定された範囲

:この文字列には数字が含まれていますか?

def contains_number(str)
  str =~ /[0-9]/
end

contains_number("The year is 2015")  # returns 12
contains_number("The cat is black")  # returns nil

注意: `=〜`を使用した場合の戻り値は、文字列インデックスまたは `nil`

のいずれかです。

文字範囲を指定するための優れた省略構文があります:

  • \ w [0-9a-zA-Z_]と同等です
  • \ d [0-9]と同じです
  • \ s 空白に一致します (タブ、通常のスペース、改行)

これらの否定的な形もあります:

  • \ W [0-9a-zA-Z_]にないもの
  • \ D 数字ではないもの
  • \ S スペース以外のもの

ドット文字. 新しい行以外のすべてに一致します。リテラルの.を使用する必要がある場合 その後、それを脱出する必要があります。

:特殊文字のエスケープ

# If we don't escape, the letter will match
"5a5".match(/\d.\d/)

# In this case only the literal dot matches
"5a5".match(/\d\.\d/) # nil
"5.5".match(/\d\.\d/) # match

修飾子

これまで、一度に1つの文字しか一致させることができませんでした。複数の文字を一致させるために、パターン修飾子を使用できます。

修飾子 説明
+ 1つ以上
* 0以上
0または1
{3,5} 3から5の間

これまでに学んだことをすべて組み合わせて、より複雑な正規表現を作成できます。

:これはIPアドレスのように見えますか?

# Note that this will also match some invalid IP address
# like 999.999.999.999, but in this case we just care about the format.

def ip_address?(str)
  # We use !! to convert the return value to a boolean
  !!(str =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)
end

ip_address?("192.168.1.1")  # returns true
ip_address?("0000.0000")    # returns false

完全な文字列照合

完全一致が必要な場合は、別のタイプの修飾子が必要になります。私が話していることを確認できるように、例を見てみましょう:

# We want to find if this string is exactly four letters long, this will
# still match because it has more than four, but it's not what we want.
"Regex are cool".match /\w{4}/

# Instead we will use the 'beginning of line' and 'end of line' modifiers
"Regex are cool".match /^\w{4}$/

# This time it won't match. This is a rather contrived example, since we could just
# have used .size to find the length, but I think it gets the idea across.

すべての行だけでなく、文字列の先頭で厳密に一致させる(\nの後) )\Aを使用する必要があります および\Z ^の代わりに および$

キャプチャグループ

キャプチャグループを使用すると、一致の一部をキャプチャして後で再利用できます。一致をキャプチャするために、キャプチャする部分を括弧で囲みます。

:ログファイルの解析

Line = Struct.new(:time, :type, :msg)
LOG_FORMAT = /(\d{2}:\d{2}) (\w+) (.*)/

def parse_line(line)
  line.match(LOG_FORMAT) { |m| Line.new(*m.captures) }
end

parse_line("12:41 INFO User has logged in.")
# This produces objects like this:
# 

この例では、.matchを使用しています =~の代わりに 。

このメソッドはMatchDataを返します 一致する場合はオブジェクト、それ以外の場合はnil。 MatchData クラスには多くの便利なメソッドがあります。詳細については、ドキュメントを確認してください!

ブール値のみが必要な場合(true / false )次に、match?を使用できます Ruby2.4以降で使用可能なメソッド。これもmatchよりも高速です RubyはMatchDataを作成する必要がないため オブジェクト。

.capturesを使用してキャプチャされたデータにアクセスできます メソッドまたはMatchDataの処理 配列のようなオブジェクトの場合、ゼロインデックスは完全に一致し、結果のインデックスには一致したグループが含まれます。

最初のキャプチャグループが必要な場合は、次のように実行できます。

m = "John 31".match /\w+ (\d+)/

m[1]
# 31

キャプチャしないグループを持つこともできます。パフォーマンスを低下させることなく、式をグループ化できます。複雑な表現を読みやすくするために、名前付きグループが役立つ場合もあります。

構文 説明 非キャプチャグループ 名前付きグループ
(?:...)
(?<foo>...)

:名前付きグループ

m = "David 30".match /(?\w+) (?\d+)/
m[:age]
# => "30"
m[:name]
# => "David"

名前付きグループはMatchDataを返します 結果を読み取るためにアクセスできるオブジェクト。

先を見て後ろを見る

これは、すべての正規表現の実装で利用できるとは限らない、より高度な手法です。 Rubyの正規表現エンジンはこれを実行できるので、それを利用する方法を見てみましょう。

先を見越して、前後に特定の一致があるかどうかを確認できます。

名前 説明 前向きな先読み ポジティブルックビハインド ネガティブな先読み ネガティブルックビハインド
(?=pat)
(?<=pat)
(?! pat)
(?<!pat)

:少なくとも1文字の前に数字がありますか?

def number_after_word?(str)
  !!(str =~ /(?<=\w) (\d+)/)
end

number_after_word?("Grade 99")

ルビーの正規表現クラス

Rubyの正規表現は、Regexpのインスタンスです。 クラス。ほとんどの場合、このクラスを直接使用することはありませんが、知っておくとよいでしょう🙂

puts /a/.class
# Regexp

考えられる用途の1つは、文字列から正規表現を作成することです。

regexp = Regexp.new("a")

正規表現を作成する別の方法:

regexp = %r{\w+}

正規表現オプション

正規表現にいくつかのオプションを設定して、動作を変えることができます。

オプション 説明
i ruby正規表現の大文字と小文字を区別しない
m ドットは改行と一致します
x 空白を無視する

これらのオプションを使用するには、正規表現の最後の/を閉じる後に文字を追加します 。

このように

"abc".match?(/[A-Z]/i)

長い正規表現のフォーマット

複雑なRuby正規表現はかなり読みにくくなる可能性があるため、複数行に分割すると便利です。これは、「x」修飾子を使用して行うことができます。この形式では、正規表現内でコメントを使用することもできます。

LOG_FORMAT = %r{
  (\d{2}:\d{2}) # Time
  \s(\w+)       # Event type
  \s(.*)        # Message
}x

Rubyの正規表現:すべてをまとめる

正規表現は多くのRubyメソッドで使用できます。

  • .split
  • .scan
  • .gsub
  • その他多数…

:.scan

を使用して文字列のすべての単語を照合します
"this is some string".scan(/\w+/)
# => ["this", "is", "some", "string"]

:文字列からすべての数値を抽出します

"The year was 1492.".scan(/\d+/)
# => ["1492"]

:文字列内のすべての単語を大文字にします

str = "lord of the rings"

str.gsub(/\w+/) { |w| w.capitalize }
# => "Lord Of The Rings"

:メールアドレスを検証する

email = "test@example.com"

!!email.match(/\A[\w.+-]+@\w+\.\w+\z/)

# true

この最後の例では、!!を使用しています 結果をブール値に変換します(true / false )、または、match?を使用することもできます Ruby 2.4+のメソッドは、すでにこれを実行しており、さらに高速です。

結論

正規表現は素晴らしいですが、少し注意が必要な場合もあります。 rubular.comなどのツールを使用すると、ruby正規表現を作成するのに役立ちます よりインタラクティブな方法で。 Rubularには、非常に便利なRuby正規表現のチートシートも含まれています。今度は、そのエディターを開いてコーディングを開始する番です!

ああ、これを共有することを忘れないでください 楽しんだら友達と一緒に、もっと多くの人が学べるように🙂


  1. JavaScript正規表現

    正規表現は、検索パターンを説明する文字列です。この検索パターンは、テキストで検索する内容を指定するために使用されます。 以下はJavaScriptの正規表現のコードです- 例 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <

  2. アサーションの背後にあるJavaScript正規表現

    Lookbehindでは、前に何かがある場合にのみパターンを一致させることができます。以下は例です- 例 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <