在PHP中,我们可以使用mb_check_encoding()
来确定一个字符串是否是有效的UTF-8。 但是,因为它需要mbstring扩展在编译和启用,这不是一个便携式解决方案。 此外,它不会告诉我们哪个字符是无效的。
是否有一个正则表达式(或另一其它100%便携式方法),其可以给定的字符串在匹配无效UTF-8字节。 这样一来,这些字节可以根据需要进行更换(保持二进制信息,例如构建包括二进制数据的测试输出xml文件时)。 所以字符转换为UTF-8会丢失信息。 所以,我们可能要转换:
"foo" . chr(128) . chr(255)
成
"foo<128><255>"
因此,只要“检测”该字符串是不够的,我们需要能够检测哪些字符无效。
您可以使用此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
我把这个在这里的完整性:
假设PHP编译时PCRE它经常也与UTF-8启用。 从而明确要求在这个问题很简单的正则表达式可以检测无效的UTF-8字符串,因为这些不匹配:
preg_match('//u', $string);
然后,您可以论点,即u
修饰符(PCRE_UTF8)并不总是可用的,和真实,这可能发生的这个问题表示:
然而,在我的实际开发过这从来都不是一个问题。 更该PCRE扩展不可用在所有的问题,这将使任何含答案PCRE无用(连我都在这里)。 但多数情况下这个问题是更多的像今天减去一些年的过去的问题。
与此类似一个较为漫长的答案已经给出在某种程度上重复的问题:
所以我觉得这个问题应该突出更多的好处建议回答附带。
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;