正则表达式来检测无效的UTF-8字符串(Regex to detect Invalid UTF-8

2019-06-17 13:57发布

在PHP中,我们可以使用mb_check_encoding()来确定一个字符串是否是有效的UTF-8。 但是,因为它需要mbstring扩展在编译和启用,这不是一个便携式解决方案。 此外,它不会告诉我们哪个字符是无效的。

是否有一个正则表达式(或另一其它100%便携式方法),其可以给定的字符串在匹配无效UTF-8字节。 这样一来,这些字节可以根据需要进行更换(保持二进制信息,例如构建包括二进制数据的测试输出xml文件时)。 所以字符转换为UTF-8会丢失信息。 所以,我们可能要转换:

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

"foo<128><255>"

因此,只要“检测”该字符串是不够的,我们需要能够检测哪些字符无效。

Answer 1:

您可以使用此PCRE正则表达式来检查字符串有效UTF8。 如果正则表达式匹配的字符串包含无效的字节序列。 这是100%,便携,因为它不依赖于PCRE_UTF8中进行编译。

$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
    | (?<=[\x0-\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


Answer 2:

我把这个在这里的完整性:

假设PHP编译时PCRE它经常也与UTF-8启用。 从而明确要求在这个问题很简单的正则表达式可以检测无效的UTF-8字符串,因为这些不匹配:

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

然后,您可以论点,即u修饰符(PCRE_UTF8)并不总是可用的,和真实,这可能发生的这个问题表示:

  • 什么是preg_match_all u标志依赖?

然而,在我的实际开发过这从来都不是一个问题。 更该PCRE扩展不可用在所有的问题,这将使任何含答案PCRE无用(连我都在这里)。 但多数情况下这个问题是更多的像今天减去一些年的过去的问题。

与此类似一个较为漫长的答案已经给出在某种程度上重复的问题:

  • 如何检测畸形的UTF-8字符串在PHP?

所以我觉得这个问题应该突出更多的好处建议回答附带。



Answer 3:

W3C有一个页面(标题为多种语言的形式的编码 ) ,该目录,其有效的UTF-8字符串匹配的下面的Perl正则表达式。

(注意,这是在另一个回答这个问题SO其中一个无效的UTF-8字符串匹配列出的正则表达式的相反)。

#  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;


文章来源: Regex to detect Invalid UTF-8 String