【问题标题】:UTF8 Encoding problem - With good examplesUTF8 编码问题 - 有很好的例子
【发布时间】:2011-05-04 23:58:13
【问题描述】:

我有以下字符编码问题,不知何故,我设法将具有不同字符编码的数据保存到我的数据库 (UTF8) 下面的代码和输出显示了 2 个示例字符串及其输出方式。其中 1 个需要更改为 UTF8,而另一个已经更改为 UTF8。

我应该/应该如何检查是否应该对字符串进行编码?例如 我需要正确输出每个字符串,那么如何检查是否已经是utf8或者是否需要转换?

我使用的是 PHP 5.2,mysql myisam 表:

CREATE TABLE IF NOT EXISTS `entities` (
  ....
  `title` varchar(255) NOT NULL
  ....
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

<?php
$text = $entity['Entity']['title'];
echo 'Original : ', $text."<br />";
echo 'UTF8 Encode : ', utf8_encode($text)."<br />";
echo 'UTF8 Decode : ', utf8_decode($text)."<br />";
echo 'TRANSLIT : ', iconv("ISO-8859-1", "UTF-8//TRANSLIT", $text)."<br />";
echo 'IGNORE TRANSLIT : ', iconv("ISO-8859-1", "UTF-8//IGNORE//TRANSLIT", $text)."<br />";
echo 'IGNORE   : ', iconv("ISO-8859-1", "UTF-8//IGNORE", $text)."<br />";
echo 'Plain    : ', iconv("ISO-8859-1", "UTF-8", $text)."<br />";
?>

输出 1:

Original : France Télécom
UTF8 Encode : France Télécom
UTF8 Decode : France T�l�com
TRANSLIT : France Télécom
IGNORE TRANSLIT : France Télécom
IGNORE : France Télécom
Plain : France Télécom

输出 2:###

Original : Cond� Nast Publications
UTF8 Encode : Condé Nast Publications
UTF8 Decode : Cond?ast Publications
TRANSLIT : Condé Nast Publications
IGNORE TRANSLIT : Condé Nast Publications
IGNORE : Condé Nast Publications
Plain : Condé Nast Publications

感谢您抽出宝贵的时间。字符编码和我相处得不太好!

更新:

echo strlen($string)."|".strlen(utf8_encode($string))."|";
echo (strlen($string)!==strlen(utf8_encode($string))) ? $string : utf8_encode($string);
echo "<br />";
echo strlen($string)."|".strlen(utf8_decode($string))."|";
echo (strlen($string)!==strlen(utf8_decode($string))) ? $string : utf8_decode($string);
echo "<br />";

23|24|Cond� Nast Publications
23|21|Cond� Nast Publications

16|20|France Télécom
16|14|France Télécom

【问题讨论】:

  • 从上面看,第一个字符串已经是UTF-8,第二个是ISO-8859-1。但是你的问题是什么?
  • 我每个字符串都可以正确输出,那么如何检查是否已经是utf8或者是否需要转换呢?
  • 不确定,但请看这里 - dev.mysql.com/doc/refman/5.0/en/… - 结合 mysql 功能,您只需一个更新查询即可完成您想要的操作。
  • 我也认为修复一次数据库比在每个请求上重新编码字符串更好。

标签: php mysql utf-8 character-encoding


【解决方案1】:

这可能是mb_detect_encoding() 函数的工作。

根据我有限的经验,当用作通用“编码嗅探器”时,它并不是 100% 可靠 - 它会检查某些字符和字节值的存在以做出有根据的猜测 - 但在这种狭隘的情况下(它'只需区分 UTF-8 和 ISO-8859-1 )它应该工作。

<?php
$text = $entity['Entity']['title'];

echo 'Original : ', $text."<br />";
$enc = mb_detect_encoding($text, "UTF-8,ISO-8859-1");

echo 'Detected encoding '.$enc."<br />";

echo 'Fixed result: '.iconv($enc, "UTF-8", $text)."<br />";

?>

对于不包含特殊字符的字符串,您可能会得到不正确的结果,但这不是问题。

【讨论】:

  • 根据我的经验 mb_detect_encoding() 根本不可靠。我过去曾尝试使用它,但它为这么多字符串返回完全错误的编码。
  • @Richard 它应该适用于这么一组可能的编码(UTF-8 应该相对容易与 ISO 区分开来)......我们将看看它是如何工作的
  • 根据我的经验, encoding_list 的顺序很重要。 "UTF-8,ISO-8859-1" 将给出除 "ISO-8859-1,UTF-8" 之外的其他结果
  • 我快疯了,非常感谢@Pekka 웃 提供的解决方案
  • 对我来说,这是唯一的解决方案。 SQL Server 数据库有一些问题。谢谢@Pekka웃 的分享!
【解决方案2】:

我创建了一个函数来解决所有这些问题。它被称为 Encoding::toUTF8()。

<?php
$text = $entity['Entity']['title'];
echo 'Original : ', $text."<br />";
echo 'Encoding::toUTF8 : ', Encoding::toUTF8($text)."<br />";
?>

输出:

Original : France Télécom
Encoding::toUTF8 : France Télécom

Original : Cond� Nast Publications
Encoding::toUTF8 : Condé Nast Publications

您不需要知道字符串的编码是什么,只要您知道它是在 Latin1 (iso 8859-1)、Windows-1252 或 UTF8 上。字符串也可以混合使用。

Encoding::toUTF8() 会将所有内容都转换为 UTF8。

我这样做是因为一项服务向我提供了一个混乱的数据源,将 UTF8 和 Latin1 混合在同一个字符串中。

用法:

$utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);

$latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);

