Help us understand the problem. What is going on with this article?

マルチバイト未対応の関数をいろいろ対応させてみた

More than 5 years have passed since last update.

出来るだけもとの関数の挙動に近づけています。

basename() について

PHP4.4.9まではNULLバイトを正しく処理できませんでしたが、
PHP5.0.0以降はバイナリセーフになりました。
(徳丸さんよりご指摘を頂きました)

但し、 setlocale()ロケールを正しく設定していることが前提になります。

エンコーディングが壊れていてもそのまま処理します。

マルチバイト対応 explode()

UTF-8限定版
explode() を使えば問題ない。
全エンコーディング対応版
function mb_explode($delimiter, $string, $limit = -1, $encoding = null) {
    $tmp = mb_regex_encoding();
    mb_regex_encoding(func_num_args() > 3 ? $encoding : mb_internal_encoding());
    $delimiter = mb_ereg_replace('[.\\\\+*?\\[^$(){}|]', '\\\\0', $delimiter);
    $ret = mb_split($delimiter, $string, $limit);
    mb_regex_encoding($tmp);
    return $ret;
}

エンコーディングが壊れていてもそのまま処理します。

マルチバイト対応 str_split()

UTF-8限定版
function str_split_utf8($string, $split_length = 1) {
    switch (true) {
        case ($split_length = (int)$split_length) < 1:
            return false;
        case !preg_match_all("/.{{$split_length}}|.++|\\A\\z/us", $string, $matches):
            return null;
        default:
            return $matches[0];
    }
}

長さ指定が間違っているときは FALSE を返します。
エンコーディングが壊れているときは NULL を返します。

全エンコーディング対応版
function mb_str_split($string, $split_length = 1, $encoding = null) {
    if ($split_length < 1) {
        return false;
    }
    if (func_num_args() < 3) {
        $encoding = mb_internal_encoding();
    }
    $ret = array();
    $len = mb_strlen($string, $encoding);
    for ($i = 0; $i < $len; $i += $split_length) {
        $ret[] = mb_substr($string, $i, $split_length, $encoding);
    }
    if (!$ret) {
        $ret[] = '';
    }
    return $ret;
}

長さ指定が間違っているときは FALSE を返します。
エンコーディングが壊れていてもそのまま処理します。

マルチバイト対応 trim()

UTF-8限定版
function trim_utf8($str, $charlist = " \t\n\r\0\x0B ") {
    $charlist = str_replace('..', '-', addcslashes($charlist, "^-:]\0\\/"));
    return preg_replace("/\\A[{$charlist}]++|[{$charlist}]++\\z/u", '', $str);
}

エンコーディングが壊れているときには NULL を返します。

全エンコーディング対応版
function mb_trim($str, $charlist = " \t\n\r\0\x0B ", $encoding = null) {
    $tmp = mb_regex_encoding();
    mb_regex_encoding(func_num_args() > 2 ? $encoding : mb_internal_encoding());
    $charlist = mb_ereg_replace('[\\[\\]^-]', '\\\\0', $charlist);
    $charlist = mb_ereg_replace('\\.{2}', '-', $charlist);
    $ret = mb_ereg_replace("\\A[{$charlist}]++|[{$charlist}]++\\z", '', $str);
    mb_regex_encoding($tmp);
    return $ret;
}

エンコーディングが壊れていてもそのまま処理します。

ltrim(), rtrim() もこんな感じでどうぞ。

マルチバイト対応 wordwrap()

想像以上に処理が大変だったので今回はギブアップ(汗)
マニュアルのコメント欄に投稿されているものもどれも再現性が微妙なものばかりで、みなさん苦戦されている様子。
いろいろ試してみた結果、 Smartymb_wordwrap() が一番良さげだったので紹介しておきます。
https://github.com/Jamesking56/Smarty-PHP/blob/master/plugins/shared.mb_wordwrap.php

もっと簡単に書けたらいいのになぁ・・・

マルチバイト対応 str_replace()

