1. ホーム
  2. PHP

【PHP】文字列を操作する基本関数の使い方|substr・strpos・str_contains・strlen

Share

Web アプリケーションを作っていると、フォームの入力値を整える、文字列の中に特定の語が含まれているか調べる、長すぎる文を切り詰める、といった「文字列をちょっと加工する」処理が頻繁に登場します。PHP にはこうした処理のための関数が標準で多数用意されており、覚えてしまえば自分でループを書くより圧倒的に速く・安全に書けます。この記事では、strlen()substr()strpos()、PHP 8.0 で追加された str_contains() などの基本的な文字列操作関数を、初心者の方にもわかるように実際に動くコードで解説します。日本語のようなマルチバイト文字を扱うときの注意点も、つまずきやすいポイントとあわせて取り上げます。

まず押さえておきたい主要な関数

最初に、この記事で扱う関数の全体像を表で整理しておきます。それぞれの詳しい使い方は後の節で説明しますが、「こんな処理をしたいときはこの関数」という対応をここでつかんでおくと、あとの内容が頭に入りやすくなります。

関数説明
strlen()文字列のバイト数を返すstrlen("abc")3
mb_strlen()文字列の文字数を返す(マルチバイト対応)mb_strlen("あいう")3
substr()文字列の一部を取り出すsubstr("Hello", 0, 2)"He"
mb_substr()文字列の一部を取り出す(マルチバイト対応)mb_substr("あいう", 0, 2)"あい"
strpos()部分文字列が最初に現れる位置を返すstrpos("Hello", "l")2
str_contains()部分文字列を含むかを true/false で返す(PHP 8.0+)str_contains("Hello", "ell")true
str_starts_with()指定した文字列で始まるかを判定する(PHP 8.0+)str_starts_with("Hello", "He")true
str_ends_with()指定した文字列で終わるかを判定する(PHP 8.0+)str_ends_with("Hello", "lo")true
strtolower()すべて小文字に変換するstrtolower("ABC")"abc"
strtoupper()すべて大文字に変換するstrtoupper("abc")"ABC"
ucfirst()先頭の 1 文字だけ大文字にするucfirst("hello")"Hello"
trim()前後の空白などを取り除くtrim(" ab ")"ab"
str_repeat()文字列を指定回数くり返すstr_repeat("ab", 3)"ababab"

文字列の長さを数える:strlen と mb_strlen

文字列の長さを取得する最も基本的な関数が strlen() です。引数に文字列を渡すと、その長さを整数で返します。ただしここで気をつけたいのは、strlen() が返すのは「文字数」ではなく「バイト数」だという点です。アルファベットや数字なら 1 文字 = 1 バイトなので一致しますが、日本語のようなマルチバイト文字では結果がずれます。

strlen.php
<?php
// 半角英字は 1 文字 = 1 バイトなので一致する
echo strlen("Hello");        // 5

// 日本語(UTF-8)は 1 文字 3 バイト → バイト数が返る
echo strlen("こんにちは");    // 15

// 文字数を数えたいときは mb_strlen を使う
echo mb_strlen("こんにちは"); // 5

UTF-8 では、ひらがなや漢字 1 文字はおおむね 3 バイトで表現されます。そのため「こんにちは」は 5 文字でも、strlen() では 15 という値になります。入力された文字数を制限したい、画面に表示する文字数を数えたい、といった「人間が見たままの文字数」を扱いたい場面では、必ず mb_strlen()(マルチバイト文字列用の関数)を使うようにしましょう。mb_ で始まる関数群は、文字エンコーディングを意識して 1 文字単位で正しく処理してくれます。

文字列の一部を取り出す:substr と mb_substr

文字列の一部分だけを切り出したいときは substr() を使います。構文は substr(string $string, int $offset, ?int $length = null): string で、第 2 引数の $offset に開始位置(先頭は 0)、第 3 引数の $length に取り出す長さを指定します。$length を省略すると、開始位置から末尾までを取り出します。

