40

どうやら家族mb_trimにはないようなので、自分用に実装しようとしています。mb_*

最近、 php.netのコメントでこの正規表現を見つけました。

/(^\s+)|(\s+$)/u

したがって、次の方法で実装します。

function multibyte_trim($str)
{
    if (!function_exists("mb_trim") || !extension_loaded("mbstring")) {
        return preg_replace("/(^\s+)|(\s+$)/u", "", $str);
    } else {
        return mb_trim($str);
    }
}

正規表現は私には正しいようですが、私は正規表現に非常に慣れていません。これにより、文字列の先頭/末尾にあるUnicode スペースが効果的に削除されますか?

4

8 に答える 8

57

標準trim関数は、いくつかのスペースおよびスペースに似た文字をトリムします。これらは ASCII 文字として定義されます。これは、 ~ の特定のバイトを意味00100 0000ます。

適切なUTF-8 入力には、バイトで構成されるマルチバイト文字が含まれることはありません0xxx xxxx適切なUTF-8 マルチバイト文字のすべてのバイトは で始まり1xxx xxxxます。

これは、適切なUTF-8 シーケンスでは、バイト0xxx xxxxはシングルバイト文字のみを参照できることを意味します。したがって、適切なUTF-8 シーケンスがあると仮定すると、 PHP のtrim関数は「文字の半分」を削除することはありません。(不適切なUTF-8 シーケンスには十分注意してください。)


on ASCII 正規表現は\s 、ほぼ同じ文字に一致しtrimます。

修飾子pregを持つ関数は、UTF -8 でエンコードされた正規表現でのみ機能し、UTF8 のnbspにも一致します。非改行スペースでのこの動作は、それを使用する唯一の利点です。/u/\s/u

他の非 ASCII 互換エンコーディングでスペース文字を置き換えたい場合、どちらの方法も機能しません。

つまり、通常のスペースを ASCII 互換の文字列に切り詰めようとしている場合は、単にtrim. 使用するとき/\s/uは、テキストの nbsp の意味に注意してください。


気をつけて:

  $s1 = html_entity_decode(" Hello   "); // the NBSP
  $s2 = "  exotic test ホ  ";

  echo "\nCORRECT trim: [". trim($s1) ."], [".  trim($s2) ."]";
  echo "\nSAME: [". trim($s1) ."] == [". preg_replace('/^\s+|\s+$/','',$s1) ."]";
  echo "\nBUT: [". trim($s1) ."] != [". preg_replace('/^\s+|\s+$/u','',$s1) ."]";

  echo "\n!INCORRECT trim: [". trim($s2,' ') ."]"; // DANGER! not UTF8 safe!
  echo "\nSAFE ONLY WITH preg: [". 
       preg_replace('/^[\s]+|[\s]+$/u', '', $s2) ."]";
于 2012-04-09T00:23:09.207 に答える
22

定義している無限再帰関数で何をしようとしているのかわかりませんが、マルチバイトセーフのトリムが必要な場合は、これでうまくいきます。

function mb_trim($str) {
  return preg_replace("/^\s+|\s+$/u", "", $str); 
}
于 2012-04-08T22:58:57.590 に答える
7

このバージョンは、2 番目のオプション パラメータ $charlist をサポートしています。

function mb_trim ($string, $charlist = null) 
{   
    if (is_null($charlist)) {
        return trim ($string);
    } 

    $charlist = str_replace ('/', '\/', preg_quote ($charlist));
    return preg_replace ("/(^[$charlist]+)|([$charlist]+$)/us", '', $string);
}

ただし、範囲の「..」はサポートされていません。

于 2012-11-08T12:11:23.520 に答える
6

わかりましたので、@ edson-medina のソリューションを使用してバグを修正し、いくつかの単体テストを追加しました。これは、trim、rtrim、および ltrim に対応する mb を与えるために使用する 3 つの関数です。

////////////////////////////////////////////////////////////////////////////////////
//Add some multibyte core functions not in PHP
////////////////////////////////////////////////////////////////////////////////////
function mb_trim($string, $charlist = null) {
    if (is_null($charlist)) {
        return trim($string);
    } else {
        $charlist = preg_quote($charlist, '/');
        return preg_replace("/(^[$charlist]+)|([$charlist]+$)/us", '', $string);
    }
}
function mb_rtrim($string, $charlist = null) {
    if (is_null($charlist)) {
        return rtrim($string);
    } else {
        $charlist = preg_quote($charlist, '/');
        return preg_replace("/([$charlist]+$)/us", '', $string);
    }
}
function mb_ltrim($string, $charlist = null) {
    if (is_null($charlist)) {
        return ltrim($string);
    } else {
        $charlist = preg_quote($charlist, '/');
        return preg_replace("/(^[$charlist]+)/us", '', $string);
    }
}
////////////////////////////////////////////////////////////////////////////////////

興味のある人のために私が書いた単体テストは次のとおりです。

public function test_trim() {
    $this->assertEquals(trim(' foo '), mb_trim(' foo '));
    $this->assertEquals(trim(' foo ', ' o'), mb_trim(' foo ', ' o'));
    $this->assertEquals('foo', mb_trim(' Åfooホ ', ' Åホ'));
}

