Help us understand the problem. What is going on with this article?

正規表現の利用入門

Hi all.
UFOです。

ここでは、私がいつの間にか使うようになっていた正規表現について、
「作業中のパターンマッチングの利用」に絞ってお話しする。
「grepするとき正規表現を知っていると、検索操作の利便性がダンチなのだ!」とかそういう話。

「作成しているプログラムの機能として、正規表現を利用する」こととは違った話になるが、
そういう機能を作るときにも、部分的に参考になるようなものにしたいね。


正規表現の「表現一覧」とか「チートシート」の類ではないのでご注意を。

そういうのが欲しい人には、僕がいつも参照してる
正規表現の基本 wrote by @sea_ship 」をおいておく。
記事中でもちょくちょく引用する。

チートシートは、webで検索すればいろいろでてくる。
Qiita にも何件か記事があるが、自分で使うときの用途に合わせて、組み直したほうが良い場合もあると思う。

https://regexper.com」 で、作成した正規表現に対応した図を生成してくれる。

導入 -正規表現とは-

英語では「Regular Expression」と呼ばれる。
「Regex」と省略されることもあり、海外の人もこの表記を使うようだ。1

安直に素直にそのまま翻訳すれば「正規表現」になる。
『正規』という言葉は聞き慣れないが、「規則などではっきりきまっていること。」(weblio辞書より)だそうだ。

「規則」については、先にも紹介した「正規表現の基本」などの記事で紹介されている。

使い所を事前に例示しておくと、

冒頭でも述べたが、grep(テキストの検索)などで役立つ。

よくある便利どころでは、
例えば「key」と「keyBoard」と「monkey」という3つの変数名が使われているときに、
変数名が「key」のものだけ探したいとき。
ひとつの解決策としては、前後にスペースを入れた「 key 」を検索することだが、
これでは、関数の引数や配列のインデックスに指定されている場合(function(key))などは検索できない場合があるし、
行末が「key」である場合もヒットしない。

まあ、今ではIDEの機能で「その変数を指して利用しているキーワード」を探し出してくれたりするので、
あんまりいらないのかもしれないが。


安直なWikipediaの利用は止め あーーー!!(wikipedia - 正規表現)

その後正規表現はテキストエディタ、ワードプロセッサなどのアプリケーションで(ないし、そもそもそれ以前に単機能の文字列探索ツールの)、マッチさせるべき対象を表すために使用されるようになり、表せるパターンの種類を増やすために本来の正規表現にはないさまざまな記法が新たに付け加えられた。このような拡張された正規表現には正規言語ではない文字列も表せるものも多く、ゆえに正規表現という名前は実態に即していない面もあるが、伝統的に正規表現と呼ばれ続けている。

いったん注釈

#1
今後この記事で、『「正規表現regex」は、「文字列string」に、マッチする』という言葉を、
『regex が string を表現する』ことを指すものとして使う。

「マッチしない」というときは、
『regex は string を表現していない』ことを指す。

#2
正規表現の規則の説明のために、次のような書き方をする。

「正規表現の記述」がどんな規則を表現するかの説明
正規表現の記述

説明内容は、イメージとして読んでいただきたい。
この説明を正確にすることが目的ではないし、
それこそwikipediaなどを参照するべき事項だからだ。
(できる限り正確に読み取れるようにする努力はする)

同じ理由で、正規表現で使われる記号などの規格の解説は「きちんと」はしない。
が、具体例を出す中で軽く説明する。

#3
紹介している各正規表現は、一応、 Visual Studio Code (1.41.1 + userSetup)で動作検証をしているが、
正規表現自体の規格が複数あり、多少の揺れがあるので、
例えば IntelliJ IDEA で検証すると異なった結果になる場合がある。2

僕なりの説明

「文字列を表現する」文字列で、
一個の正規表現で「複数種類の文字列」を表現でき、
かつ「表現したくない文字列」を無視する正規表現が作れる。
また、一部の拡張機能を使うことで、「普通は表現できない文字列を表現できる」

さらには、そのようにして表現した文字列に対して、
それぞれの内容の一部を保持したまま置換することができる。

