确保 PHP 中的 UTF-8 有效

2022-08-30 16:58:13

我正在使用PHP来处理来自各种来源的文本。我预计它不会是UTF-8,ISO 8859-1Windows-1252以外的任何东西。如果它不是其中之一,我只需要确保文本变成有效的UTF-8字符串,即使字符丢失。iconv 的 //TRANSLIT 选项是否解决了这个问题?

例如,此代码是否可确保字符串可以安全地插入到 UTF-8 编码的文档(或数据库)中?

function make_safe_for_utf8_use($string) {

    $encoding = mb_detect_encoding($string, "UTF-8,ISO-8859-1,WINDOWS-1252");

    if ($encoding != 'UTF-8') {
        return iconv($encoding, 'UTF-8//TRANSLIT', $string);
    }
    else {
        return $string;
    }
}

答案 1

UTF-8 可以存储任何 Unicode 字符。如果您的编码是其他任何东西,包括ISO-8859-1或Windows-1252,UTF-8可以存储其中的每个字符。因此,当您将字符串从任何其他编码转换为 UTF-8 时,不必担心丢失任何字符。

此外,ISO-8859-1 和 Windows-1252 都是单字节编码,其中任何字节都有效。从技术上讲,无法区分它们。我会选择Windows-1252作为非UTF-8序列的默认匹配项,因为解码方式不同的字节是唯一0x80-0x9F范围。这些解码为各种字符,如Windows-1252中的智能引号和欧元,而在ISO-8859-1中,它们是几乎从未使用的不可见的控制字符。Web浏览器有时可能会说他们正在使用ISO-8859-1,但通常他们会真正使用Windows-1252。

此代码是否可确保字符串可以安全地插入到 UTF-8 编码文档中

为此,您肯定希望将可选的“strict”参数设置为 TRUE。但我不确定这是否真的涵盖了所有无效的UTF-8序列。该函数不声明显式检查字节序列的 UTF-8 有效性。在已知的情况下,mb_detect_encoding以前会错误地猜测UTF-8,尽管我不知道这是否仍然可以在严格模式下发生。

如果您想确定,请使用W3推荐的正则表达式自己动手:

if (preg_match('%^(?:
      [\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
)*$%xs', $string))
    return $string;
else
    return iconv('CP1252', 'UTF-8', $string);

答案 2

使用 mbstring 库,您有 mb_check_encoding())。

使用示例:

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

在最近的Windows 10系统上使用PHP 7.1.9,正则表达式解决方案在任何字符串长度(仍然是20,000次迭代)下都优于:mb_check_encoding()

  • 10 个字符:正则表达式 = > 4 毫秒,= > 64 毫秒mb_check_encoding()
  • 10000 个字符:正则表达式 = > 125 毫秒,=> 2.4 秒mb_check_encoding()

推荐