iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

めざせ☆全問正解 正規表現マスター!

はじめに

初めまして、こんにちは!
iimonに入社して5ヶ月目のなかむ〜です。
本記事はiimonアドベントカレンダー10日目の記事となります。

実務の中で正規表現を読み解くのに時間がかかったり、書き方で悩んだりしたので、今回は復習も兼ねて正規表現のレベル別に問題を10問作成しました。
答えはあくまでも書き方の一例なので色々試してみてくださいね。
解説も用意してあるので、挑戦して頂けると幸いです!

めざせ☆全問正解 正規表現マスター!

動作確認環境

https://paiza.io/ja/projects/new?language=javascript

対象

以下のNFAエンジンを使用している言語を想定しています。
JavaScript,Perl,PHP,Python,Ruby,vi,sed(殆どのバージョン)

出題形式

問題ごとに文字列と正規表現を書き換えてください。

const text = '対象の文字列';  
const regex = /正規表現/;  
console.log(text.match(regex));  

問題1

問題:全ての数字を取得してください。
文字列:12345 abcde 67890
期待値:['1', '2', '3', '4','5', '6', '7', '8','9', '0']

答え&解説

答え/\d/g

const text = '12345 abcde 67890';  
const regex = /\d/g;  
console.log(text.match(regex));  

解説
\d : 数字にマッチするメタ文字です。
gフラグ : グローバル検索を行うためのフラグで、このフラグを使用すると、文字列内のすべてのマッチする箇所を検索することができます。

問題2

問題:小文字のアルファベットのみを取得してください。
文字列Hello world This is JavaScript
期待値:['e', 'l', 'l', 'o', 'w','o', 'r', 'l', 'd', 'h','i', 's', 'i', 's', 'a','v', 'a', 'c', 'r', 'i','p', 't']

答え&解説

答え/[a-z]/g

const text = 'Hello world This is JavaScript';  
const regex = /[a-z]/g;  
console.log(text.match(regex));  

解説
[a-z] : 角括弧[]内に指定された範囲のいずれかの文字にマッチします。ここでは、a-zで小文字のアルファベット全てを指定しています。

問題3

問題:大文字で始まる単語のみを取得してください。
文字列:Sometimes it is Easy to spot Capital letters
期待値:[ 'Sometimes', 'Easy', 'Capital' ]

答え&解説

答え/[A-Z][a-z]*/g

const text = 'Sometimes it is Easy to spot Capital letters';  
const regex = /[A-Z][a-z]*/g;  
console.log(text.match(regex));  

解説
[A-Z] : 大文字のアルファベット1文字にマッチします。
[a-z]* : 小文字のアルファベットが0回以上続くパターンにマッチします。

問題4

問題:4桁の数字で始まり、その後にハイフンと3桁の数字が続く形式を取得してください。
文字列:1234-567 9876-543 12-3456 1234567
期待値:[ '1234-567', '9876-543' ]

答え&解説

答え/\d{4}-\d{3}/g

const text = '1234-567 9876-543 12-3456 1234567';  
const regex = /\d{4}-\d{3}/g;  
console.log(text.match(regex));  

解説
\d : [0-9] と同じ意味を持ちます。1つの数字にマッチします。
{4} : 正規表現の量指定子です。直前の要素が指定した回数繰り返されることを示します。

問題5

問題:「http」または「https」で始まるURLだけを取得してください。
文字列http://www.example.com https://secure.example.com ftp://files.example.com
期待値:[ 'http://www.example.com', 'https://secure.example.com' ]

答え&解説

答え/https?:\/\/\S+/g

const text = 'http://www.example.com https://secure.example.com ftp://files.example.com';  
const regex = /https?:\/\/\S+/g;  
console.log(text.match(regex));  

解説
https? : 「http」に続いて「s」が0回または1回出現するパターンにマッチします。
:\/\/ : コロンとエスケープした二つの/(スラッシュ)とマッチします。
\S+ : \Sは空白文字以外の任意の文字にマッチします。プラス記号(+)は、直前の文字が1回以上連続していることにマッチします。つまり、空白が現れるまでの一連の非空白文字列にマッチします。

問題6

問題正規表現を使用して、以下のルールに従うメールアドレスだけを取得してください。
1. メールアドレスは username@domain.com の形式であること。
2. username 部分は、英小文字、数字、ピリオド(.)、プラス(+)、アンダースコア(_)のみを含むことができる。
3. username の最初の文字は英小文字でなければならない。
4. domain 部分は、英小文字とピリオド(.)のみを含むことができる。
5. domain の最後は .com で終わる必要がある。
文字列example@domain.com, user@domain.co.jp, a.user+filter@sub.domain.com, Example@domain.com
期待値:[ 'example@domain.com', 'a.user+filter@sub.domain.com' ]

答え&解説

答え/\b[a-z][a-z0-9._+]*@[.a-z]+\.com/g

const text = 'example@domain.com a.user+filter@sub.domain.com user@domain.co.jp Example@domain.com';  
const regex = /\b[a-z][a-z0-9._+]*@[.a-z]+\.com/g;  
console.log(text.match(regex));  

解説
\b : 単語の境界を意味します。単語の先頭や末尾にマッチします。
[a-z] : 最初の文字が英小文字であることを確認します。
[a-z0-9._+]* : 英小文字、数字、ピリオド、プラス、アンダースコアが0回以上繰り返されることを許容します。
@ : メールアドレスのローカル部分とドメイン部分を区切る@記号にマッチします。
[.a-z]+ : ドメイン名が英小文字で始まり、ピリオドで区切られることを確認します(サブドメインを含むことができます)。