「文字列を表現する」文字列

というのは、そのままの意味で、
例えば「pay」にマッチする正規表現には

文字「p」の直後に文字「a」が、その直後に文字「y」がある文字列にマッチ
pay

がある。
正規表現で表した「pay」は、文字列「pay」にヒットする。

が、この表現は「pay」にしかマッチしない。「say」や「paid」にはマッチしてくれない。(当然だが)
また、すべての「pay」にマッチしてしまう。「payang」は「pay」を含むので、この「pay」の部分にマッチしてしまう。


ところで、

文字「p」の直後に文字「a」が、その直後に文字「y」がある文字列

というのは、まどろっこしいので、こういうものを今後

文字列「pay」

と表記する。

一個の正規表現で「複数種類の文字列」を表現できる

例えば、「pay」と「say」と「paid」にマッチする正規表現を作れる。
以下のような例がある。

文字列「pay」または文字列「say」または文字列「paid」にマッチ
pay|say|paid

|」はよくある「or」、「または」を意味するそれで、
「前後の正規表現のどちらかにマッチする」文字列にマッチする。

文字「p」または文字「s」の直後に文字列「ay」がある文字列、または文字列「said」にマッチ
[ps]ay|said

[]」は、中にあるどれかの文字にマッチする。順番は無視される([jet][ejt]と同じ。)
範囲指定ができる。([0123456789][0-9]と同じ。[abcABCDE][a-cA-E]と同じ。)
否定もできる。(後述)


任意の文字(.)の直後に文字「a」が、その直後に文字「y」または文字列「id」がある文字列にマッチ
.a(y|id)

.」は、任意の文字(1文字)にマッチする。スペースや記号も文字なので、これらにもマッチする。
行頭や行末、改行を回避したいときや、「任意文字列」を表現する場合に使うことができる。

()」は、正規表現を区切ることができる。
.ay|id任意の文字の直後に文字列「ay」がある文字列、または文字列「id」にマッチする。
これでは、「paid」にはマッチしない。(「paid」の「id」の部分にはマッチする。)
しかも、「kid」などの、「id」が含まれる文字列の「id」の部分にマッチしてしまう。

.a(y|id)ならば、「paid」にマッチするし、「kid」は「aの直後に「y」または「id」がある」文字列ではないのでマッチしない。

ただ、これは「said」などにもマッチしてしまう。
1文字目を「[p|s]」から「.」に変えたことで、「raid」や「ray」などにもマッチするようになった。
先述のように、「.」はスペースなどあらゆる文字にマッチするので、「 aid」や「おray」にもマッチする。
.」を使うとすげえいろんな表現にマッチさせられるけど、範囲が広くなることが弊害となる場合もある。

「表現したくない文字列」を無視する正規表現ができる

少し内容を先取りしてしまったが、説明を追加する。

先述の通り、「pay」を使って検索をすると、次のような不便を感じる場合がある。

すべての「pay」にマッチしてしまう。「payang」は「pay」を含むので、この「pay」の部分にマッチしてしまう。

「pay」だけど「payang」ではない文字列にヒットしたい場合は、例えば次のように表現する。

文字列「pay」の直後に、a以外の文字があるような文字列
pay[^a]

これならば、「payang」にはマッチしない。
しかし、「paypal」にはマッチしてしまう。

文字を範囲指定することもできた。

文字列「pay」の直後に、アルファベットと数字以外の文字があるような文字列
pay[^a-zA-Z0-9]

しかし、「pay-easy」にはマッチしてしまう。

これでは埒が明かないし、
このまま続けたとしても、「~~以外の文字」がある文字列にマッチするので、
行末にある「pay」にはマッチしてくれない。

そこで、

普通は表現できない文字列を表現できる

先に紹介した「正規表現の基本」で、境界正規表現エンジン の項で紹介されている。
「非単語境界」から下は私は使ったことがない。(使おうと思ったことがない。便利かもしれないけどわかんにゃい)
が、「行頭^」「行末$」「単語境界\b」は死ぬほど使い勝手が良い。

例えば、先に書いた

行末にある「pay」にはマッチしてくれない。

については次のように書けばマッチしてくれる。