UTF-8限定版
str_replace() を使えば問題ない。
全エンコーディング対応版
function mb_str_replace($search, $replace, $subject, $encoding = null) {
    $tmp = mb_regex_encoding();
    mb_regex_encoding(func_num_args() > 3 ? $encoding : mb_internal_encoding());
    foreach ((array)$search as $i => $s) {
        if (!is_array($replace)) {
            $r = $replace;
        } elseif (isset($replace[$i])) {
            $r = $replace[$i];
        } else {
            $r = '';
        }
        $s = mb_ereg_replace('[.\\\\+*?\\[^$(){}|]', '\\\\0', $s);
        $subject = mb_ereg_replace($s, $r, $subject);
    }
    mb_regex_encoding($tmp);
    return $subject;
}

エンコーディングが壊れていてもそのまま処理します。

マルチバイト対応 strtr()

UTF-8限定版シグネチャ
string strtr_utf8 ( string $str , string $from , string $to )
string strtr_utf8 ( string $str , array $replace_pairs )
UTF-8限定版コード
function strtr_utf8() {
    if (func_num_args() < 3) {
        list($str, $replace_pairs) = func_get_args();
    } else {
        list($str, $from, $to) = func_get_args();
        $from = preg_split('//u', $from, -1, PREG_SPLIT_NO_EMPTY);
        $to = preg_split('//u', $to, -1, PREG_SPLIT_NO_EMPTY);
        $replace_pairs = array();
        foreach ($from as $i => $f) {
            if (!isset($to[$i])) {
                break;
            }
            $replace_pairs[$f] = $to[$i];
        }
    }
    return strtr($str, $replace_pairs);
}

エンコーディングが壊れていてもそのまま処理します。

全エンコーディング対応版シグネチャ
string mb_strtr ( string $str , string $from , string $to [, string $encoding] )
string mb_strtr ( string $str , array $replace_pairs [, string $encoding] )
全エンコーディング対応版コード
<?php
function mb_strtr() {
    $args = func_get_args();
    if (!is_array($args[1])) {
        list($str, $from, $to) = $args;
        $encoding = isset($args[3]) ? $args[3] : mb_internal_encoding(); 
        $replace_pairs = array();
        $len = mb_strlen($from, $encoding);
        for ($i =0; $i < $len; $i++) {
            $k = mb_substr($from, $i, 1, $encoding);
            $v = mb_substr($to, $i, 1, $encoding);
            $replace_pairs[$k] = $v;
        }
        return $replace_pairs ? mb_strtr($str, $replace_pairs, $encoding) : $str;
    }
    list($str, $replace_pairs) = $args;
    $tmp = mb_regex_encoding();
    mb_regex_encoding(isset($args[2]) ? $args[2] : mb_internal_encoding());
    uksort($replace_pairs, function ($a, $b) {
        return strlen($b) - strlen($a);
    });
    $from = $to = array();
    foreach ($replace_pairs as $f => $t) {
        if ($f !== '') {
            $from[] = '(' . mb_ereg_replace('[.\\\\+*?\\[^$(){}|]', '\\\\0', $f) . ')';
            $to[] = $t;
        }
    }
    $pattern = implode('|', $from);
    $ret = mb_ereg_replace_callback($pattern, function ($from) use ($to) {
        foreach ($to as $i => $t) {
            if ($from[$i + 1] !== '') {
                return $t;
            }
        }
    }, $str);
    mb_regex_encoding($tmp);
    return $ret;
}

エンコーディングが壊れていてもそのまま処理します。
PHP5.4.1 以降のみ対応。

備考

なお文字列置換関数に関して、動作の軽い順に

  • strtr() (引数3つ)
  • str_replace() (配列を含まない)
  • str_replace() (配列を含む)
  • strtr() (引数2つ)

――超えられない壁――

  • strtr_utf8() (引数2つ)
  • strtr_utf8() (引数3つ)

――超えられない壁――

  • mb_str_replace() (配列を含まない)
  • mb_strtr() (引数2つ)
  • mb_str_replace() (配列を含む)
  • mb_strtr() (引数3つ)

だいたいこんな感じのパフォーマンス順位になると思います。
正確なことは検証してないので分かりませんが。

mpyw
古い記事はそのまま参考にしないようにご注意ください
synapse
Synapseは、オンラインサロンサービスにおけるパイオニアとして、かつて存在していたスタートアップです。
https://synapseam.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away