iconv 函数有时会给我一个错误:
Notice:
iconv() [function.iconv]:
Detected an incomplete multibyte character in input string in [...]
有没有办法在将数据发送到 inconv() 之前检测 UTF-8 字符串中是否存在非法字符?
首先,请注意,无法检测文本是否属于特定的不需要的编码。您只能检查字符串在给定编码中是否有效。
preg_match
[PHP 手册] 中提供的 UTF-8 有效性检查。如果给出无效字符串,它将返回 empty1(没有附加信息²):
$validUTF8 = (bool) preg_match('//u', $string);
mb_check_encoding
[PHP手册]:
$validUTF8 = mb_check_encoding($string, 'UTF-8');
mb_detect_encoding
[PHP 手册]:
$validUTF8 = ! (false === mb_detect_encoding($string, 'UTF-8', true));
将
strict
参数设置为 true
非常重要。
iconv
[PHP 手册] 允许您动态更改/删除无效序列。 (但是,如果 iconv
遇到这样的序列,它会生成通知;此行为无法更改。)
echo 'TRANSLIT : ', iconv("UTF-8", "ISO-8859-1//TRANSLIT", $string), PHP_EOL;
echo 'IGNORE : ', iconv("UTF-8", "ISO-8859-1//IGNORE", $string), PHP_EOL;
您可以使用
@
并检查返回字符串的长度:
strlen($string) === strlen(@iconv('UTF-8', 'UTF-8//IGNORE', $string));
同时查看
iconv
手册页上的示例。
备注:
¹ preg_match() 空 返回值:
0
直到5.3.3(含)false
自5.3.4起。(4.3.5 之前/直到 4.3.4:
//u
测试没有用,因为它在主题字符串 1
上返回 "\x80"
,这不是 UTF-8 中的完整二进制序列,最多只是一个连续字节)
² 没有附加信息:
原始
0
返回值本身不包含任何附加信息,preg_match() 也不产生诊断消息。
正如之前在评论中概述的那样,可以获得更多信息,特别是在匹配错误(不匹配)的情况下会出现 PREG_*_ERROR。
这是通过调用 preg_last_error()PHP >= 5.2 after preg_match() 并针对 PREG_BAD_UTF8_ERROR 测试返回整数值来识别主题字符串不是 UTF-8 来实现的。
对于诊断消息,请使用 preg_last_error_msg()PHP >= 8,它返回字符串“格式错误的 UTF-8 字符,可能编码错误”(不带引号),因为最后一个错误是 PREG_BAD_UTF8_ERROR。
对于使用 json_encode 的人,请尝试 json_last_error
<?php
// An invalid UTF8 sequence
$text = "\xB1\x31";
$json = json_encode($text);
$error = json_last_error();
var_dump($json, $error === JSON_ERROR_UTF8);
输出(例如 PHP 版本 5.3.3 - 5.3.13、5.3.15 - 5.3.29、5.4.0 - 5.4.45)
string(4) "null"
bool(true)
您可以尝试使用
mb_detect_encoding
来检测是否有不同的字符集(与 UTF-8 不同),然后根据需要使用 mb_convert_encoding
转换为 UTF-8。 人们更有可能为您提供不同字符集的有效内容,而不是为您提供无效的 UTF-8。
UTF-8 中哪些字符无效的规范非常清楚。您可能想在尝试解析它之前将其删除。它们不应该在那里,所以如果您可以在生成 XML 之前避免它,那就更好了。
请参阅此处以获取参考:
http://www.w3.org/TR/xml/#charsets
这不是完整的列表。许多解析器也不允许一些低编号的控制字符,但我现在找不到完整的列表。
但是, iconv 可能对此有内置支持:
在 iconv() 前面放置一个
@
以抑制 NOTICE,并在源编码 id 中的 UTF-8 后面放置一个 //IGNORE 以忽略无效字符:
@iconv('UTF-8//IGNORE', $destinationEncoding, $yourString);