正規表現「pay」にマッチして、その直後は行末であるような文字列
pay$

でもでも、これじゃ行末の「pay」にしかマッチしない。

行末以外の「pay」にもマッチして欲しいし、
焼きそばだのオンライン決済サービスだのネットバンキングだのにはマッチして欲しくない!

そんな人には「単語境界」だ!

文字列「pay」の直後に、「単語構成文字」が使われていない文字列
pay\b

これならば行末の「pay」にもどこかの「pay」にもマッチするが、「payang」や「paypal」の「pay」にはマッチしない。

...残念なことに「pay-easy」の「pay」にはマッチしてしまう。
「-」は「単語構成文字」ではないからだ。これは多くの場合、引き算だのの演算子に使われている。

(逆に言えば、変数「pay」を使った演算「pay-easy」を検出することは可能ということでもある。
まあ、コード規約とかIDEの自動フォーマット機能で、演算子の前後にはスペースが入れられることが多々あるっちゃあるんですがね。)

『「単語構成文字」ってなんだよ(威圧感)』という人もいるだろう。
先に紹介した「正規表現の基本」で、定義済みの文字クラス の項で紹介されている。(この辺も便利)
\w := [a-zA-Z_0-9]
ところで、この「単語構成文字列」には「日本語の文字」も含まれていないので、「payぽー」の「pay」にもマッチしてしまう。

ところで、この「pay\b」、「某電子マネー」の文字列の一部にはマッチするとおわかりいただけるだろうか。
そう。「paypay」の「pay」の部分にはマッチするのである。
どのように書けばこれにマッチしないかも、おわかりいただけるだろうか。(答えは注釈3へ!)


ところで、「paypal」の「pay」の部分にはなどと書いていたが、
逆に、「~~pay」で説明できるすべての単語にマッチさせたいときはどうするのか。

「~~」の部分が何文字かは把握することができない。

ここで、「数量子」というものが便利になる。
先に紹介した「正規表現の基本」で以下略

最長とか最短とか強欲な壺とかの違いや、その使い方は、以下に詳しい。
最長一致数量子/最短一致数量子/強欲な数量子 wrote by buzzword (伊倉 辰雄)

※この「最長/最短/強欲」はきちんと使いこなせていないので、ちゃんとした説明になってないです。
数量子自体は「最長」でよく使ってます

文字Cが3個連続している文字列、すなわちCCC
C{3}
CCC

「CCCCCCdCCCCd」
なら以下の『』の部分にマッチ
「『CCC』『CCC』d『CCC』Cd」

「文字Cまたは文字d」が3個以上連続している文字列の直後にdがある、長いほうの文字列
[Cd]*d

「CCCCCCdCCCCd」
なら以下の『』の部分にマッチ
「『CCCCCCdCCCCd』」

「文字Cまたは文字d」が3個以上連続している文字列の直後にdがある、短いほうの文字列
[Cd]*?d

「CCCCCCdCCCCd」
なら以下の『』の部分にマッチ
「『CCCCCCd』『CCCCd』」

文字Cが0個以上連続している文字列
C{0,}
C*
文字Cが1個以上連続している文字列
C{1,}
C+

先に紹介した単語構成文字列を示す文字クラス「\w」を使って、

単語構成文字で構成された文字列
\w+

とすればすべての単語を表現することもできる。
ただ、\wは数字にもマッチしてしまうので、
数字だけでリテラルが成立する表現(よく見るのは10進数の整数のリテラル)にもマッチする。
数字始まりの変数名などが定義できない制約があれば、
[a-zA-Z_]\w*とすれば、「すべての整数リテラル以外の単語」にマッチする。
(整数リテラルを単語と呼ぶかはちょいと疑問だが)

ちょっと脱線したが、

単語構成文字が0文字以上連続した文字列の直後に文字列「pay」があり、その直後は単語境界であるような文字列
\w*pay\b

で、「paypay」を含む「pay」で終わる単語を表現できる。
単語「pay」自体にはマッチしたくないなら「\w+pay\b」とすることで実現できる。

箸休め - エスケープ文字 -

