正規表現のメタ文字:^ $ . と末尾改行の罠、\dの違い

正規表現のメタ文字 . ^ $ | ? * + ( ) [ ] { } \ を整理。Multiline・Singleline の実際の出力、$ が末尾の改行の前で一致する罠、\d が全角数字に一致するかをエンジン別に実測。

2026年11月改訂: 2021年に韓国語で書いた講座を日本語で書き直しました。例はすべて .NET 10(System.Text.RegularExpressions)、Python 3.9・3.12 の re、PHP 8.5(PCRE2 10.47)、Dart 3.11(RegExpはJavaScriptと同じ文法)で実行し直しています。元記事の表には直すべき点がいくつもありました。\d・\w が [0-9]・[a-zA-Z0-9_] と同じという説明は .NET の既定動作と違い、\s の一覧には半角スペースが抜けていて、\x09 を「改行文字」と紹介していた箇所は正しくはタブです。元記事になかった ^・$・|・{ }、Multiline・Singleline、$ の末尾改行の罠を追加しました。

この記事は、正規表現で特別な意味を持つ記号(メタ文字)をまとめて把握したい人と、^\d+$ で入力チェックをしたのにおかしな値が通ってしまい原因を探している人に向けたものです。先に結論を書くと、メタ文字の意味はほとんどのエンジンで共通ですが、$ がどこに一致するか、\d がどの数字に一致するかといった境界の挙動はエンジンごとに違いました。入力チェックに使うパターンでは、この違いがそのままセキュリティ上の問題になります。

メタ文字14個の早見表

