【问题标题】:How can I detect a malformed UTF-8 string in PHP?如何在 PHP 中检测格式错误的 UTF-8 字符串?
【发布时间】:2011-07-17 11:36:23
【问题描述】:

iconv 函数有时会报错:

Notice:
iconv() [function.iconv]:
Detected an incomplete multibyte character in input string in [...]

有没有办法在将数据发送到 inconv() 之前检测 UTF-8 字符串中是否存在非法字符?

【问题讨论】:

标签: php encoding utf-8 iconv


【解决方案1】:

首先,请注意无法检测文本是否属于特定的不需要的编码。您只能检查字符串在给定编码中是否有效。

您可以使用自 PHP 4.3.5 起在 preg_match [PHP Manual] 中提供的 UTF-8 有效性检查。如果给出无效字符串,它将返回0(没有附加信息):

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

另一种可能是mb_check_encoding [PHP Manual]:

$validUTF8 = mb_check_encoding($string, 'UTF-8');

您可以使用的另一个函数是mb_detect_encoding [PHP Manual]

$validUTF8 = ! (false === mb_detect_encoding($string, 'UTF-8', true));

strict 参数设置为true 很重要。

此外,iconv [PHP Manual] 允许您动态更改/删除无效序列。 (但是,如果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('!.!u', $str) 可以解决问题 - 在尝试查找任何内容之前,它会默默地检查 str 是否为 utf-8。 - 甚至不需要正则表达式中的那个点
  • @user393087:我做了一些小改动,使preg_match 方法也能在空字符串上正常工作。
  • @hakre:感谢您提供的精彩内容。
  • 所有选项的概览!我写了一个 micro-benchmark 来看看哪个更快 - preg_match() 似乎是最快的(在 PHP 7 下),无论是有效/无效字符串还是短/长字符串。
  • @Bananaapple: preg_... PHP 中的函数使用 PCRE 库,如果出现此错误,PHP 常量会反映来自 PCRE 库的错误代码 (PCRE_ERROR_BADUTF8)。 PHP 7.3 got a migration from PCRE to PCRE2 在引擎盖下。无论您使用的是 PHP 版本,您都可以通过检查 PCRE_VERSION 常量和/或 phpinfo() 来检查正在使用的 PCRE 库版本。这可能会揭示变化。有关技术详细信息,请查看 pcre.org/current/doc/html/pcre2unicode.html 以获取“UTF-8 字符串中的错误”。
【解决方案2】:

您可以尝试使用mb_detect_encoding 来检测您是否有不同的字符集(与UTF-8 不同),然后在需要时使用mb_convert_encoding 转换为UTF-8。人们更有可能给你提供不同字符集的有效内容,而不是给你无效的 UTF-8。

【讨论】:

  • 请注意,有效的 ASCII 字符串也是有效的 UTF8 字符串。这意味着 mb_detect_encoding 将为任何有效的 UTF8 字符串且不包含任何 Unicode 字符的字符串返回“ASCII”
【解决方案3】:

关于哪些字符在 UTF-8 中无效的规范非常明确。您可能想在尝试解析之前将其剥离。他们不应该在那里,所以如果你能在生成 XML 之前避免它,那就更好了。

参考这里:

http://www.w3.org/TR/xml/#charsets

这不是一个完整的列表。许多解析器也不允许一些低编号的控制字符,但我现在找不到完整的列表。

不过,iconv 可能对此有内置支持:

http://www.zeitoun.net/articles/clear-invalid-utf8/start

【讨论】:

    【解决方案4】:

    在 iconv() 前面放一个@ 以抑制 NOTICE 并在源编码 id 中的 UTF-8 之后放一个 //IGNORE 以忽略无效字符:

    @iconv('UTF-8//IGNORE', $destinationEncoding, $yourString);
    

    【讨论】:

    • 我知道如何忽略它,我不知道如何检测它,我不想将它默默地传递给我的代码。
    • 顺便说一句,另一个问题中的preg_match() 解决方案非常有趣,我会同意的。
    猜你喜欢
    • 1970-01-01
    • 2014-05-20
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-09
    相关资源
    最近更新 更多