web-dev-qa-db-ja.com

PHP:文字列内のすべての非印刷可能文字を削除するにはどうすればよいですか?

文字0〜31および127を削除する必要があると思いますが、

これを効率的に行うための関数またはコードがあります。

141

7ビットASCII?

Tardisが1963年に上陸したばかりで、7ビットの印刷可能なASCII文字だけが必要な場合、0-31および127-255のすべてをこれでリッピングできます。

$string = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $string);

0〜31、127〜255の範囲のすべてに一致し、削除します。

8ビット拡張ASCII?

ホットタブタイムマシンに陥り、80年代に戻りました。何らかの形式の8ビットASCIIがある場合は、128〜255の範囲の文字を保持することができます。簡単な調整-0〜31および127を探すだけ

$string = preg_replace('/[\x00-\x1F\x7F]/', '', $string);

UTF-8?

ああ、21世紀へようこそ。 UTF-8でエンコードされた文字列がある場合、/umodifier を正規表現で使用できます

$string = preg_replace('/[\x00-\x1F\x7F]/u', '', $string);

これは0-31と127を削除するだけです。これはASCIIとUTF-8で機能します。これは、両方が 同じ制御セット範囲 を共有するためです(以下のmguttで説明)。厳密に言えば、これは/u修飾子なしで機能します。しかし、他の文字を削除したい場合は簡単になります...

Unicodeを扱う場合、 潜在的に多くの非印刷要素 がありますが、単純なものを考えてみましょう: NO-BREAK SPACE(U + 00A0)

UTF-8文字列では、これは0xC2A0としてエンコードされます。その特定のシーケンスを探して削除することもできますが、/u修飾子を適切に使用すれば、単に\xA0を文字クラスに追加できます。

$string = preg_replace('/[\x00-\x1F\x7F\xA0]/u', '', $string);

補遺:str_replaceはどうですか?

preg_replaceは非常に効率的ですが、この操作を頻繁に行う場合は、削除したい文字の配列を作成し、以下のmguttで示されているようにstr_replaceを使用できます。

//build an array we can re-use across several operations
$badchar=array(
    // control characters
    chr(0), chr(1), chr(2), chr(3), chr(4), chr(5), chr(6), chr(7), chr(8), chr(9), chr(10),
    chr(11), chr(12), chr(13), chr(14), chr(15), chr(16), chr(17), chr(18), chr(19), chr(20),
    chr(21), chr(22), chr(23), chr(24), chr(25), chr(26), chr(27), chr(28), chr(29), chr(30),
    chr(31),
    // non-printing characters
    chr(127)
);

//replace the unwanted chars
$str2 = str_replace($badchar, '', $str);

直感的には、これは高速のように見えますが、常にそうであるとは限りません。あなたがそれがあなたを救うかどうかを確かめるために間違いなくベンチマークする必要があります。さまざまな文字列の長さでランダムデータを使用していくつかのベンチマークを行ったところ、このパターンはphp 7.0.12を使用して明らかになりました

     2 chars str_replace     5.3439ms preg_replace     2.9919ms preg_replace is 44.01% faster
     4 chars str_replace     6.0701ms preg_replace     1.4119ms preg_replace is 76.74% faster
     8 chars str_replace     5.8119ms preg_replace     2.0721ms preg_replace is 64.35% faster
    16 chars str_replace     6.0401ms preg_replace     2.1980ms preg_replace is 63.61% faster
    32 chars str_replace     6.0320ms preg_replace     2.6770ms preg_replace is 55.62% faster
    64 chars str_replace     7.4198ms preg_replace     4.4160ms preg_replace is 40.48% faster
   128 chars str_replace    12.7239ms preg_replace     7.5412ms preg_replace is 40.73% faster
   256 chars str_replace    19.8820ms preg_replace    17.1330ms preg_replace is 13.83% faster
   512 chars str_replace    34.3399ms preg_replace    34.0221ms preg_replace is  0.93% faster
  1024 chars str_replace    57.1141ms preg_replace    67.0300ms str_replace  is 14.79% faster
  2048 chars str_replace    94.7111ms preg_replace   123.3189ms str_replace  is 23.20% faster
  4096 chars str_replace   227.7029ms preg_replace   258.3771ms str_replace  is 11.87% faster
  8192 chars str_replace   506.3410ms preg_replace   555.6269ms str_replace  is  8.87% faster
 16384 chars str_replace  1116.8811ms preg_replace  1098.0589ms preg_replace is  1.69% faster
 32768 chars str_replace  2299.3128ms preg_replace  2222.8632ms preg_replace is  3.32% faster

タイミング自体は10000回の反復ですが、より興味深いのは相対的な違いです。 512文字までは、preg_replaceが常に勝っています。 1〜8kbの範囲では、str_replaceのエッジはわずかでした。

面白い結果だと思ったので、ここに含めました。 重要なことは、この結果を使用して使用する方法を決定するのではなく、独自のデータに対してベンチマークを行い、決定することです

