正規表現は、文字列のパターンを記述するための簡潔かつ柔軟なツールです。この vignette は、stringi によって実装されたstringrの正規表現の主要な機能を説明しています。チュートリアルではないので、もし正規表現に馴染みがなければ、 http://r4ds.had.co.nz/strings.html から始めることをお勧めします。もし、詳細をマスターしたいのであれば、Jeffrey E. F. Friedl による古典的な Mastering Regular Expressions を読むことをお勧めします。
正規表現は stringr のデフォルトのパターン・エンジンです。つまり、パターンマッチの関数をそのままの文字列で使う場合、regex()
の呼び出しでラップするのと同じです。
# The regular call:
str_extract(fruit, "nana")
# Is shorthand for
str_extract(fruit, regex("nana"))
以下の例で見るように、デフォルトのオプションを上書きしたい場合は、 regex()
を明示的に使用する必要があります。
最も単純なパターンは、正確な文字列にマッチします。
<- c("apple", "banana", "pear")
x str_extract(x, "an")
#> [1] NA "an" NA
ignore_case = TRUE
を指定すると、大文字と小文字を区別しないマッチングを行うことができます。
<- c("banana", "Banana", "BANANA")
bananas str_detect(bananas, "banana")
#> [1] TRUE FALSE FALSE
str_detect(bananas, regex("banana", ignore_case = TRUE))
#> [1] TRUE TRUE TRUE
次に複雑なのは .
で、これは改行以外のあらゆる文字にマッチします。
str_extract(x, ".a.")
#> [1] NA "ban" "ear"
dotall = TRUE
と設定することで、.
が \n
を含むすべてにマッチするようにすることができます。
str_detect("\nX\n", ".X.")
#> [1] FALSE
str_detect("\nX\n", regex(".X.", dotall = TRUE))
#> [1] TRUE
もし「.
」が任意の文字にマッチするなら、「.
」そのものにはどうしたらマッチするのでしょうか?「エスケープ」を使って、正規表現の特殊な振る舞いを使わず、正確にマッチさせたいことを伝える必要があります。文字列と同様に、正規表現もバックスラッシュ \
を使って特殊な挙動をエスケープします。つまり、.
にマッチするには、正規表現 \.
が必要です。残念ながら、これは問題を引き起こします。正規表現を表現するために文字列を使いますが、文字列の中で \
はエスケープシンボルとしても使われます。だから、正規表現 \.
を作るには、文字列 "\\."
が必要となります。
# To create the regular expression, we need \\
<- "\\."
dot
# But the expression itself only contains one:
writeLines(dot)
#> \.
# And this tells R to look for an explicit .
str_extract(c("abc", "a.c", "bef"), "a\\.c")
#> [1] NA "a.c" NA
正規表現で \
がエスケープ文字として使われる場合、\
そのもの にはどのようにマッチングするのでしょうか?正規表現を作るためにエスケープする必要がある。その正規表現を作るには、文字列を使用する必要があり、その文字列もまた \
をエスケープする必要がある。つまり、\
そのものにマッチするためには、"\\\\"
と書く必要があります。バックスラッシュが4つとなります。
<- "a\\b"
x writeLines(x)
#> a\b
str_extract(x, "\\\\")
#> [1] "\\"
この vignette では、正規表現を表す文字列に \.
を、正規表現を表す文字列に "\\."
を使用します。
別の引用符の付け方は \Q...\E
で、...
に含まれるすべての文字が完全一致として扱われます。これは、正規表現の一部としてユーザー入力に完全に一致させたい場合に便利です。
<- c("a.b.c.d", "aeb")
x <- "a.b"
starts_with
str_detect(x, paste0("^", starts_with))
#> [1] TRUE TRUE
str_detect(x, paste0("^\\Q", starts_with, "\\E"))
#> [1] TRUE FALSE
また、エスケープを使うことで、入力しにくい文字を個別に指定することができます。個々の Unicode 文字を指定するには、16進数で指定する方法(4桁が最も一般的)と、名前で指定する方法があります。
\xhh
: 16進数2桁。
\x{hhhh}
: 16進数1-4桁。
\uhhhh
: 16進数4桁。
\Uhhhhhhhh
: 16進数8桁。
N{grinning face}
は、基本的な笑顔の絵文字にマッチします。
同様に、多くの一般的な制御文字を指定することができます。
\a
: bell.
\cX
: control-X character
\e
: escape (\u001B
).
\f
: form feed (\u000C
).
\n
: line feed (\u000A
).
\r
: carriage return (\u000D
).
\t
: horizontal tabulation (\u0009
).
\0ooo
: これは8進数の文字にマッチします。‘ooo’ は1桁から3桁までの8進数です。 000から0377まで。先頭の0は必須。
(これらの多くは歴史的な興味しかないため、ここでは完全性を期すためにのみ記載しています。)
複数の文字にマッチするパターンも数多く存在します。すでに見たことがあるかもしれませんが、 .
は任意の文字にマッチします (改行は除く)。これは grapheme cluster 、つまり、1つのシンボルを形成する個々の要素の集合にマッチします。例えば、“á” は “a” とアクセント記号で表現されます。.
は “a” にマッチし、\X
は記号全体にマッチします。
<- "a\u0301"
x str_extract(x, ".")
#> [1] "a"
str_extract(x, "\\X")
#> [1] "á"
その他に、より狭いクラスの文字にマッチする5つのエスケープペアがあります。
\d
: 任意の数字にマッチします。その補数である \D
は、10進数でない任意の文字にマッチします。
str_extract_all("1 + 2 = 3", "\\d+")[[1]]
#> [1] "1" "2" "3"
技術的には、\d
は Unicode Category of Nd (“Number, Decimal Digit”) の任意の文字を含み、他の言語の数字記号も含まれます。
# Some Laotian numbers
str_detect("១២៣", "\\d")
#> [1] TRUE
\s
: 任意のホワイトスペースにマッチします。これには、タブ、改行、フォームフィード、Unicode Zカテゴリに含まれるすべての文字(さまざまなスペース文字やその他のセパレータが含まれます)が含まれます。その補集合である \S
は、任意の非ホワイトスペース文字にマッチします。
<- "Some \t badly\n\t\tspaced \f text")
(text #> [1] "Some \t badly\n\t\tspaced \f text"
str_replace_all(text, "\\s+", " ")
#> [1] "Some badly spaced text"
\p{property name}
は \p{Uppercase}
や \p{Diacritic}
のような特定の unicode プロパティを持つ任意の文字にマッチします。その補集合である \P{property name}
は、そのプロパティを持たない全ての文字にマッチします。unicode のプロパティの完全なリストは http://www.unicode.org/reports/tr44/#Property_Index にあります。
<- c('"Double quotes"', "«Guillemet»", "“Fancy quotes”"))
(text #> [1] "\"Double quotes\"" "«Guillemet»" "“Fancy quotes”"
str_replace_all(text, "\\p{quotation mark}", "'")
#> [1] "'Double quotes'" "'Guillemet'" "'Fancy quotes'"
\w
は、アルファベット、マーク、10進数を含む、あらゆる「単語」文字にマッチします。その補数である \W
は、任意の非単語文字にマッチします。
str_extract_all("Don't eat that!", "\\w+")[[1]]
#> [1] "Don" "t" "eat" "that"
str_split("Don't eat that!", "\\W")[[1]]
#> [1] "Don" "t" "eat" "that" ""
技術的には、w
はコネクタ句読点、\u200c
(zero width connector) と \u200d
(zero width joiner) にもマッチしますが、これらは実際にはほとんど見かけません。
\b
は単語境界、つまり単語と非単語文字の間の遷移にマッチします。\B
はその逆で、単語と非単語文字の両方がある境界をマッチします。
str_replace_all("The quick brown fox", "\\b", "_")
#> [1] "_The_ _quick_ _brown_ _fox_"
str_replace_all("The quick brown fox", "\\B", "_")
#> [1] "T_h_e q_u_i_c_k b_r_o_w_n f_o_x"
また、[]
を使って独自の 文字クラス を作成することもできます。
[abc]
: a, b または c にマッチ[a-z]
: a から z の文字 (Unicode コード順) にマッチ[^abc]
: a, b または c 以外にマッチ[\^\-]
: ^
または -
にマッチ[]
内で使うことができるビルド済みクラスもたくさんあります。
[:punct:]
: 句読点 (punctuation)[:alpha:]
: アルファベット文字 (letters)[:lower:]
: 小文字 (lowercase letters)[:upper:]
: 大文字 (uppercase letters)[:digit:]
: 数字[:xdigit:]
: 16進数[:alnum:]
: letters と numbers.[:cntrl:]
: control characters.[:graph:]
: letters, numbers, と punctuation.[:print:]
: letters, numbers, punctuation, と 空白[:space:]
: 半角スペース (\s
と基本的に同等).[:blank:]
: 半角スペースとタブこれらはすべて、文字クラスを表す []
の中に入ります。例えば、 [[:digit:]AX]
は、すべての数字、A、Xにマッチします。
また、Unicode のプロパティ、例えば [\p{Letter}]
や集合演算、例えば [\p{Letter}--p{script=latin}]
も使用できます。詳しくは ?"stringi-search-charclass"
を参照してください。
|
は alternation 演算子で、1 つ以上のマッチの中から選択します。例えば、 abc|def
は abc
または def
にマッチします。
str_detect(c("abc", "def", "ghi"), "abc|def")
#> [1] TRUE TRUE FALSE
abc|def
は abc
や def
にマッチしますが、 abcyz
や abxyz
にはマッチしないことに注意してください。
(訳注: abcyz
にはマッチします。開発者用最新版では、この部分が削除されているようです。)
括弧を使用すると、デフォルトの優先順位の規則を上書きすることができます。
str_extract(c("grey", "gray"), "gre|ay")
#> [1] "gre" "ay"
str_extract(c("grey", "gray"), "gr(e|a)y")
#> [1] "grey" "gray"
また、カッコは backreferences で参照できる「グループ」を定義し、\1
や \2
など、str_match()
で抽出することができます。例えば、以下の正規表現は、繰り返される文字の組を持つすべての果物を見つけます。
<- "(..)\\1"
pattern %>%
fruit str_subset(pattern)
#> [1] "banana" "coconut" "cucumber" "jujube" "papaya"
#> [6] "salal berry"
%>%
fruit str_subset(pattern) %>%
str_match(pattern)
#> [,1] [,2]
#> [1,] "anan" "an"
#> [2,] "coco" "co"
#> [3,] "cucu" "cu"
#> [4,] "juju" "ju"
#> [5,] "papa" "pa"
#> [6,] "alal" "al"
グループ化しない括弧である (?:...)
を使用すると、優先順位を制御することはできますが、グループ内のマッチを捕まえることはできません。これはキャプチャする括弧よりも若干効率的です。
str_match(c("grey", "gray"), "gr(e|a)y")
#> [,1] [,2]
#> [1,] "grey" "e"
#> [2,] "gray" "a"
str_match(c("grey", "gray"), "gr(?:e|a)y")
#> [,1]
#> [1,] "grey"
#> [2,] "gray"
これは、マッチの捕捉と優先順位の制御を個別に行う必要がある、より複雑なケースで最も有用です。
デフォルトでは、正規表現は文字列の任意の部分にマッチします。文字列の先頭か末尾にマッチするように正規表現を anchor するのが便利なことがよくあります。
^
は文字列の先頭にマッチします。$
は文字列の末尾にマッチします。<- c("apple", "banana", "pear")
x str_extract(x, "^a")
#> [1] "a" NA NA
str_extract(x, "a$")
#> [1] NA "a" NA
“$” や “^” そのものにマッチするには、それらをエスケープする必要があり、 \$
と \^
となります。
複数行の文字列には、 regex(multiline = TRUE)
を使用することができます。これは、 ^
と $
の動作を変更し、3つの新しい演算子を導入しています。
^
は各行の先頭にマッチするようになりました。
$
は各行の末尾にマッチするようになりました。
A
は入力の先頭にマッチします。
z
は入力の終わりにマッチします。
Z
は入力の最後にマッチしますが、最終行終端がある場合はその前にマッチします。
<- "Line 1\nLine 2\nLine 3\n"
x str_extract_all(x, "^Line..")[[1]]
#> [1] "Line 1"
str_extract_all(x, regex("^Line..", multiline = TRUE))[[1]]
#> [1] "Line 1" "Line 2" "Line 3"
str_extract_all(x, regex("\\ALine..", multiline = TRUE))[[1]]
#> [1] "Line 1"
繰り返し演算子を使って、パターンにマッチする回数を制御することができます。
?
: 0回または1回。+
: 1回以上*
: 0回またはそれ以上<- "1888 is the longest year in Roman numerals: MDCCCLXXXVIII"
x str_extract(x, "CC?")
#> [1] "CC"
str_extract(x, "CC+")
#> [1] "CCC"
str_extract(x, 'C[LX]+')
#> [1] "CLXXX"
なお、これらの演算子の優先順位は高いので、次のように書くことができます。colou?r
と書くと、アメリカでもイギリスでも同じ綴りになります。つまり、ほとんどの場合、 bana(na)+
のように括弧が必要になります。
また、マッチする数を正確に指定することもできます。
{n}
: ちょうどn 回{n,}
: n 回以上{n,m}
:n 〜 m 回str_extract(x, "C{2}")
#> [1] "CC"
str_extract(x, "C{2,}")
#> [1] "CCC"
str_extract(x, "C{2,3}")
#> [1] "CCC"
デフォルトでは、これらのマッチは「欲張り (greedy)」で、可能な限り長い文字列にマッチします。これらのマッチの後に ?
をつけると、可能な限り短い文字列にマッチする「怠惰な (lazy, 訳注 reluctant とも言う)」マッチになります。
??
: 0 回か 1回、0 回が望ましい。+?
: 1 回以上、できるだけ少ない回数でマッチします。*?
: 0 回以上、できるだけ少ない回数でマッチします。{n,}?
: n 回以上、なるべく少ない回数でマッチします。{n,m}?
: n 〜 m 回、n 回以上でなるべく少なくマッチすること。str_extract(x, c("C{2,3}", "C{2,3}?"))
#> [1] "CCC" "CC"
str_extract(x, c("C[LX]+", "C[LX]+?"))
#> [1] "CLXXX" "CL"
また、マッチの後に +
を付けることで「独占的 (possessive, 絶対などとも訳されますが、訳注 良い訳がないようです)」にすることができます。これは、マッチの後の部分が失敗しても、その繰り返しがより少ない文字数で再試行されないことを意味します。これは、最悪のシナリオ(「壊滅的バックトラック」と呼ばれる)においてパフォーマンスを向上させるために使われる高度な機能です。
?+
: 0 回または 1 回、独占的++
: 1 回以上、独占的*+
: 0 回以上、独占的{n}+
: 正確に n 回、独占的{n,}+
:n 回以上、独占的{n,m}+
: n 〜 m 回、独占的関連する概念として、 atomic-match の括弧、 (?>...)
があります。後のマッチが失敗し、エンジンがバックトラックを必要とする場合、アトミックマッチはそのまま維持されます:全体として成功または失敗します。次の二つの正規表現を比べてみてください。
str_detect("ABC", "(?>A|.B)C")
#> [1] FALSE
str_detect("ABC", "(?:A|.B)C")
#> [1] TRUE
アトミックマッチは A にマッチして失敗し、次の文字が C なので失敗します。正規のマッチは A にマッチするので成功しますが、C にマッチしないので、バックトラックして代わりに B を試行します。
これらのアサーションは、文字を「消費」することなく、現在のマッチの前方または後方を検索します (つまり、入力位置を変更します)。
(?=...)
: 肯定先読みアサーション。現在の入力で ...
にマッチする場合にマッチする。
(?!...)
: 否定先読みアサーション。現在の入力が ...
にマッチ しない 場合にマッチする。
(?<=...)
: 肯定後読みアサーション。現在の位置の直前の文字にマッチする場合。長さは制限されていなければなりません (つまり *
や +
は使えません)。
(?<!...)
: 否定後読みアサーション。現在の位置の直前の文字にマッチ しない 場合。長さは制限されている必要があります (つまり *
や +
はありません)。
これらは、あるパターンが存在することを確認したいが、そのパターンを結果に含めたくない場合に便利です。
<- c("1 piece", "2 pieces", "3")
x str_extract(x, "\\d+(?= pieces?)")
#> [1] "1" "2" NA
<- c("100", "$400")
y str_extract(y, "(?<=\\$)\\d+")
#> [1] NA "400"
正規表現にコメントを含めるには、2つの方法があります。一つ目は (?#...)
を使う方法です。
str_detect("xyz", "x(?#this is a comment)")
#> [1] TRUE
もう一つは、regex(comments = TRUE)
を使用する方法です。この形式では、スペースや改行は無視され、 #
以降はすべて無視されます。スペースにマッチさせるには、 "\\ "
とエスケープする必要があります。これは複雑な正規表現を記述するのに便利な方法です。
<- regex("
phone \\(? # 最初のカッコ
(\\d{3}) # 市外局番
[)- ]? # カッコ閉じ、あるいはハイフンまたは空白
(\\d{3}) # 数字3つ
[ -]? # 空白またはハイフン
(\\d{3}) # 数字3つ
", comments = TRUE)
str_match("514-791-8141", phone)
#> [,1] [,2] [,3] [,4]
#> [1,] "514-791-814" "514" "791" "814"