public function test_rtrim() {
    $this->assertEquals(rtrim(' foo '), mb_rtrim(' foo '));
    $this->assertEquals(rtrim(' foo ', ' o'), mb_rtrim(' foo ', ' o'));
    $this->assertEquals('foo', mb_rtrim('fooホ ', ' ホ'));
}

public function test_ltrim() {
    $this->assertEquals(ltrim(' foo '), mb_ltrim(' foo '));
    $this->assertEquals(ltrim(' foo ', ' o'), mb_ltrim(' foo ', ' o'));
    $this->assertEquals('foo', mb_ltrim(' Åfoo', ' Å'));
}
于 2015-05-08T18:55:15.187 に答える
5

UTF-8 文字列の非 ASCII 互換スペース (改行しないスペースなど) をトリミングすることもできます。修飾子を使用しても、preg_replace('/^\p{Z}+|\p{Z}+$/u','',$str);

\s「ASCII 互換」スペース文字のみに一致します。 ただし、既知のすべての Unicode スペース文字に一致しますu
\p{Z}

于 2012-09-14T06:51:11.200 に答える
2

mb_ereg_replace はそれを回避しているようです:

function mb_trim($str,$regex = "(^\s+)|(\s+$)/us") {
    return mb_ereg_replace($regex, "", $str);
}

..しかし、正規表現については、人々がトリム()にフィードできると期待する「charlist」パラメーター(つまり、トリミングする文字のリスト)に追加する方法を知るのに十分な知識がないので、正規表現をパラメーターにしました。

特殊文字の配列を使用して、charlist 内の各文字をステップスルーし、正規表現文字列を作成するときにそれに応じてエスケープすることができます。

于 2012-05-24T14:11:49.507 に答える
0

私の2セント

あなたの質問に対する実際の解決策は、外国の入力文字列を変更する前に、まずエンコードチェックを行うことです。多くの人は、入力データの「サニタイズと検証」についてはすぐに習得できますが、初期段階で使用している文字列の根底にある性質 (文字エンコーディング) を特定するステップを習得するには時間がかかります。

各文字を表すのに何バイトが使用されますか? 適切にフォーマットされた UTF-8 では、1 (文字がtrim扱う)、2、3、または 4 バイトになります。問題は、UTF-8 の従来の、または不正な形式の表現が登場したときに発生します。バイト文字の境界が期待どおりに整列しない可能性があります (素人の話です)。

PHP では、すべての文字列を強制的に適切な UTF-8 エンコーディング (1 文字あたり 1、2、3、または 4 バイト) に準拠させるべきであると主張する人もtrim()います。 with はtrim()、文字列の先頭と末尾から削除しようとする拡張 ASCII / 1 バイト値に対して一致します ( trim マニュアル ページ)。

ただし、コンピューター プログラミングは多様な分野であるため、すべてのシナリオで機能する包括的なアプローチをとることはできません。そうは言っても、適切に機能するために必要な方法でアプリケーションを作成してください。フォーム入力を使用して基本的なデータベース駆動型の Web サイトを作成するだけですか? はい、私のお金のために、すべてを UTF-8 にする必要があります。

: UTF-8 の問題が安定している場合でも、国際化の問題は引き続き発生します。なんで?多くの英語以外の文字セットが 2、3、または 4 バイト空間 (コード ポイントなど) に存在します。明らかに、中国語、日本語、ロシア語、アラビア語、またはヘブライ語のスクリプトを処理する必要があるコンピューターを使用している場合、すべてを 2、3、および 4 バイトでも動作させたいと思うでしょう! PHPtrim関数は、デフォルトの文字またはユーザー指定の文字を削除できることに注意してください。trimこれは特に、一部の漢字を説明する必要がある場合に重要です。

誰かが自分のサイトにアクセスできないという問題に対処したいのですが、それから、発生してはならないアクセスと応答の問題に対処したいと考えています。考えてみると、これは最小特権(セキュリティ) とユニバーサル デザイン(アクセシビリティ) の原則に沿っています。

概要

入力データが適切な UTF-8 エンコーディングに準拠していない場合は、例外をスローすることをお勧めします。PHP のマルチバイト関数を使用して、エンコーディングまたはその他のマルチバイト ライブラリを特定できます。PHP が Unicode (Perl、Java など) を完全にサポートするように作成されている場合、PHP はより優れたものになります。PHP Unicode の取り組みは数年前に終了したため、追加のライブラリを使用して UTF-8 マルチバイト文字列を適切に処理する必要があります。/uフラグを追加するだけpreg_replace()では、全体像が見えません。

アップデート:

そうは言っても、次のマルチバイト トリムは、URL のパス コンポーネントから REST リソースを抽出しようとする場合に役立つと思います (当然、クエリ文字列を除く)。注: これは、パス文字列をサニタイズして検証した後に役立ちます。

function mb_path_trim($path)
{
    return preg_replace("/^(?:\/)|(?:\/)$/u", "", $path);
}
于 2018-08-14T01:26:37.280 に答える