ここまでで登場した「|[]().$^$\{}*+」の記号は、
それぞれ正規表現の中では特別な意味を持たされてしまっている。

よって、文字列中のこれらの文字を表現するためにエスケープが必要となる。
よくある「\」を先につける方法でエスケープできる。

また、「\」を使ったエスケープ文字として「\w」「\b」も登場した。
例の記事の文字の項には、その他にも
「改行\n」「タブ文字\t」や、その他の文字の表現方法が記載されている。
定義済みの文字クラスの項にも、単語構成文字のほかに「空白文字\s」や、「数字\d」や、
それらの逆(「単語構成文字以外\W」など)が記載されている。

\n」や数量指定を駆使すれば、複数行に渡った文字列にマッチする正規表現を行うこともできる。

「3行以上何も記述されていない行が連続するところ」には、次の正規表現がマッチする

「行頭から空白文字が0文字以上の直後に改行文字」な文字列が3回以上連続する文字列
(^\s*\n){3,}

ちなみに、「空白文字\s」には、少なくともVisual Studio Codeでは、全角の空白文字も含まれる。
※環境にもよるかもしれない。

エスケープすべき文字かどうかわからない場合は、
VSCodeで、検索窓の正規表現オプションをオンにしてから、検索窓をEscキーなどで閉じ、
エディタ部分でエスケープが必要かわからない文字(を含む文字列)を範囲選択した状態で検索窓を開くと、
エスケープされた状態で検索窓に入力される。
エスケープしてほしくない文字も容赦なくエスケープされるので、範囲指定は慎重に行う必要がある。

ヘビーなおかず - 否定的先読みなどなど -

「特定の文字を含まない文字列」にマッチする正規表現を作るのは割と簡単だが、
「特定の文字"列"を含まない文字列」にマッチする正規表現を作るのは、ちょいとめんどう。
というより、そのための仕組を僕がちゃんと理解できていない。

正規表現で文字列を含まない、否定の記述 wrote by UX MILK
正規表現の先読み・後読みを極める! wrote by Takeshi Arabiki (a_bicky)
こんどこそわかる(肯|否)定(先|後)読み wrote by @tohta
[コラム] 正規表現の先読み/後読みは、どう考えても名前が悪いので、呼称禁止令を出してルックと気軽に呼んでみませんか。 wrote by @mochizukikotaro
などで紹介されている、「否定的(先|後)読み」という機能を使う必要がある。

また、この機能での検索をするための条件があるようだ。(よくわかってない)
VSCodeでは、settingsに"search.usePCRE2": trueを追加する必要があるとか。(よくわかってない)

(?!文字列str)の書式で、<『「文字列str」が表す正規表現にマッチする文字列』にマッチしない文字列>にマッチするようだ。
上の正規表現を使って検索した場合、
「文字列str」の
文字列str」にはマッチしないが、
「文字列str」にはマッチする。

「行の中に」という条件で書かないと、「それを含まない文字列」という書き方にはなってくれない。
すなわち、

^(?!.*文字列str).*$

のように書く必要がある。

まれに使いたいときがあるけれど、そうそう使わないのであまり気にしていない。

正規表現を使った置換

正直説明がめんどくなってきた(好きでやってるんですがね)

ここまで説明したような方法でマッチした文字列に対して、
その内容をそれぞれにおいて一部流用しつつ置換を行えるというのは、
「特定の条件を満たす文字列を検索して、その前後にナニかをつける」とか
「前後についているナニかを取り除く」とかもできるということ。

パンはパンでも食べられないパンはパンツ

さて、あなたのテキストファイル中のすべてのパンをパンツにしようか。
ByTheWay, now I try to convert all breads in your text to pants.

1.パンを検索する。ただしパンツはかからないようにする。
「パン」を「パンツ」に単純に変換すると、「パンツ」が「パンツツ」になってしまう。

また、できれば単語境界が取りたい。
ただ、日本語の文章で分節毎に半角スペースを入れているお行儀の良い人は僕くらいのものだと思うので、
てきとうにカタカナにヒットしない条件とかにしてみる

文字列「パン」の直後にカタカナ以外一文字または行末
パン([^ア-ン]|$)