304
Paul Dixon

ここでの他の回答の多くは、ユニコード文字を考慮していません(例:öäüßйȝîûηыეமிᚉ⠛)。この場合、次を使用できます。

$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/u', '', $string);

技術的に制御文字である\x80-\x9F(7ビットASCII文字の範囲より上)の範囲に奇妙な文字のクラスがありますが、時間が経つにつれて印刷可能な文字に悪用されてきました。これらに問題がない場合は、次を使用できます。

$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $string);

改行、キャリッジリターン、タブ、改行なしスペース、およびソフトハイフンも削除する場合は、次を使用できます。

$string = preg_replace('/[\x00-\x1F\x7F-\xA0\xAD]/u', '', $string);

上記の例では、mustが単一引用符を使用していることに注意してください。

基本的な印刷可能ASCII文字以外のすべてを削除したい場合(上記のすべてのサンプル文字が削除されます)、次を使用できます。

$string = preg_replace( '/[^[:print:]]/', '',$string);

参照については http://www.fileformat.info/info/charset/UTF-8/list.htm を参照してください

135
Dalin

文字クラスを使用できます

/[[:cntrl:]]+/
26
ghostdog74

PHP 5.2以降では、filter_varへのアクセス権もありますが、これについては言及していませんので、それをそこに捨てると思いました。 filter_varを使用して、印刷不可能な文字<32および> 127を除去するには、次のようにします。

32未満のASCII文字をフィルター

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);

127を超えるASCII文字をフィルター

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_HIGH);

両方を削除:

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW|FILTER_FLAG_STRIP_HIGH);

また、高い文字を削除しながら、低い文字(改行、タブなど)をHTMLエンコードできます。

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_ENCODE_LOW|FILTER_FLAG_STRIP_HIGH);

HTMLの除去、電子メールとURLのサニタイズなどのオプションもあります。そのため、サニタイズ(データの除去)および検証(静かに除去するのではなく有効でない場合はfalseを返す)の多くのオプションがあります。

サニタイズ:http://php.net/manual/en/filter.filters.sanitize.php

検証:http://php.net/manual/en/filter.filters.validate.php

ただし、FILTER_FLAG_STRIP_LOWが改行とキャリッジリターンを削除するという問題がまだあります。これは、textareaに対しては完全に有効な文字です。したがって、Regexの回答の一部は、たとえばこのスレッドを確認した後、テキストエリアに対してこれを行う予定です。

$string = preg_replace( '/[^[:print:]\r\n]/', '',$input);

これは、数値の範囲ごとに削除された多くの正規表現よりも読みやすいようです。

23
Kevin Nelson

これは簡単です:

$ string = preg_replace( '/ [^ [:cntrl:]] /'、 ''、$ string);

18
jacktrade

すべてのソリューションは部分的に機能し、以下でもすべてのケースをカバーしているとは限りません。私の問題は、utf8 mysqlテーブルに文字列を挿入しようとしたことでした。文字列(およびそのバイト)はすべてutf8に準拠していましたが、いくつかの不正なシーケンスがありました。私はそれらのほとんどが制御またはフォーマットであると仮定します。

function clean_string($string) {
  $s = trim($string);
  $s = iconv("UTF-8", "UTF-8//IGNORE", $s); // drop all non utf-8 characters

  // this is some bad utf-8 byte sequence that makes mysql complain - control and formatting i think
  $s = preg_replace('/(?>[\x00-\x1F]|\xC2[\x80-\x9F]|\xE2[\x80-\x8F]{2}|\xE2\x80[\xA4-\xA8]|\xE2\x81[\x9F-\xAF])/', ' ', $s);

  $s = preg_replace('/\s+/', ' ', $s); // reduce all multiple whitespace to a single space

  return $s;
}

さらに問題を悪化させるのは、 ここで少し話したように、テーブル対サーバー対接続対コンテンツのレンダリングです

15
Wayne Weibel

私のUTF-8準拠バージョン:

preg_replace('/[^\p{L}\s]/u','',$value);

9
cedivad

通常のエクスプレスを使用して、保持したい文字以外のすべてを削除できます。

$string=preg_replace('/[^A-Za-z0-9 _\-\+\&]/','',$string);

A〜Zまたはa〜z以外のすべての文字(^)、0〜9の数字、スペース、アンダースコア、ハイペン、プラス、アンパサンドを、何も置き換えない(つまり、削除する)。

6
Richy B.
preg_replace('/(?!\n)[\p{Cc}]/', '', $response);

これにより、すべての制御文字( http://uk.php.net/manual/en/regexp.reference.unicode.php )が削除され、\n改行文字が残ります。私の経験から、制御文字は最も頻繁に印刷の問題を引き起こすものです。

5
Gajus

@ PaulDixonの答え は、完全に間違っています。これは、printableを削除するためですextended ASCII文字 128-255! 部分的に修正されました。彼は、拡張ASCII文字を持たないため、127文字の7ビットASCIIセットから128-255を削除したい理由がわかりません。

しかし最後に、128_255を削除しないことが重要でした。たとえば、chr(128)\x80)は8ビットASCIIのユーロ記号であり、Windowsの多くのUTF-8フォント ユーロ記号を表示 およびAndroidは自分のテストに関するものです。

