确保在PHP中使用有效的utf-8

时间:2009-10-06 03:34:53

标签: php encoding utf-8

我正在使用PHP来处理来自各种来源的文本。我不认为它将是UTF-8,ISO-8859-1或WINDOWS-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;
    }
}

6 个答案:

答案 0 :(得分:37)

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-recommended regex

自行完成
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);

答案 1 :(得分:14)

使用 mbstring 库,您有 mb_check_encoding()

使用示例:

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

当表现很重要时,这比接受的答案中提供的正则表达式更快。<​​/ strike>

我的配置快速测试显示(20,000次迭代):

    <击>
  • 正则表达式: ~310ms
  • mb_check_encoding: ~90ms

修改

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

  • 10个字符:regex =&gt; 4毫秒,mb_check_encoding() =&gt; 64ms的
  • 10000个字符:regex =&gt; 125毫秒,mb_check_encoding() =&gt; 2.4S

答案 2 :(得分:3)

只需注意:您可以使用“u”修饰符来测试字符串的UTF-8有效性,而不是使用经常推荐的(相当复杂的)regular expression by W3C

<?php
  if (preg_match("//u", $string)) {
      // $string is valid UTF-8
  }

答案 3 :(得分:1)

请查看 http://www.phpwact.org/php/i18n/charsets以获取有关字符集的指南。此页面链接到专门针对utf8的页面。

答案 4 :(得分:0)

回答“iconv是幂等的”

也不是iconv - iconv不是幂等的

utf8_encode()与...之间的巨大差异的iconv() 是iconv可能会引发错误,如“在输入字符串中检测到不完整的多字节字符” 即使用

  

iconv('ISO-8859-1','UTF-8'。'// IGNORE',$ str)

在上面的代码中:

  

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

你必须知道mb_detect_encoding即使对于无效的utf-8字符串(形成错误的utf8)也可以回答uft-8

答案 5 :(得分:-1)

不确定这是否可以达到同样的效果,但是你不能在所有文本上使用utf8_encode()而不用担心检测吗?如果文本已经是UTF-8,那么它不会受到伤害。如果不是,它将被转换。如果你已经考虑过这样做,那么这对你不起作用吗?