substr.php
<?php
$str = "Hello, World";

// 先頭(0 番目)から 5 文字を取り出す
echo substr($str, 0, 5);  // Hello

// 7 番目から末尾まで(length 省略)
echo substr($str, 7);     // World

// offset に負の値を渡すと末尾から数えた位置になる
echo substr($str, -5);    // World

覚えておくと便利なのが、開始位置に負の値を渡せる点です。substr($str, -5) のように書くと、文字列の末尾から数えて 5 文字目を開始位置とします。ファイルの拡張子だけを取り出したいときなど、末尾を起点に処理したい場面で役立ちます。

日本語を切り出すなら mb_substr

substr() もバイト単位で動作するため、日本語をそのまま切り出すと文字の途中で分割され、文字化けの原因になります。次の例を見てください。

mb_substr.php
<?php
$str = "こんにちは";

// substr はバイト単位 → 3 バイト = 1 文字分しか取れない
echo substr($str, 0, 3);     // こ

// mb_substr なら「文字数」で正しく取り出せる
echo mb_substr($str, 0, 3);  // こんに

substr($str, 0, 3) は「先頭から 3 バイト」を取り出すため、3 バイトでちょうど 1 文字分の「こ」だけが返ります。日本語のように 1 文字が複数バイトになる文字列を扱うときは、mb_substr() を使えば文字数の単位で安全に切り出せます。一覧画面で本文を一定の文字数だけ表示する、といった処理では mb_substr() が基本になります。

特定の文字がどこにあるか調べる:strpos

文字列の中で、ある部分文字列が最初に現れる位置を知りたいときは strpos() を使います。構文は strpos(string $haystack, string $needle, int $offset = 0): int|false で、第 1 引数に「探される側の文字列」、第 2 引数に「探したい文字列」を渡します。見つかった場合はその開始位置(先頭は 0)を整数で、見つからなかった場合は false を返します。

strpos.php
<?php
$str = "Hello, World";

// "World" は 7 番目から始まる
var_dump(strpos($str, "World"));  // int(7)

// 先頭にある場合は 0 が返る
var_dump(strpos($str, "Hello"));  // int(0)

// 見つからないときは false
var_dump(strpos($str, "PHP"));    // bool(false)

日本語を含む文字列で「何文字目にあるか」を正しく求めたい場合は、ここでも mb_strpos() を使います。strpos() はバイト位置を返すため、日本語では位置の数値がずれてしまうからです。

strpos の 0 と false を取り違えない

strpos() でとくに初心者がはまりやすいのが、戻り値の 0false の扱いです。先ほどの例のとおり、探したい文字列が先頭にあると strpos() は位置 0 を返します。一方、見つからなかったときは false を返します。PHP ではこの 2 つが条件式の中で同じ「偽」として扱われてしまうため、書き方を誤ると意図しない動作になります。

== や if だけで判定すると先頭一致を見逃す

次のコードは「文字列が見つかったかどうか」を判定しているつもりですが、正しく動きません。

strpos-bug.php
<?php
$str = "Hello, World";

// strpos が 0 を返すと、0 は「偽」とみなされてしまう
if (strpos($str, "Hello")) {
    echo "見つかりました";
} else {
    echo "見つかりませんでした"; // 実際にはこちらが表示される
}

“Hello” は文字列の先頭にあるので strpos()0 を返しますが、if (0) は偽と判定されるため、「見つかりませんでした」と表示されてしまいます。これは「見つからなかった場合の false」と「先頭で見つかった場合の 0」を if が区別できないために起こる、典型的なバグです。

=== false で型まで含めて比較する

この問題を避けるには、値と型の両方を比較する厳密比較演算子 ===(または !==)を使い、戻り値が false かどうかをはっきり判定します。0 は整数、false は真偽値なので、=== なら両者をきちんと区別できます。

strpos-fixed.php
<?php
$str = "Hello, World";

