UTF8编码问题 - 有很好的例子

时间:2010-11-04 10:51:26

标签: php mysql utf-8 character-encoding

我有以下字符编码问题,不知何故我设法将不同字符编码的数据保存到我的数据库(UTF8)下面的代码和输出显示了2个示例字符串以及它们的输出方式。其中1个需要更改为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

6 个答案:

答案 0 :(得分:26)

这可能是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 />";

?>

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

答案 1 :(得分:9)

我做了一个解决所有这些问题的函数。它叫做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

答案 2 :(得分:6)

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

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

答案 3 :(得分:1)

我将这两个小功能与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个不同字符集中的字符串,我会做的不是用charset检测/转换来困扰所有应用程序代码,而是写一个“一次性”脚本,它将读取所有表记录并更新其字符串格式正确(如果我是你,我会选择UTF-8)。这样,您的代码将更清晰,更易于维护。

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

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

答案 4 :(得分:0)

我没有在这里试用你的样品,但从过去的经验来看,有一个快速的解决方案。在数据库连接之后,在运行任何其他查询之前执行以下查询:

SET NAMES UTF8;

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

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

  • 所有文件都应保存为UTF-8(首选无BOM [字节顺序掩码])
  • 您的HTTP Server应发送编码标头UTF-8。使用Firebug或Live HTTP Headers进行检查。
  • 如果您的服务器压缩和/或标记化响应,您可能会看到标题内容为chunked或gzip。如果将文件保存为UTF-8和
  • ,则不会出现此问题
  • 使用正确的元标记将编码声明为HTML标头。
  • 在所有应用程序(套接字,文件系统,数据库......)中,不要忘记每次都标记UTF-8。在打开数据库连接时这样做可以帮助您不需要一直编码/解码/调试。 Grab'em by root。

答案 5 :(得分:-1)

  1. 您使用什么数据库?
  2. 在将其转换为utf-8之前,您需要知道原始字符串的字符集,如果它在ISO-8859-1(latin1)中,则utf8_encode()是最简单的方法,否则您需要使用icov或要转换的mbstring lib,这两个都需要知道输入的字符集才能正确转换。
  3. 插入/选择数据时,是否告诉数据库有关字符集的信息?