/XXXXX[\s\S]*?<td[^>]*>\s*(?:¥)?([\d,]+)\s*<\/td>/
XXXXXは項目名を伏せているが、上記の正規表現を見てぱっと意味がわかるだろうか?
あるHTMLメールのDOMから特定の金額を抽出する正規表現です。
この正規表現について、あまり意味がわからなかった部分に焦点をあてて紐どいてみたいと思います。
(ぱっとみて分かった正規表現部分)
XXXXX 該当ラベルを特定
([\d,]+) 数字とカンマをマッチ(例:1,000)
[\s\S]*? の意味と貪欲・非貪欲マッチ
意味
-
[\s\S]:空白文字と非空白文字 → すべての文字にマッチ -
*:貪欲マッチ(最大限でマッチ) -
*?:非貪欲マッチ(最小限でマッチ)
→ [\s\S]*? は「改行を含む任意の文字列を、できるだけ短くマッチさせる」という意味。
→ 改行をまたいだHTMLからマッチさせたいときに便利。
なぜ . ではだめ?
- JavaScriptの
.は 改行にマッチしない - なので、改行を含むHTMLなどを扱うときは
[\s\S]が必要
例
<p>1段落</p><p>2段落</p>
貪欲(greedy):<p>.*</p>
できるだけたくさんマッチする
マッチ結果: <p>1段落</p><p>2段落</p>
→ 最初の <p> から 最後の </p> までごっそり取ってしまう(貪欲)
非貪欲(lazy):<p>.*?</p>
できるだけ少なくマッチする
マッチ結果: <p>1段落</p>
→ 最初の <p> から 最初に見つかった </p> で止まる(非貪欲)
<td>タグの開始部分をマッチ <td[^>]*>
| 部分 | 意味 |
|---|---|
<td |
文字列として <td にマッチ(=開始タグ) |
[^>]* |
> 以外の文字を0回以上マッチ(=属性やスペースを許容) |
> |
> で閉じるタグ終端 |
下記のようなtdタグに属性があってもマッチできるようにする正規表現
<td class="cell" style="padding:12px;">test</td>
正規表現: <td[^>]*> にマッチする部分は?
<td class="cell" style="padding:12px;">
→ > に到達するまでのすべての文字が含まれる!
\s* の意味
| 記号 | 意味 |
|---|---|
\s |
「空白文字」:スペース、タブ、改行(\n, \r)など |
* |
直前の要素を 0回以上 繰り返し |
\s* |
空白・改行を いくつでも(0回でも)許容する |
\s*はタグの前後にある改行や空白に対応するための正規表現
なぜ <td> の直後に \s* を入れるのか?
HTMLは整形の都合で、以下のようにタグの中身が改行やインデントを含むことがよくあります
<td>
¥1,000
</td>
このとき、<td>タグの直後に改行やスペースが入っているため、
正規表現でこの部分を正しくマッチさせるには、空白や改行を許容する必要があるため
(?:¥)?の意味とキャプチャグループ
| 部分 | 意味 |
|---|---|
(?:...) |
非キャプチャグループ(グループ化するが、後から取り出さない) |
¥ |
文字列 ¥ に一致(¥ のHTMLエンティティ) |
? |
直前の要素があってもなくてもよい(0回または1回) |
→ 「¥ が あってもなくてもよい。でも、後で取り出す必要はないので、キャプチャ(取り出し)しない」
キャプチャ(取り出す) と 非キャプチャ(取り出さない)の違い
| パターン | 説明 | マッチ結果例(JSの .match()) |
|---|---|---|
(¥)?([\d,]+) |
¥ もキャプチャする |
["¥1,000", "¥", "1,000"] |
(?:¥)?([\d,]+) |
¥ はキャプチャしない |
["¥1,000", "1,000"] |