次の記号はパターンの中で文字どおりではなく特別な意味を持ちます。文字そのものを探すには前に \ を付けます(\.、\$、\()。ユーザー入力をまるごとエスケープする方法は単純な文字列検索にあります。

メタ文字 意味 詳しい記事
. 改行以外の任意の1文字 この記事
^ $ 文字列(または行)の先頭と末尾 この記事
| または(選択) この記事
? * + 0〜1回、0回以上、1回以上 量指定子
{ } 繰り返し回数 {2}、{2,5} 繰り返し文字
( ) グループ サブ式
[ ] 文字集合 [abc] 文字集合
\ エスケープ、\d などの特殊シーケンス この記事

| は優先順位がいちばん低いので、よく間違えます。^cat|dog$ は「^cat または dog$」という意味で、「cat か dog だけの行」ではありません。catalog、hotdog、dogma の3行に Multiline で適用すると、4エンジンとも catalog の cat と hotdog の dog を見つけました。意図どおりにするには ^(cat|dog)$ と括弧でくくる必要があり、そうすると dog だけの行にしか一致しませんでした。

量指定子の形をしていない { は、ほとんどのエンジンが文字として扱いました。x{a} は4エンジンとも文字どおり x{a} に一致しました。ところが a{,2} は分かれました。.NET と Dart は文字列 a{,2} とみなし、Python と PHP(PCRE2 10.47)は {0,2} の量指定子と解釈しました。Dart は unicode: true では Lone quantifier brackets エラーでした。文字の { を探すときは常に \{ と書くのが安全です。

エスケープシーケンスと2021年の例

2021年の表を実行結果に合わせて直しました。

シーケンス 意味 訂正点
\t \n \r \f タブ、改行(LF)、復帰(CR)、改ページ
\v 垂直タブ(U+000B) PHP(PCRE)では垂直方向の空白全体で \n にも一致
[\b] バックスペース(文字集合の中だけ) 集合の外の \b は単語境界
\xHH 16進コードの1文字(\x09 = タブ) 「16進数の数字に一致」ではない
\0、\012 8進コードの1文字(\0 = NUL、\012 = \n) 「8進数の数字に一致」ではない
\d \D 数字 / 数字以外 エンジンによりUnicodeの数字を含む(後述)
\w \W 単語文字 / それ以外 .NETの既定ではひらがな・漢字も含む
\s \S 空白類 / それ以外 元の一覧に半角スペースがなかった

\x の後ろには16進数がちょうど2桁必要でした。.NET は \x も \x4 も Insufficient or invalid hexadecimal digits、Python は incomplete escape \x4 というエラーで、Dart は既定のモードでは \x を文字 x として黙って受け入れました。\v はテキスト a\nb\x0Bc に対して .NET・Python・Dart が \x0B だけ、PHP は \n と \x0B の2つを見つけました。

2021年の例は、すべてのエンジンで元記事と同じ結果でした。

\[\d\]  テキスト [0] [a] [1][2] [b] [c] [3]      → [0], [1], [2], [3]
\[\W\]  テキスト [0] [-] [a] [B] [$][@] [3]      → [-], [$], [@]
\x09    テキスト Text1<タブ>A Text2<タブ>B Text3 → タブ2つ

[ と ] は文字集合を作るメタ文字なので \[、\] とエスケープしています。元記事は \x09 を「改行を意味する16進数」と紹介してからタブを探していましたが、0x09 はタブで、改行(LF)は \x0A です。

\d・\w・\s はエンジンごとに違う

2021年の表は \d を [0-9] としていましたが、同じ入力でもエンジンによって結果が違いました。入力は ascii 123 / arabic-indic ١٢٣ / fullwidth 123 / devanagari १२ です。

エンジン・モード \d+ の結果
.NET 10 既定 123、١٢٣、123、१२
.NET 10 RegexOptions.ECMAScript 123
Python 3.9 / 3.12(str パターン) 123、١٢٣、123、१२
Python re.ASCII 123
PHP 8.5 /u 123、١٢٣、123、१२
PHP 8.5(/u なし) 123
Dart(既定、unicode: true とも) 123

日本語の入力フォームでは全角数字が当たり前に入ってきます。.NET で ^\d+$ のチェックを通った 123 を int.Parse に渡すと、FormatException: The input string '123' was not in a correct format. になりました。チェックと変換の基準が食い違っているわけです(Python の int('123') は 123 を返しました)。ASCIIの数字だけを受け付けたいなら [0-9] と書くか、RegexOptions.ECMAScript(.NET)や re.ASCII(Python)を指定します。全角数字も受け付けたいなら、チェックの前に正規化(NFKC)してから [0-9] で検査するほうが、変換処理との食い違いが起きません。

\w と \s も同様でした。

入力 .NET 既定 .NET ECMAScript Python Python re.A PHP /u Dart
\w+ / hello 안녕 日本語 café_1 4つすべて hello、caf、_1 4つすべて hello、caf、_1 4つすべて hello、caf、_1
\s / 半角スペース・NBSP・全角スペース・タブ 4つ 半角・タブ 4つ 半角・タブ 4つ 4つ

最後の行に注目してください。Dart(JavaScript)は全角スペース(U+3000)を含むUnicodeの空白をすべて \s とみなしましたが、.NETの ECMAScript オプションは半角スペースとタブだけでした。「ECMAScriptオプション=JavaScriptと同じ動作」ではありません。範囲を自分で書く方法は文字集合で扱います。

^ $ と Multiline、. と Singleline

^ と $ は既定では文字列全体の先頭と末尾に一致し、Multiline を指定すると各行の先頭と末尾に一致します。. は既定では \n に一致せず、Singleline(Python の re.S、PHP の /s、Dart の dotAll)で一致するようになります。テキスト first line\nsecond line\nthird で確かめました(4エンジンとも同じ)。

パターン 指定なし Multiline Singleline 両方
^\w+ first first、second、third first first、second、third
\w+$ third line、line、third third line、line、third
first.*third なし なし 全体に一致 全体に一致

名前のせいで混乱しがちですが、2つは反対語ではありません。Multiline は ^・$ に、Singleline は . に作用するので、同時に指定できます。

. にはエンジン差が2つありました。1つ目は、.+ を ab\r\ncd に適用すると .NET・Python・PHP は ab\r のように \r まで含めたのに対し、Dart は ab だったこと。2つ目は、絵文字 😀 1文字に ^.$ を適用すると Python と PHP(/u)は一致したのに .NET は一致せず、^..$ のほうに一致したことです。.NET の . はUTF-16のコード単位1つに一致するからです。Dart は既定では .NET と同じ、unicode: true では Python と同じでした。

$ の罠:末尾の改行の前でも一致する

入力チェックでいちばんよくある罠です。^\d+$ で「数字だけ」をチェックすると、末尾に改行の付いた "123\n" も通ってしまいます。

パターン ^\d+$  入力 "123\n"
.NET 10     : 一致("123")
Python 3.9  : 一致
Python 3.12 : 一致
PHP 8.5     : 一致
Dart        : 不一致

.NET・Python・PHP の $ は「文字列の末尾」または「文字列末尾の改行の直前」に一致します。改行が2つの "123\n\n" はどのエンジンでも一致しませんでした。Webフォーム、ファイルから読んだ1行、HTTPヘッダーの値など、末尾に \n が残りやすい値をチェックするときに問題になります。

フローチャート。入力は 123 と改行で、^\d+$ で検査する。判断:どのエンジンか。.NET・Python・PHP では一致する。$ が末尾の改行の直前にも一致するからだ。JavaScript と Dart では一致しない。対策は .NET と PHP では \z、Python では \Z か re.fullmatch、PHP では D 修飾子。どれも入力を拒否する。
^\d+$ が "123\n" を通してしまう理由。.NET・Python・PHP では $ が末尾の改行の直前にも一致する。JavaScript(Dart で確認)は一致しない。本当の末尾に固定する書き方を使う。

本当の末尾に固定する方法はエンジンごとに違います。

エンジン ^\d+\z ^\d+\Z その他の方法
.NET 10 不一致(本当の末尾) 一致($ と同じ)
PHP 8.5 不一致 一致 /^\d+$/D → 不一致
Python 3.9 / 3.12 エラー bad escape \z 不一致(本当の末尾) re.fullmatch(r"\d+", s) → None
Dart 不一致 不一致 $ がもともと本当の末尾

Python の \Z は .NET・PHP の \z と同じ意味です。同じ記号がエンジンによって逆の動作をするので、パターンを移植するときは必ず確認してください。Dart は unicode: true では \z も \Z も Invalid identity escape エラーでした。

Windowsの改行(CRLF)にはもうひとつ罠があります。ab\r\ncd\r\n に Multiline の ^\w+$ を適用すると、.NET・Python・PHP は何も見つけませんでした。$ は \n の前で一致しますが、その手前に \r があって \w+ が届かないからです。Dart は \r も行末とみなして ab、cd を見つけ、PHP はパターンの先頭に (*ANYCRLF) を付けると ab、cd を見つけました。.NET では \r?$ と書くか、先に行単位に分割するのが確実です。

まとめ

状況 おすすめ
入力値全体をチェック .NET・PHP は ^…\z、Python は re.fullmatch、Dart は ^…$
ASCIIの数字だけ [0-9]、または .NET ECMAScript / Python re.ASCII
行ごとに処理 Multiline、CRLFなら \r?$
複数行にまたがる .* Singleline(re.S、/s、dotAll)
文字の {・.・$ \{、\.、\$
選択 | 範囲を ( ) でくくる

正規表現をそもそも使うべきかどうかは正規表現とは?に、繰り返しは量指定子と繰り返し文字に続きます。

確認環境:macOS(Apple Silicon Mac)、.NET 10.0.14、Python 3.9.6・3.12.14、PHP 8.5.6(PCRE2 10.47)、Dart 3.11.5、2026年11月。

よくある質問

^\d+$ で数字だけをチェックしたのに "123\n" が通るのはなぜ?

.NET・Python・PHP では $ が文字列の末尾だけでなく、末尾の改行の直前にも一致するからです。本当の末尾に固定するには、.NET・PHP は \z、Python は \Z か re.fullmatch、PHP は D 修飾子を使います。JavaScriptと同じ文法の Dart は最初から一致しませんでした。

\d は [0-9] と同じ?

エンジンによります。.NETの既定、Python 3の str パターン、PHPの /u モードでは、\d+ が全角数字 123、アラビア・インド数字 ١٢٣、デーヴァナーガリー数字にも一致しました。.NETの RegexOptions.ECMAScript、Pythonの re.ASCII、Dart ではASCIIの 123 だけでした。

Multiline と Singleline の違いは?

Multiline は ^ と $ を各行の先頭と末尾に一致させ、Singleline は . を改行 \n にも一致させます。別々のメタ文字に作用するので同時に指定でき、両方指定すると両方の効果が適用されました。

  • #正規表現
  • #メタ文字
  • #Regex
  • #Multiline
  • #.NET
  • #Python

t.laboratory

実際に作って運用しているアプリ・サーバー・サービスで起きた問題と、その解決に使ったコードを記録する技術ブログです。

運営者情報 · アプリ

↑