行末のパンにもパンツになっていただく。慈悲はない。
あと、フライパンとかが巻き添え食うけどシラネ

2.置換文字列を決める

パンツ

にするとこまったことが起こる。
「パン食べたい人」を置換すると「パンツべたい人」になるんですよね。なにそれどういう人なの
なぜって、置換対象の文字列はあくまで「検索に使った正規表現にマッチする文字列」なので、
「カタカナ以外1文字」の部分が巻き添え食って「ツ」に置換される。

正規表現を使った置換では、「()」で囲われた文字列を再利用(置換後の文字列で使用)できる。
windowsのコマンドライン引数みたく、「$1」とか「$2」とかでn番目指定する。(1始まり)
10番目以降が指定できるかはシラネ。(だめだとすれば「$[1-9]」だね)

今回は、ちょうどうしろのほうで「()」を使っている。n番目指定は1だ。
これを使って、

「パンツの直後に一個目の()の中を付け足した文字列」に置換
パンツ$1

これで無事、「パン食べたい人」を「パンツ食べたい人」に置換できる。
ただの腹ペコが、痴漢か痴女になっちゃったね。(渾身)

見出しにも変わってもらおう

  • パンはパンでも食べられないパンはパンツ
  • パンツはパンツでも食べられないパンツはパンツ

うん。まるで食べられるパンツがあるみたいだね。ふっしぎー

特定のxmlタグに囲まれた内容を保持したまま、タグ名だけを変更する

こちら、本気でキチンと書くととてつもなくめんどくさいです。
改行なしでなら以下のようなのでいけます。

Find
<hoge[^>]*>(.*)</hoge>
Replace
<fuga class="hehe">$1</fuga>

最長数量子を指定してるので、hoge入れ子だと一番外側が置換される。
(.*?)とすれば、一番内側のにマッチ。
もともとのxmlが間違ってたりするとよりおかしなことになる。

まあそもそも、xmlを文字列ベースでどうこするのをやめなさいとか言われそうだけど。(DOM使おうね)

行コメントを削除する

こちら、置換というか削除なんで、置換後の文字列は入れなくても良かったり、あ、しますか?

行コメントといっても、
「行の途中から行コメント」とか
「プログラム上で使う文字列リテラルに行コメントの開始文字列と同じ文字列が入っている」パターンとか
もある。
例えばjavaで、

??.java
// 行コメント
String str = "STRRRRRRRRRRR!//" // こっからコメント;
           + "//ぶひいいいいい" // コメントの中に// またかいちゃった
; // デリミタ

とか。(デリミタだけ改行先に書くバカお上品な人はそうそういらっしゃらないと思うが。)

丁寧にコメント部分だけを指す正規表現も作れると思うが、
めんどくさければ行全部コメントのところだけにマッチさせれば良い。

