LoginSignup
5
3

More than 1 year has passed since last update.

n番目に文字列が現れる場所を探す

Last updated at Posted at 2013-10-05

【2021/10/15 追記】
この記事は更新が停止されています。現在では筆者の思想が変化している面もありますので,過去の記事として参考程度にご覧ください。

番目指定は

0番目、1番目、2番目、…

というように0以上の整数とします。
想定しない値の例外処理はしていません。

strpos() を使う

strpos() の第3引数 $offset なんて滅多に出番ないと思ってたけど、こういう使い方ができるとは・・・

function strpos_n($str, $needle, $n = 0) {
    $offset = 0;
    $len = strlen($needle);
    while ($n-- >= 0 && ($pos = strpos($str, $needle, $offset)) !== false) {
        $offset = $pos + $len;
    }
    return $pos;
}
var_dump(strpos_n('He has a pencil that has a eraser on the top.', 'has', 1)); // int(21)

そのまま置換をしたい場合は substr_replace() に結果を渡してください。

マルチバイト対応版

バイトオフセットの代わりに文字オフセットで返します。

function mb_strpos_n($str, $needle, $n = 0, $encoding = null) {
    if (func_num_args() < 4) {
        $encoding = mb_internal_encoding();
    }
    $offset = 0;
    $len = mb_strlen($needle, $encoding);
    while ($n-- >= 0 && ($pos = mb_strpos($str, $needle, $offset, $encoding)) !== false) {
        $offset = $pos + $len;
    }
    return $pos;
}

preg_match_all() を使う

preg_match_all() のオプション PREG_OFFSET_CAPTURE を利用した手法です。
回数が増えてきたりするとこちらの方が速いと思われます。

function strpos_n($str, $needle, $n = 0) {
    $pattern = '/' . preg_quote($needle, '/') . '/';
    return
        preg_match_all($pattern, $str, $matches, PREG_OFFSET_CAPTURE) > $n ?
        $matches[0][$n][1] :
        false
    ;
}

こちらはマルチバイトに対応できません。

5
3
0

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
5
3