下载:

http://dl.dropbox.com/u/186012/PHP/forceUTF8.zip

我已经包含了另一个函数,Encoding::fixUFT8(),它将修复每个看起来乱码的 UTF8 字符串。

用法:

$utf8_string = Encoding::fixUTF8($garbled_utf8_string);

例子:

echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");

将输出:

Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football

【讨论】:

  • 谢谢!你能用这个创建一个 github,我很乐意做一些升级。
【解决方案3】:

另一种方式,可能更快,更不可靠:

echo (strlen($str)!==strlen(utf8_decode($str)))
  ? $str                //is multibyte, leave as is
  : utf8_encode($str);  //encode

它比较原始字符串和 utf8_decoded 字符串的长度。 一个包含多字节字符的字符串,其 strlen 与类似的单字节编码 strlen 不同。

例如:

strlen('Télécom') 

应该在 Latin1 中返回 7,在 UTF8 中返回 9

【讨论】:

  • 是的,这似乎是最好的选择。他仍然应该在做任何事情之前备份他的数据库:)
  • 这个方法应该也可以直接应用到数据库中,通过动态转换字符集并比较字节长度(我认为mySQL有一个函数)......就像更快地修复数据库的想法
  • @Lizard 我认为你错误地实现了它。您需要输出utf8_decode 以查看它是否有效(您要输出两次编码版本)
【解决方案4】:

我制作了这 2 个小函数,它们可以很好地与 UTF-8 和 ISO-8859-1 检测/转换配合使用...

function detect_encoding($string)
{
    //http://w3.org/International/questions/qa-forms-utf-8.html
    if (preg_match('%^(?: [\x09\x0A\x0D\x20-\x7E] | [\xC2-\xDF][\x80-\xBF] | \xE0[\xA0-\xBF][\x80-\xBF] | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} | \xED[\x80-\x9F][\x80-\xBF] | \xF0[\x90-\xBF][\x80-\xBF]{2} | [\xF1-\xF3][\x80-\xBF]{3} | \xF4[\x80-\x8F][\x80-\xBF]{2} )*$%xs', $string))
        return 'UTF-8';

    //If you need to distinguish between UTF-8 and ISO-8859-1 encoding, list UTF-8 first in your encoding_list.
    //if you list ISO-8859-1 first, mb_detect_encoding() will always return ISO-8859-1.
    return mb_detect_encoding($string, array('UTF-8', 'ASCII', 'ISO-8859-1', 'JIS', 'EUC-JP', 'SJIS'));
}

function convert_encoding($string, $to_encoding, $from_encoding = '')
{
    if ($from_encoding == '')
        $from_encoding = detect_encoding($string);

    if ($from_encoding == $to_encoding)
        return $string;

    return mb_convert_encoding($string, $to_encoding, $from_encoding);
}

如果您的数据库包含 2 个不同字符集中的字符串,我会做的不是用字符集检测/转换来困扰您的所有应用程序代码,而是编写一个“一次性”脚本,该脚本将读取您的所有表记录并更新它们的字符串到正确的格式(如果我是你,我会选择 UTF-8)。这样,您的代码将更简洁,更易于维护。

只需在数据库的每个表中循环记录并像这样转换字符串:

//if the 3rd param is not specified the "from encoding" is detected automatically
$newString = convert_encoding($oldString, 'UTF-8');

【讨论】:

    【解决方案5】:

    我没有在这里尝试您的示例,但根据过去的经验,有一个快速解决方法。在数据库连接后立即执行以下查询,然后再运行任何其他查询:

    SET NAMES UTF8;
    

    这是符合 SQL 标准的,并且适用于其他数据库,例如 Firebird 和 PostgreSQL。

    但请记住,您还需要确保在其他位置使用 UTF-8 声明,以使您的应用程序正常运行。遵循快速清单。

    • 所有文件都应保存为 UTF-8(最好没有 BOM [字节顺序掩码])
    • 您的 HTTP 服务器应发送编码标头 UTF-8。使用 Firebug 或 Live HTTP 标头进行检查。
    • 如果您的服务器压缩和/或标记化响应,您可能会看到标头内容被分块或压缩。如果您将文件保存为 UTF-8 和
    • ,这不是问题
    • 使用适当的元标记将编码声明为 HTML 标头。
    • 在所有应用程序(套接字、文件系统、数据库...)中,不要忘记在任何时候都标记 UTF-8。在打开数据库连接时进行此操作可以帮助您无需一直编码/解码/调试。通过 root 获取它们。

    【讨论】:

    • 没有解决 OP 的问题。他有一个包含两种混合编码的数据集,他不知道哪一行是哪一行。
    • 我明白了……我给出了一个全面的答案。这个案子不太好,也没有真正解决@Lizard的问题。 @Pekka's 和@Dr.Molle's 走上了正轨。需要一个函数来根据需要进行检测和转换。
    【解决方案6】:
    1. 您使用什么数据库?
    2. 在转换为 utf-8 之前,您需要知道原始字符串的字符集,如果它在 ISO-8859-1 (latin1) 中,则 utf8_encode() 是最简单的方法,否则您需要使用 icov 或要转换的 mbstring 库,这两个都需要知道输入的字符集才能正确转换。
    3. 当您插入/选择数据时,您会告诉数据库有关字符集的信息吗?

    【讨论】:

      猜你喜欢
      • 2013-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-14
      相关资源
      最近更新 更多