21

PHPではmb_check_encoding()、文字列が有効なUTF-8であるかどうかを判断するために使用できます。ただし、mbstring拡張機能をコンパイルして有効にする必要があるため、これは移植可能なソリューションではありません。さらに、どの文字が無効であるかはわかりません。

特定の文字列の無効なUTF-8バイトに一致する正規表現(または他の100%移植可能なメソッド)はありますか?

そうすれば、必要に応じてこれらのバイトを置き換えることができます(バイナリデータを含むテスト出力XMLファイルを作成する場合など、バイナリ情報を保持します)。したがって、文字をUTF-8に変換すると、情報が失われます。したがって、次のように変換することができます。

"foo" . chr(128) . chr(255)

の中へ

"foo<128><255>"

したがって、文字列が十分ではないことを「検出」するだけで、どの文字が無効であるかを検出できる必要があります。

4

4 に答える 4

39

このPCRE正規表現を使用して、文字列内の有効なUTF-8を確認できます。正規表現が一致する場合、文字列には無効なバイトシーケンスが含まれています。コンパイルするのにPCRE_UTF8に依存しないため、100%移植可能です。

$regex = '/(
    [\xC0-\xC1] # Invalid UTF-8 Bytes
    | [\xF5-\xFF] # Invalid UTF-8 Bytes
    | \xE0[\x80-\x9F] # Overlong encoding of prior code point
    | \xF0[\x80-\x8F] # Overlong encoding of prior code point
    | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
    | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
    | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
    | (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
    | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
    | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
    | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
    | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
)/x';

テキストのバリエーションをいくつか作成することでテストできます。

// Overlong encoding of code point 0
$text = chr(0xC0) . chr(0x80);
var_dump(preg_match($regex, $text)); // int(1)
// Overlong encoding of 5 byte encoding
$text = chr(0xF8) . chr(0x80) . chr(0x80) . chr(0x80) . chr(0x80);
var_dump(preg_match($regex, $text)); // int(1)
// Overlong encoding of 6 byte encoding
$text = chr(0xFC) . chr(0x80) . chr(0x80) . chr(0x80) . chr(0x80) . chr(0x80);        
var_dump(preg_match($regex, $text)); // int(1)
// High code-point without trailing characters
$text = chr(0xD0) . chr(0x01);
var_dump(preg_match($regex, $text)); // int(1)

等...

実際、これは無効なバイトと一致するため、preg_replaceで使用してそれらを置き換えることができます。

preg_replace($regex, '', $text); // Remove all invalid UTF-8 code-points
于 2012-07-29T12:53:39.397 に答える
11

PHPがPCREでコンパイルされていると仮定すると、ほとんどの場合、UTF-8でも有効になっています。したがって、質問で明示的に求められているように、この非常に単純な正規表現は、無効なUTF-8文字列を検出できます。これは、それらが一致しないためです。

preg_match('//u', $string);

次に、u修飾子(PCRE_UTF8)が常に使用可能であるとは限らず、この質問が示すように、これが発生する可能性があると主張できます。

しかし、私の実際の開発者の生活では、これは決して問題ではありませんでした。さらに、PCRE拡張機能がまったく利用できないという問題があります。これにより、PCREを含む回答は役に立たなくなります(ここでは私の場合でも)。しかし、ほとんどの場合、その問題は、今日から数年を引いた時点での過去の問題でした。

これに似たより長い答えが、どういうわけか重複した質問で与えられています:

したがって、この質問は、提案された回答がもたらす利点の多くを浮き彫りにする必要があると思います。

于 2012-07-29T14:02:53.387 に答える
4

W3Cには、有効なUTF-8文字列に一致する次のPerl正規表現を一覧表示するページ( Multilingual form encodingというタイトル)があります。

(これは、無効なUTF-8文字列に一致するこのSO質問への別の回答にリストされている正規表現の反対であることに注意してください。)

#  Returns true if $field is UTF-8, and false otherwise.

$field =~
  m/\A(
     [\x09\x0A\x0D\x20-\x7E]            # ASCII
   | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
   |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
   | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
   |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
   |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
   | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
   |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
  )*\z/x;
于 2013-12-29T17:09:28.583 に答える
0

これは、Unicode文字、linke絵文字、ロシア語または中国語を検出するために機能します。

private function has_unicode($string)
{
    $pattern = '/^.*[^\x{00}-\x{00FF}]+.*$/u';
    return preg_match($pattern, $string) ? true : false;
}
于 2020-08-04T21:46:02.957 に答える