PHPで文字列の一部を取得する方法|substrとmb_substrの違い(日本語対応)

PHPで文字列の一部を取り出すには substr()mb_substr() があります。英数字なら substr() で十分ですが、日本語など多バイト文字では substr() を使うと文字化けします。この記事では両者の違いと、正しい使い分けを解説します。

この記事の結論:substr()バイト単位mb_substr()文字単位で切り出します。日本語を含む文字列は必ず mb_substr() を使ってください。英数字だけなら substr() でも問題ありません。
スポンサーリンク

substr:バイト単位で取り出す

substr($string, $start, $length) は、開始位置から指定バイト数を取り出します。位置は0から数えます。英数字(半角ASCII)なら1文字=1バイトなので、見た目どおりに動きます。

substr(英数字なら直感どおり)
<?php
$s = "Hello, World!";
echo substr($s, 0, 5); // "Hello"
echo substr($s, 7);    // "World!"(長さ省略で末尾まで)

注意:substrは日本語で文字化けする

ここが最重要ポイントです。substr()バイト数で切るため、1文字3バイトのUTF-8日本語では、文字の途中で切れて文字化けします。

NG: 日本語にsubstrを使うと壊れる
<?php
$s = "こんにちは、世界!";

// 先頭5文字のつもりが…「こ」(3バイト) + 「ん」の途中2バイト = 文字化け
echo substr($s, 0, 5); // "こ" + 壊れたバイト(意図どおりにならない)

echo strlen($s);    // 25(バイト数)
echo mb_strlen($s); // 9(文字数)
「こんにちは」を取りたくて substr($s, 0, 5) としても、得られるのは「こ」+壊れた1.6文字ぶんのバイトです。日本語の「文字数」で切りたいなら、次の mb_substr() を使ってください。

mb_substr:文字単位で正しく取り出す

mb_substr()文字単位で切り出すため、日本語でも正しく動きます。使い方は substr() とほぼ同じです。

mb_substr(日本語も正しく)
<?php
$s = "こんにちは、世界!";

echo mb_substr($s, 0, 5); // "こんにちは"(正しく5文字)
echo mb_substr($s, 6);    // "世界!"(6文字目以降)
第4引数でエンコーディング("UTF-8" など)も指定できますが、省略すると内部エンコーディング(通常UTF-8)が使われます。環境がUTF-8なら省略しても問題ありません。

負の開始位置・長さ

開始位置に負の値を渡すと末尾から数えます。長さに負の値を渡すと、末尾からその文字数を残して切り取ります(substrmb_substr 共通)。

負の値の使い方
<?php
$s = "abcdefg";

echo substr($s, -3);     // "efg"(末尾3文字)
echo substr($s, 0, -2);  // "abcde"(末尾2文字を除く)

$ja = "あいうえお";
echo mb_substr($ja, -2); // "えお"(末尾2文字)
正確な文字数の取得は文字列の長さを正確に取得する方法$str[$i] のような添字アクセスで起こる文字化け・範囲外はUninitialized string offset エラーの解決方法で解説しています。

よくある質問(FAQ)

Qsubstrとmb_substrはどう使い分けますか?
A日本語など多バイト文字を含むなら mb_substr()、半角英数字だけなら substr() で問題ありません。substrはバイト単位なので、日本語に使うと文字化けします。
Qsubstrで日本語が文字化けします。
Asubstr()バイト単位で切るためです。UTF-8の日本語は1文字3バイトなどなので、文字の途中で切れて壊れます。mb_substr() を使えば文字単位で正しく切り出せます。
Qmb_substrの第4引数(エンコーディング)は必須ですか?
A必須ではありません。省略すると内部エンコーディング(通常UTF-8)が使われます。明示したい場合は mb_substr($s, 0, 5, "UTF-8") と書きます。
Q末尾から数文字を取りたいです。
A開始位置に負の値を渡します。mb_substr($s, -3) で末尾3文字を取得できます。英数字なら substr($s, -3) でも同じです。

まとめ

PHPで文字列の一部を取得するポイントを整理します。

  • substr()バイト単位mb_substr()文字単位
  • 日本語を含むなら必ず mb_substr()(substrは文字化けする)
  • 位置は0始まり、長さ省略で末尾まで
  • 負の開始位置は末尾から、負の長さは末尾を残して切る
  • mb_substrの第4引数(encoding)は省略可(既定はUTF-8相当)

関連として、文字列の長さを正確に取得する方法Uninitialized string offset エラーの解決方法文字列パターンの確認方法もあわせて読むと、PHPの文字列処理に強くなれます。