// 「false ではない」=見つかった、と判定する
if (strpos($str, "Hello") !== false) {
    echo "見つかりました"; // こちらが正しく表示される
} else {
    echo "見つかりませんでした";
}

このように、strpos() の結果を「含まれているか」の判定に使うときは、必ず === false!== false で比較するのが鉄則です。位置そのものが必要なら戻り値を変数に入れて使い、含むか否かだけを知りたいなら次に紹介する関数を使うと、こうした取り違えを根本から避けられます。

含むかどうかを素直に判定する:str_contains(PHP 8.0+)

PHP 8.0 からは、文字列の中に部分文字列が含まれているかを直接 true / false で返す str_contains() が追加されました。strpos() のように位置を返すわけではないので、先ほどの 0false の取り違えを気にする必要がなく、コードの意図もそのまま読み取れます。

str_contains.php
<?php
$str = "Hello, World";

// 含まれていれば true、含まれていなければ false
var_dump(str_contains($str, "World")); // bool(true)
var_dump(str_contains($str, "PHP"));   // bool(false)

// strpos のような === false の判定が不要になる
if (str_contains($str, "Hello")) {
    echo "見つかりました"; // 先頭一致でも正しく動く
}

「含むかどうかだけを知りたい」という用途であれば、str_contains() を使うのがいまの PHP では最もシンプルで安全です。あわせて、特定の文字列で始まるかを調べる str_starts_with()、終わるかを調べる str_ends_with() も同じく PHP 8.0 で追加されました。URL の先頭が https:// かを確認したり、ファイル名が .png で終わるかを判定したりといった処理が、ひと目で読めるコードになります。

starts-ends.php
<?php
$url  = "https://example.com/image.png";

// 先頭が https:// かどうか
var_dump(str_starts_with($url, "https://")); // bool(true)

// 末尾が .png かどうか
var_dump(str_ends_with($url, ".png"));        // bool(true)

大文字・小文字の変換と前後の空白除去

大文字・小文字をそろえる処理もよく使います。すべて小文字にするには strtolower()、すべて大文字にするには strtoupper()、先頭の 1 文字だけを大文字にするには ucfirst() を使います。メールアドレスの大文字・小文字を統一して比較したいときなどに便利です。

case.php
<?php
echo strtolower("Hello WORLD"); // hello world
echo strtoupper("Hello world"); // HELLO WORLD
echo ucfirst("hello");          // Hello

フォームから受け取った値には、前後に余計な空白が入っていることがよくあります。trim() を使うと、文字列の前後にある空白や改行、タブといった空白文字をまとめて取り除けます。前側だけを処理する ltrim()、後ろ側だけを処理する rtrim() もあります。

trim.php
<?php
$input = "  user@example.com  ";

// 前後の空白を取り除いてから使う
$email = trim($input);

echo "[" . $email . "]"; // [user@example.com]

同じ文字列をくり返す:str_repeat

同じ文字列を指定した回数だけくり返したいときは str_repeat() が便利です。第 1 引数にくり返す文字列、第 2 引数に回数を渡します。区切り線を引いたり、桁数をそろえるための文字を作ったりするときに使えます。

str_repeat.php
<?php
// "ab" を 3 回くり返す
echo str_repeat("ab", 3); // ababab

// 区切り線として使う
echo str_repeat("-", 20); // --------------------

まとめ

PHP の文字列操作は、用途に合った関数を選べば短く安全に書けます。長さを数えるなら strlen()、一部を取り出すなら substr()、含まれるかや位置を調べるなら strpos()str_contains() が基本になります。とくに strpos() は、見つからないときの false と先頭一致の 0 を区別するために === false で判定すること、含むかどうかだけ知りたいときは str_contains() を使うことを覚えておきましょう。そして日本語などのマルチバイト文字を扱う場面では、strlen()substr() がバイト単位で動くことに注意し、文字数の単位で正しく処理できる mb_strlen()mb_substr() を選ぶのがポイントです。まずは手元の環境でひとつずつ実行して、戻り値の型や値を確かめてみてください。

参考ページ