また、UTF-8文字列(おそらくマルチバイトUTF-8文字の開始バイト)からASCII chars 128-255を削除すると、多くのUTF-8文字が削除されます。だからそれをしないでください!現在使用されているすべてのファイルシステムで完全に正当な文字です。 予約されている範囲は0〜31のみ

代わりに、これを使用して、印刷できない文字0〜31および127を削除します。

$string = preg_replace('/[\x00-\x1F\x7F]/', '', $string);

は、ASCIIおよびUTF-8で機能します。これは、両方が 同じコントロールセット範囲 を共有するためです。

の 最速 正規表現を使用しない遅い¹代替:

$string = str_replace(array(
    // control characters
    chr(0), chr(1), chr(2), chr(3), chr(4), chr(5), chr(6), chr(7), chr(8), chr(9), chr(10),
    chr(11), chr(12), chr(13), chr(14), chr(15), chr(16), chr(17), chr(18), chr(19), chr(20),
    chr(21), chr(22), chr(23), chr(24), chr(25), chr(26), chr(27), chr(28), chr(29), chr(30),
    chr(31),
    // non-printing characters
    chr(127)
), '', $string);

すべての空白文字\t\n、および\rを保持する場合は、このリストからchr(9)chr(10)、およびchr(13)を削除します。注:通常の空白はchr(32)なので、結果に残ります。問題を引き起こす可能性があるため、改行しないスペースchr(160)を削除するかどうかを自分で決定します。

¹@PaulDixonによってテストされ、自分で検証されました。

3
mgutt

どうですか:

return preg_replace("/[^a-zA-Z0-9`_.,;@#%~'\"\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:\-\s\\\\]+/", "", $data);

含めたいものを完全に制御できます

2
sdfor

マークされた回答は完璧ですが、文字127(DEL)を見逃しますが、これも印刷できない文字です

私の答えは

$string = preg_replace('/[\x00-\x1F\x7f-\xFF]/', '', $string);
1
Mubashar Ahmad

入力文字列からすべての非ASCII文字を削除するには

$result = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $string);

このコードは、0〜31および128〜255の16進数の範囲の文字を削除し、結果の文字列に32〜127の16進文字のみを残します。この例では$ resultと呼びます。

1
Junaid Masood

https://github.com/neitanod/forceutf8 を使用してUTF8の問題を解決しました

use ForceUTF8\Encoding;

$string = Encoding::fixUTF8($string);
0
mnv

「cedivad」はスウェーデン語の文字ÅÄÖの永続的な結果で問題を解決しました。

$text = preg_replace( '/[^\p{L}\s]/u', '', $text );

ありがとう!

0
Andreas Ek

印刷できない文字を削除せずにこれを行う方法をまだ探しているが、それらをエスケープするのではなく、私はこれを手伝いました。自由に改善してください!文字は\\ x [A-F0-9] [A-F0-9]にエスケープされます。

次のように呼び出します。

$escaped = EscapeNonASCII($string);

$unescaped = UnescapeNonASCII($string);

<?php 
  function EscapeNonASCII($string) //Convert string to hex, replace non-printable chars with escaped hex
    {
        $hexbytes = strtoupper(bin2hex($string));
        $i = 0;
        while ($i < strlen($hexbytes))
        {
            $hexpair = substr($hexbytes, $i, 2);
            $decimal = hexdec($hexpair);
            if ($decimal < 32 || $decimal > 126)
            {
                $top = substr($hexbytes, 0, $i);
                $escaped = EscapeHex($hexpair);
                $bottom = substr($hexbytes, $i + 2);
                $hexbytes = $top . $escaped . $bottom;
                $i += 8;
            }
            $i += 2;
        }
        $string = hex2bin($hexbytes);
        return $string;
    }
    function EscapeHex($string) //Helper function for EscapeNonASCII()
    {
        $x = "5C5C78"; //\x
        $topnibble = bin2hex($string[0]); //Convert top nibble to hex
        $bottomnibble = bin2hex($string[1]); //Convert bottom nibble to hex
        $escaped = $x . $topnibble . $bottomnibble; //Concatenate escape sequence "\x" with top and bottom nibble
        return $escaped;
    }

    function UnescapeNonASCII($string) //Convert string to hex, replace escaped hex with actual hex.
    {
        $stringtohex = bin2hex($string);
        $stringtohex = preg_replace_callback('/5c5c78([a-fA-F0-9]{4})/', function ($m) { 
            return hex2bin($m[1]);
        }, $stringtohex);
        return hex2bin(strtoupper($stringtohex));
    }
?>
0