11
7

More than 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

PHPの正規表現のuフラグっていつ使うの?

Last updated at Posted at 2023-07-21

PHPのpreg_*系関数で使う正規表現は、区切り文字の後ろにいろいろなフラグを付けることができます。
その中で私が用途をすぐ理解できなかったuフラグについて書きます。

tl;dr

  • uフラグなしだと、マルチバイト文字のマッチが思った通りにできない可能性がある
  • uフラグによって\d\wの挙動が変わる

注:以降の記述では、扱う文字列はすべてUTF-8であることを前提とします。

preg正規表現のuフラグ

なにはともあれ公式マニュアルを確認します。

u (PCRE_UTF8)
この修正子は、Perl 非互換な PCRE の機能を有効にします。パターンと対象文字列は、 UTF-8 として処理されます。 無効な対象文字列を preg_* 関数に渡しても、何もマッチしません。 無効なパターンを渡すと、E_WARNING レベルのエラーが発生します。 5オクテットおよび6オクテットの UTF-8 シーケンスは無効とみなされます。

👨「つまりマルチバイト文字を含む正規表現にはuをつけなきゃいけないんだな」
👨「uフラグ完全に理解した」

uフラグがなくても動く??

では実際に動かしてみます。

<?php

function test(string $regexp, string $subject)
{
    preg_match($regexp, $subject, $match);
    var_dump($match[0]);
}

test('/^あ/', 'あいう'); // uなし、マッチしない?
test('/^あ/u', 'あいう'); // uあり、マッチするはず

実行結果

string(3) "あ"
string(3) "あ"

👨「???」
👨「uフラグなんもわからん」

正しく動かないパターン

uフラグはあってもなくても一緒?と思うのはまだ早いです。

test('/^[あい]/', 'あいう');
test('/^[あい]/u', 'あいう');

実行結果

string(1) ""
string(3) "あ"

解説

PHPの文字列というのは、実際には文字列というよりバイト列です。
なので、バイナリファイルもfile_get_contentsでそのまま文字列(バイト列)として読み込むことができます。

"あ"という文字列は、PHPインタプリタでは"\xE3\x81\x82"というバイト列として認識します。
/^[あい]/という正規表現は実際には/^[\xE3\x81\x82\xE3\x81\x84]/、つまり先頭にある\xE3,\x81,\x82,\x84いずれかのバイトにマッチします。
実行結果のstring(1) ""の正体は、文字列"あいう"の先頭1バイトなのです。

ということで、こんなコードは意図しない動きになります。

$input = 'い';
if (preg_match('/^[あ]/', $input)) {
  echo "マッチしました!";
} else {
  echo "マッチ失敗……";
}

実行結果

マッチしました!

文字クラスだけでなく、繰り返しの+,*なども同じ理由で問題になります。
つまり/あ+/が「あ」の繰り返しではなく、末尾のバイトの繰り返しになってしまいます。
また、.uなしだと1バイトにマッチしてしまいます。

uフラグの注意点(2023/07/23 追記)

@tadsan指摘いただいた内容を追記します)

じゃあ常にuフラグをつければいいか、というとそうとも限りません。

\dが全角数字にもマッチするようになってしまいます。
https://3v4l.org/vBbh0#v8.2.7

同様に、\wも全角数字・全角アルファベットにマッチするようになります。

これを避けるためには、[0-9]のような指定を行う必要があります。

まとめ

  • uフラグがないと、文字単位ではなくバイト単位で処理される
  • ただし、場合によってはuがあってもなくても同じ挙動をするように見えることもある
  • \dなど一部の正規表現の挙動が変わってしまう
  • マルチバイト文字を扱うなら常にuを付ける、と思っておけば良さそう
11
7
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
7