問題7

問題:拡張子が.jpgまたは.pngで終わるファイル名だけを抽出して取得してください。
文字列:image.jpg, picture.png, archive.zip
期待値:[ 'image.jpg', 'picture.png' ]

答え&解説

答え/\w+\.(jpg|png)/g

const text = 'image.jpg, picture.png, archive.zip';  
const regex = /\w+\.(jpg|png)/g;  
console.log(text.match(regex));  

解説
\w+ : \wは[a-zA-Z0-9_]と同義です。+は直前の文字(この場合は\w)が1回以上続くことを意味します。
\. : エスケープしたリテラルのドットとマッチします。
(jpg|png) : この部分は括弧内にあるいずれかの文字列にマッチします。ここでは、リテラルの文字列「jpg」または「png」です。パイプ記号(|)は「または」という意味です。

問題8

問題:括弧内のテキストだけを抽出して取得してください。
文字列:"This is (quite) a challenge!" he said.
期待値:[ 'quite' ]

答え&解説

答え/(?<=\()[^)]+(?=\))/g

const text = '"This is (quite) a challenge!" he said.';  
const regex = /(?<=\()[^)]+(?=\))/g;  
console.log(text.match(regex));  

解説
(?<=\() : これは肯定的後読みアサーションです。肯定的後読みアサーションは (?<=Y)X の形式をとります。ここで Y は直前に存在するべきパターンであり、X は実際にマッチさせたいパターンです。このアサーションは Y が存在した場合にのみ X にマッチしますが、マッチの結果に Y は含まれません。ここでは、(がエスケープされた開き括弧(を意味しています。

[^)]+ : [^...]は「除外する文字セット」を意味し、+は一回以上の繰り返しを意味しています。つまり、閉じ括弧が出現するまでの一つ以上の文字にマッチします。文字クラス([])内では、多くの正規表現エンジンにおいて、特定のメタ文字はエスケープする必要がなくなります。

(?=\)) : これは肯定的先読みアサーションです。肯定的先読みアサーションは (?=Y)X の形式をとります。ここで X は実際にマッチさせたいパターンであり、Y はその直後に存在するべきパターンです。このアサーションは X が存在し、かつその直後に Y が続く場合にのみ X にマッチします。しかし、マッチの結果には Y は含まれません。ここでは、\)がエスケープされた閉じ括弧)を意味しています。

問題9

問題IPv4アドレスだけを抽出して取得してください。
文字列:The server's IP is 192.168.1.1. The gateway is 10.0.0.1.
期待値:[ '192.168.1.1', '10.0.0.1' ]

答え&解説

答え/(?:\d{1,3}\.){3}\d{1,3}/g

const text = "The server's IP is 192.168.1.1. The gateway is 10.0.0.1.";  
const regex = /(?:\d{1,3}\.){3}\d{1,3}/g;  
console.log(text.match(regex));  

解説
(?:\d{1,3}\.) : これは非キャプチャグループ((?:...))です。非キャプチャグループとは、グループ化は行いますが、マッチした部分をキャプチャ(保存)しないものを指します。これにより、後からそのグループを参照する必要がない場合に、処理の効率化を図ることができます。
{3} : 直前の文字を3回繰り返すことを意味します。
\d{1,3} :\dは[0-9]と同義です。{1,3}は直前の文字(この場合は\d)が最少1回、最大3回繰り返すことを示します。

問題10

問題:最初と最後が同じ文字である単語だけを抽出して取得してください。ただし、単語は最低3文字以上です。
文字列:The level of this challenge is designed to entertain.
期待値:[ 'level', 'designed' ]

答え&解説

答え/\b(\w)(\w+)\1\b/g

const text = 'The level of this challenge is designed to entertain.';  
const regex = /\b(\w)(\w+)\1\b/g;  
console.log(text.match(regex));  

解説
この正規表現は以下のパターンにマッチする単語を抽出します。
\b : 単語の境界を意味します。単語の先頭や末尾にマッチします。
(\w) : \wは[a-zA-Z0-9_]と同義です。キャプチャグループで括弧 () を使って特定の部分式をグループ化し、その部分にマッチしたテキストを後で参照したり取得します。単語の最初の文字を捉えます。
(\w+) : 同じくキャプチャグループですが、こちらは \w が1回以上繰り返す(つまり、1文字以上のワード文字の連続)にマッチします。このグループは最初の文字に続く単語の残りの部分を捉えます。
\1 : 最初のキャプチャグループ (\w) でマッチした文字を参照するための後方参照です。これにより、単語の最初と最後の文字が同じであることを要求しています。

おわりに

お疲れ様でした!いかがでしたか?

正規表現は特定の文字列パターンを検索、抽出、置換する際に非常に有用なツールです。
個人的には先読み・後読みアサーションや後方参照の理解に時間がかかりましたが、何度も読むことによって少しずつ慣れてきました。
今後は'対象'で軽く書いた正規表現エンジンについてや、正規表現の効率性などについても学んでいきたいです。

皆さんの普段の開発に役立てていただけると幸いです。
読んでいただき、ありがとうございました!

株式会社iimonでは一緒にマスターをめざす仲間を探してます!
Wantedly
Green

次は私のメンターでマスター of マスターの林さんです!
よろしくお願いします!

参考

正規表現 一覧
詳説 正規表現 第3版
MDN JavaScript 正規表現