コメント行の前に空白文字列のみの行があればそれらの行にもマッチ
^\s*//.*\n
(^\s*//.*\n)+
コメント行の前の空白行にはマッチしないが、改行が含まれないため行が残る
^\s*//.*$
コメント行の前の空白行にはマッチしない。[半角スペースタブ全角スペース]
(^[ \t ]*//.*\n)+

IDEAだと顕著だが、ヒット件数が多すぎるととっても重たくなる場合があるので、
「一回以上連続」指定の「+」が後ろについているものを使うほうが良いかも。

また、「なんでそうなんの?」案件なのだが、コメント行の直前まで続く空白行まで含めてマッチしちゃう書き方がある。
最後の書き方でそういう空白行が含まれないところから見るに、
\n」がない(複数行にまたがない)検索をした場合には「\s」に改行文字が含まれないけど、
\n」が入った検索をした場合には「\s」に改行文字が含まれるっぽい

注意書き

最後に書くことではないような気もするが、ここまで読んでくれた人に、正規表現を使おうと思い立った時に気をつけたほうがよさそうなことをいくつか。

ソースコードの中では極力使わない。

「正規表現で表現できる文字列」のデータから、なんかデータを取り出すために、「正規表現のパターンにマッチする部分を取り出す」とかをしない。
JSonからデータ取り出すんなら「NAME\s*\:\s*(VALUE:[^,]*)」みたいなの書かずにJSonハンドラを使いましょうね、というような話である。(ちなみに先の例は全くアテにならない。)
xmlデータを扱うのは文字列操作じゃなくDOMで行おうね、というような。

これらはそれなりに学習コスト高めだけれど、規格を作る人達の作った規格を扱う道具は、正しく使ったほうがいいし、それが間違えてたら、規格を作っている人らに訴えかければ良いので。(規格を作る人達は、正規表現使ってお道具作ってると思いますが。(JSon公式も正規表現で説明されてる)ありがとうございます。)

正規化(規格化)されているハズの文字列の中から特定の情報を抜き出す・操作するみたいなことが必要な場合でも、その規格が移り変わる可能性とか、その文字列を作るコードとかがどれだけ正確か、信用しきれないことを思うと、僕はそれをやるべきじゃないと思う。そんな状態でデータのやり取りをするな。コメント欄じゃねーんだから。
(既存コードがそれなんだよねってことでしかたなくやったことあるけど、すっげー後味悪かったし、正しく動く自信はないし、今動いててもいつか改修あったときにデバッグする人かっわいそ)

置換するときは慎重に。

変数名の変更などで「\bname\baltName」をした時、既に「altName」の命名のモノがあったら...
.Col=3のマジックナンバー「3」を定数名に置換するために、「\b3\bCOL_NAME」をした時、文字列定数に「"3"」があったら...

みたいな、正規表現だけで解決しようとするとめんどくさい問題というのは、潜在的に(見つけにくいし、確実にあるとは言えないが)存在している。
問題がなければその置換をしてもいいが、一斉置換したい場合、これを一件一件視認しながら行うのは面倒で、ミスを生みやすい。
grep置換に関しては、エディタによってはundoができないので、バックアップをとらなければ、最悪復元できなくなり、詰む。

取れる対策として、

    • チェックしてから置換する。
      先に置換に使う検索用正規表現で変更箇所をチェックして、それを置換文字列に置換したらどんな文字列になるか確認してから置換する。(VSCodeやJetBrains系エディタなど、近年のエディタには、grep置換の結果をプレビューする機能がある場合もある)
      差分が確認ができるツールがあれば、差分を見て判断すればいいので、あとでチェックしても良い。
    • バックアップを取る。
      復元できるようにしておく。
    • 差分が確認できる状態にしておく。
      gitみたいな分散バージョン管理なら、「コミット→置換→差分を確認→置換の旨をコメントにしてコミット」みたいな手順を踏めば(コミットが増える汚さはあるけど)確実。

検索で使う時も2番手程度で考える

下策を持って上首尾に至ったなら、上策から始めるよりも数段勝る偉業ではないか!

...と、どこかのアニメ作品でイスカンダル王がおっしゃられている。
のもあるし、正規表現というのはちと頭を使うので、時間をちょっとかけることになる。
単純検索でヒットしすぎてしんどい、ってなって初めて選択肢として正規表現があがるだろう。(まあ僕は検索機能をほぼ常時正規表現モードにしてて、カッコとか検索するときに初めて「あっれなんかエラー出てる」ってなるんですけどね)

また、もしも「この単語と同等のものを指すワード」みたいなのを探したいなら、IDEであればサポートしていることがある「参照をすべて表示」とか、「定義に移動」みたいな機能を使用するほうが速い場合もある。(find usage とか、Go to Declaration とか)

正規表現でできることがわかっていて、すぐ実行できるならすぐやっちゃう

二番手ではあるんだけど、たとえば似たような変数名が多かったり、
「AnyName」「haveAnyName」「deleteAnyName」「AnyNameFlag」とかいうクソ残念な命名が多用されているなら、
\bを使って即座に検索して脳汁出してもいいと思う。

こういう検索は、してみるまでどうなるかわからなかったりするので、
僕の場合は常時正規表現をオンにして、単純検索してから、「うっわうじゃうじゃ出てきた\b使お」って感じで切り替えたりすることもある。

時短のためのお道具なのだからうまく使おうというお話である。
時短にならないならよしたほうがいいし、時短になるなら積極的に使おう。4

組み合わせ検索には割と強い

「この関数にこの変数を渡していることはあるのか」とか、
「関数で、この名前のものの定義のある場所」とか、
「この変数に代入している箇所」とか、
こういったものの検索にはかなり強い。
特に、ワードや記号間のスペースを無視する言語が多い現状で、「name=」と「name =」を同時に検索できる(\bname\s*=)、とかは、割と便利。

ある問題を解決するために、正規表現を使うことを考える時、考慮すべき問題が2つ増える

Jamie Zawinski さん(NetScapeの開発者の一、らしい)の発言。詳しく解説できるほど読み込んでいない。
ひとつの問題としては、表現したいと思う事柄を、正確表現していることを保証できる正規表現というものは、作るのが非常に難しいし、複雑な記述になる。また、これがソースコード上で利用されている場合、後で別の人がそれを見返そうと思ったとき、それが正しいかどうかを検証する手間が発生するし、正規表現そのものを知っていることを強要することになる。
これが10文字以内で、難しい表現を使わないなら、まあ調べて頑張れば読めると思うけれど、複雑に入り組んだものになると、読む側にもそれなりのコストを要求することになる(yyyy-MM-ddをある程度丁寧に表現するだけでも、((19|2\d)\d{2}-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])とかになる。しかもこれは02/31を許す。)

参考

具体例

  • 正規表現とは? wrote by @soarflat

    • Qiitaの記事。こっちの記事は簡潔で、本記事よりやさしい。具体例がいくつか書かれている。
  • 個人的★正規表現チートシート wrote by @UFOnian (僕)

    • 僕的ソースリーディングで使ったりしてる正規表現集。ぶっちゃけただのメモ。読んでるソースコードがどれだけ信用できるかによって、空白文字を入れるかどうかとか変わるので、あまりアテにしないでほしい。

入門

ツール。全部web上動作

  • Online regex tester and debugger created by Regex101
    • 入力された正規表現に対して、どんな文字列がマッチするか確認できる。
  • regexper created by Jeff Avallone
    • 入力された正規表現に対応した図を生成してくれる。

  1. Regex Search - FireFox ADD-ONSなど 

  2. 「規格が複数ある」わけではなく、「指定しないときのデフォルト値が異なる」というべきかもしれない。例えば、VSCodeの検索窓には、正規表現オプション([.*]ボタン)とは別に大文字小文字を区別する/しないを指定するボタン([Aa]ボタン)があり、デフォルトはオフだ。また、正規表現側の機能で、大文字小文字の区別の有無を指定するオプションも存在する。(「/exp/i」や「(?i:exp)」(Microsoftの.Net系で使われる)など) 

  3. \bpay\bとすれば、単語「pay」のみにマッチしてくれる。もちろん、「pay」で終わる単語にマッチするために「pay\b」を使うことも有用。「pay」で始まる単語なら「\bpay」。 

  4. あまりこういうことは言いたくないが、覚えるのめんどいし慣れるまではめっちゃ頭使うし時間もかかる。慣れれば早くなる。こういう、初期コストでかい効率Addする性質のものを、広く使おうという動きはあまり促進されないように思う。最初に3時間かけてその後の100時間のうち30時間費やすはずのものが15時間になったら12%節約なんすよ、初期の進捗は遅れになるけど(僕)。さらに言えば、正規表現は言語に依存しないテクなので、(マスターしようとするとべらぼうに時間かかるけど)最初の一回である程度理解しとけばその後どこでも使えるので、「3時間」をかけなくてよくなります。こういう汎用スキルはもっとくと後がラクだと僕は思います。 

UFOnian
学習期間2年・実践2年目の初心者 [C(C++):初歩(クラス除く),Java・C#:初歩(クラス込み),VB Family:業務でそこそこ,PHP・CSS・HTML・JavaScript:味見,正規表現:少し使う] 趣味的に楽しんだことの雑記とかブログみたいのを書き散らす。 ツイッターはちょー雑多なのでエンジニアクラスタにフォローされてもお役にたたない。問題があればリプとかDMください。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away