【问题标题】:Detect encoding and make everything UTF-8检测编码并使一切变为 UTF-8
【发布时间】:2010-10-28 23:44:12
【问题描述】:

我正在从各种 RSS 提要中读取大量文本并将它们插入到我的数据库中。

当然,提要中使用了几种不同的字符编码,例如UTF-8 和 ISO 8859-1。

不幸的是,文本的编码有时会出现问题。示例:

  1. “Fußball”中的“ß”在我的数据库中应如下所示:“Ÿ”。如果是“Ÿ”,则显示正确。

  2. 有时,“Fußball”中的“ß”在我的数据库中看起来像这样:“ß”。那当然是显示错误了。

  3. 在其他情况下,“ß”被保存为“ß” - 因此无需任何更改。然后也显示错了。

我可以做些什么来避免情况 2 和 3?

如何使所有内容都使用相同的编码,最好是 UTF-8?什么时候必须使用utf8_encode(),什么时候必须使用utf8_decode()(效果很清楚,但什么时候必须使用函数?),什么时候必须对输入不做任何事情?

如何使所有内容都使用相同的编码?也许使用函数mb_detect_encoding()?我可以为此编写一个函数吗?所以我的问题是:

  1. 如何找出文本使用的编码方式?
  2. 如何将其转换为 UTF-8 - 无论旧编码是什么?

这样的功能会起作用吗?

function correct_encoding($text) {
    $current_encoding = mb_detect_encoding($text, 'auto');
    $text = iconv($current_encoding, 'UTF-8', $text);
    return $text;
}

我已经测试过了,但它不起作用。它有什么问题?

【问题讨论】:

  • ““Fußball”中的“ß”在我的数据库中应该如下所示:“Ÿ”。”。不,它应该看起来像 ß。确保您的排序规则和连接设置正确。否则排序和搜索会为您中断。
  • 您的数据库设置错误。如果要存储 Unicode 内容,只需为此进行配置。因此,与其尝试解决 PHP 代码中的问题,不如先修复数据库。
  • 使用:$from=mb_detect_encoding($text); $text=mb_convert_encoding($text,'UTF-8',$from);

标签: php encoding utf-8 character-encoding


【解决方案1】:

这很简单:当你得到不是 UTF-8 的东西时,你必须将它编码 UTF-8。

因此,当您获取某个符合 ISO 8859-1 的提要时,请通过 utf8_encode 对其进行解析。

但是,如果您要获取 UTF-8 提要,则无需执行任何操作。

【讨论】:

  • 谢谢!好的,我可以通过使用 mb-detect-encoding() 了解提要是如何编码的,对吧?但是,如果提要是 ASCII,我能做什么? utf8-encode() 仅适用于 ISO-8859-1 到 UTF-8,不是吗?
  • ASCII 是 ISO-8859-1 和 UTF-8 的子集,因此使用 utf8-encode() 不应该进行更改 - 如果它实际上只是 ASCII
  • 所以如果不是 UTF-8,我总是可以使用 utf8_encode 吗?这真的很容易。根据 mb-detect-encoding() 为 ASCII 的文本包含“ä”。这是一个ASCII字符吗?还是 HTML?
  • 那是 HTML。实际上这是编码的,所以当你在给定的页面上打印它时它显示正常。如果你愿意,你可以先 ut8_encode() 然后 html_entity_decode()。
  • 字符 ß 以 UTF-8 编码,字节序列为 0xC39F。用 Windows-1252 解释,该序列表示两个字符  (0xC3) 和 Ÿ (0x9F)。如果你再次用 UTF-8 编码这个字节序列,你会得到 0xC383 0xC29F 代表 Windows-1252 中的 ß。因此,您的错误是将此 UTF-8 编码数据作为 UTF-8 以外的编码处理。这个字节序列显示为您所看到的字符只是一个解释问题。如果您使用其他编码/字符集,您可能会看到其他字符。
【解决方案2】:

计算 RSS 提要的字符编码似乎是 complicated。即使是普通的网页也经常忽略或谎报其编码。

因此您可以尝试使用正确的方法来检测编码,然后回退到某种形式的自动检测(猜测)。

【讨论】:

  • 我不想从提要信息中读出编码。因此,如果提要信息错误,则相同。我想从文本中检测编码。
  • @marco92w:如果声明的编码错误,那不是你的问题。建立标准并不是为了好玩。
  • @Gumbo:但如果你在现实世界中工作,你必须能够处理诸如错误声明编码之类的事情。问题是很难仅从某些文本中猜测(正确)编码。标准很棒,但许多(大多数?)页面/提要不符合标准。
  • @Kevin ORourke:完全正确。那是我的问题。 @Gumbo:是的,这是我的问题。我想读出提要并汇总它们。所以我必须纠正错误的编码。
  • @marco92w:但是如果你不知道正确的编码和当前的编码,你就无法更正编码。这就是 charset/encoding 声明 if for: 描述数据编码的编码。
【解决方案3】:

php.net/mb_detect_encoding

echo mb_detect_encoding($str, "auto");

echo mb_detect_encoding($str, "UTF-8, ASCII, ISO-8859-1");

我真的不知道结果是什么,但我建议您只获取一些具有不同编码的提要,然后尝试mb_detect_encoding 是否有效。

更新
auto 是“ASCII、JIS、UTF-8、EUC-JP、SJIS”的缩写。它返回检测到的字符集,您可以使用该字符集将字符串转换为带有iconv 的utf-8。

<?php
function convertToUTF8($str) {
    $enc = mb_detect_encoding($str);

    if ($enc && $enc != 'UTF-8') {
        return iconv($enc, 'UTF-8', $str);
    } else {
        return $str;
    }
}
?>

我没有测试过,所以不能保证。也许还有更简单的方法。

【讨论】:

  • 谢谢。 'auto' 和 'UTF-8, ASCII, ISO-8859-1' 作为第二个参数有什么区别? “自动”是否具有更多编码?那么使用'auto'会更好,不是吗?如果它真的没有任何错误,那么我只需将“ASCII”或“ISO-8859-1”更改为“UTF-8”。怎么样?
  • 您的函数并非在所有情况下都能正常工作。有时我会收到一个错误:注意:iconv(): Detected an invalid character in input string in ...
【解决方案4】:

检测编码很难。

mb_detect_encoding 的工作原理是根据您通过的候选人数量进行猜测。在某些编码中,某些字节序列是无效的,因此它可以区分各种候选者。不幸的是,有很多编码,其中相同的字节是有效的(但不同)。在这些情况下,无法确定编码;在这些情况下,您可以实现自己的逻辑来进行猜测。例如,来自日本网站的数据可能更有可能采用日本编码。

只要您只处理西欧语言,要考虑的三种主要编码是utf-8iso-8859-1cp-1252。由于这些是许多平台的默认设置,因此它们也最有可能被错误地报告。例如。如果人们使用不同的编码,他们可能会坦率地说,否则他们的软件会经常崩溃。因此,一个好的策略是信任提供者,除非编码被报告为这三者之一。您仍然应该使用mb_check_encoding 仔细检查它是否确实有效(请注意,validbeing 不同 - 相同的输入可能对许多编码都有效)。如果是其中之一,您可以使用mb_detect_encoding 来区分它们。幸运的是,这是相当确定的;您只需要使用正确的检测序列,即UTF-8,ISO-8859-1,WINDOWS-1252

一旦您检测到编码,您需要将其转换为您的内部表示(UTF-8 是唯一明智的选择)。函数utf8_encodeISO-8859-1 转换为UTF-8,因此它只能用于特定的输入类型。对于其他编码,请使用mb_convert_encoding

【讨论】:

  • 非常感谢!哪个更好:mb-convert-encoding() 或 iconv()?我不知道有什么区别。是的,我只需要解析西欧语言,尤其是英语、德语和法语。
  • 我刚刚看到:mb-detect-encoding() 没用。它仅支持 UTF-8、UTF-7、ASCII、EUC-JP、SJIS、eucJP-win、SJIS-win、JIS 和 ISO-2022-JP。不支持对我来说最重要的 ISO-8859-1 和 WINDOWS-1252。所以我不能使用 mb-detect-encoding()。
  • 我的,你是对的。我已经有一段时间没有使用它了。然后,您必须编写自己的检测代码,或使用外部实用程序。 UTF-8 可以相当可靠地确定,因为它的转义序列很有特点。可以区分 wp-1252 和 iso-8859-1,因为 wp-1252 可能包含在 iso-8859-1 中非法的字节。使用 Wikipedia 获取详细信息,或查看 php.net 的 cmets-section,在各种与字符集相关的函数下。
  • 我认为当您查看特殊歌曲出现的形式时,您可以区分不同的编码:德国的“ß”以不同的形式出现:有时是“Ÿ”,有时是“ß”,有时是“ß”。为什么?
  • 是的,但是你需要在比较它之前知道字符串的内容,这首先违背了目的。德语 ß 的外观不同,因为它在不同的编码中具有不同的值。 Somce 字符恰好在不同的编码中以相同的方式表示(例如,ascii 字符集中的所有字符在 utf-8、iso-8859-* 和 wp-1252 中以相同的方式编码),所以只要你使用只是那些字符,他们看起来都一样。这就是为什么它们有时被称为 ascii 兼容的。
【解决方案5】:

您首先必须检测所使用的编码。在解析 RSS 提要(可能通过 HTTP)时,您应该从 Content-Type HTTP header fieldcharset 参数中读取编码。如果不存在,则从XML processing instructionencoding 属性中读取编码。如果也没有,use UTF-8 as defined in the specification


编辑   我可能会这样做:

我会使用cURL 来发送和获取响应。这允许您设置特定的标头字段并获取响应标头。获取响应后,您必须解析 HTTP 响应并将其拆分为标头和正文。然后,标头应包含 Content-Type 标头字段,该字段包含 MIME 类型和(希望)带有编码/字符集的 charset 参数。如果没有,我们将分析 XML PI 中是否存在 encoding 属性并从那里获取编码。如果这也缺失,XML 规范定义使用 UTF-8 作为编码。

$url = 'http://www.lr-online.de/storage/rss/rss/sport.xml';

$accept = array(
    'type' => array('application/rss+xml', 'application/xml', 'application/rdf+xml', 'text/xml'),
    'charset' => array_diff(mb_list_encodings(), array('pass', 'auto', 'wchar', 'byte2be', 'byte2le', 'byte4be', 'byte4le', 'BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable', '7bit', '8bit'))
);
$header = array(
    'Accept: '.implode(', ', $accept['type']),
    'Accept-Charset: '.implode(', ', $accept['charset']),
);
$encoding = null;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
$response = curl_exec($curl);
if (!$response) {
    // error fetching the response
} else {
    $offset = strpos($response, "\r\n\r\n");
    $header = substr($response, 0, $offset);
    if (!$header || !preg_match('/^Content-Type:\s+([^;]+)(?:;\s*charset=(.*))?/im', $header, $match)) {
        // error parsing the response
    } else {
        if (!in_array(strtolower($match[1]), array_map('strtolower', $accept['type']))) {
            // type not accepted
        }
        $encoding = trim($match[2], '"\'');
    }
    if (!$encoding) {
        $body = substr($response, $offset + 4);
        if (preg_match('/^<\?xml\s+version=(?:"[^"]*"|\'[^\']*\')\s+encoding=("[^"]*"|\'[^\']*\')/s', $body, $match)) {
            $encoding = trim($match[1], '"\'');
        }
    }
    if (!$encoding) {
        $encoding = 'utf-8';
    } else {
        if (!in_array($encoding, array_map('strtolower', $accept['charset']))) {
            // encoding not accepted
        }
        if ($encoding != 'utf-8') {
            $body = mb_convert_encoding($body, 'utf-8', $encoding);
        }
    }
    $simpleXML = simplexml_load_string($body, null, LIBXML_NOERROR);
    if (!$simpleXML) {
        // parse error
    } else {
        echo $simpleXML->asXML();
    }
}

【讨论】:

  • 谢谢。这很容易。但它真的有用吗? HTTP 标头或 XML 的属性中经常出现错误的编码。
  • 再次:这不是你的问题。制定标准是为了避免此类麻烦。如果其他人不关注他们,那是他们的问题,而不是你的问题。
  • 好的,我想你现在终于说服了我。 :)
  • 感谢您的代码。但是为什么不简单地使用它呢? paste.bradleygill.com/index.php?paste_id=9651你的代码要复杂得多,有什么更好的?
  • 好吧,首先你发出两个请求,一个用于 HTTP 标头,一个用于数据。其次,您正在寻找charset=encoding= 的任何外观,而不仅仅是在适当的位置。第三,你没有检查声明的编码是否被接受。
【解决方案6】:

你的编码看起来像你编码成 UTF-8 两次;也就是说,从其他编码,到 UTF-8,然后再到 UTF-8。好像您有 ISO 8859-1,从 ISO 8859-1 转换为 UTF-8,并将新字符串视为 ISO 8859-1 以再次转换为 UTF-8。

这是你所做的一些伪代码:

$inputstring = getFromUser();
$utf8string = iconv($current_encoding, 'utf-8', $inputstring);
$flawedstring = iconv($current_encoding, 'utf-8', $utf8string);

你应该试试:

  1. 使用mb_detect_encoding() 或您喜欢使用的任何方式检测编码
  2. 如果是UTF-8,转换成ISO 8859-1,重复步骤1
  3. 最后,转换回 UTF-8

假设在“中间”转换中您使用了 ISO 8859-1。如果您使用的是 Windows-1252,则转换为 Windows-1252 (latin1)。原始源编码并不重要;您在有缺陷的第二次转换中使用的那个是。

这是我对发生的事情的猜测;要获得四个字节来代替一个扩展的 ASCII 字节,您几乎没有其他办法。

德语也使用ISO 8859-2Windows-1250 (Latin-2)。

【讨论】:

    【解决方案7】:

    此备忘单列出了一些与 PHP 中的 UTF-8 处理相关的常见注意事项: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

    检测字符串中的多字节字符的这个函数也可能很有帮助 (source):

    
    function detectUTF8($string)
    {
        return preg_match('%(?:
            [\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);
    }
    

    【讨论】:

    • 我认为这不能正常工作:echo detectUTF8('3٣3'); #1
    【解决方案8】:

    我知道这是一个较老的问题,但我认为有用的答案永远不会有害。我在桌面应用程序、SQLite 和 GET/POST 变量之间的编码存在问题。有些是 UTF-8,有些是 ASCII,当涉及到外来字符时,基本上一切都会搞砸。

    这是我的解决方案。它会在处理之前的每个页面加载时清除您的 GET/POST/REQUEST(我省略了 cookie,但您可以根据需要添加它们)。它在标题中运行良好。如果 PHP 无法自动检测到源编码,PHP 会抛出警告,因此这些警告会被 @'s 抑制。

    //Convert everything in our vars to UTF-8 for playing nice with the database...
    //Use some auto detection here to help us not double-encode...
    //Suppress possible warnings with @'s for when encoding cannot be detected
    try
    {
        $process = array(&$_GET, &$_POST, &$_REQUEST);
        while (list($key, $val) = each($process)) {
            foreach ($val as $k => $v) {
                unset($process[$key][$k]);
                if (is_array($v)) {
                    $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = $v;
                    $process[] = &$process[$key][@mb_convert_encoding($k,'UTF-8','auto')];
                } else {
                    $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = @mb_convert_encoding($v,'UTF-8','auto');
                }
            }
        }
        unset($process);
    }
    catch(Exception $ex){}
    

    【讨论】:

    • 谢谢你的回答,jocull。函数 mb_convert_encoding() 是我们已经拥有的,对吧? ;) 所以你的答案中唯一的新东西是改变所有变量编码的循环。
    【解决方案9】:

    可以在php.net 上找到实现isUTF8 函数的真正好方法:

    function isUTF8($string) {
        return (utf8_encode(utf8_decode($string)) == $string);
    }
    

    【讨论】:

    • 不幸的是,这仅在字符串仅包含 ISO-8859-1 中包含的字符时才有效。但这可以工作:@iconv('utf-8', 'utf-8//IGNORE', $str) == $str
    • 它不能正常工作:echo (int)isUTF8('z'); # 1 echo (int)isUTF8(NULL); #1
    • 虽然不完美,但我认为这是实现粗略 UTF-8 检查的好方法。
    • mb_check_encoding($string, 'UTF-8')
    • 只是为了说明这将是多么糟糕:ISO 8859-1 中有 191 个可打印字符; Unicode 13 定义了大约 140000。因此,如果您选择一个随机的 Unicode 字符,将其正确编码为 UTF-8,并将其传递给此函数,则此函数错误返回 false 的可能性超过 99%。如果您认为这些是晦涩的字符,请注意 ISO 8859-1 没有欧元符号,因此 isUTF8('€') 将在 99% 之内。
    【解决方案10】:

    如果将utf8_encode() 应用于已经是UTF-8 的字符串,它将返回乱码的UTF-8 输出。

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

    您不需要知道字符串的编码是什么。它可以是 Latin1(ISO 8859-1)Windows-1252 或 UTF-8,或者字符串可以混合使用它们。Encoding::toUTF8() 会将所有内容转换为 UTF-8。

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

    用法:

    require_once('Encoding.php');
    use \ForceUTF8\Encoding;  // It's namespaced now.
    
    $utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);
    
    $latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);
    

    下载:

    https://github.com/neitanod/forceutf8

    我添加了另一个函数Encoding::fixUFT8(),它将修复每个看起来乱码的 UTF-8 字符串。

    用法:

    require_once('Encoding.php');
    use \ForceUTF8\Encoding;  // It's namespaced now.
    
    $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
    

    我已将函数 (forceUTF8) 转换为名为 Encoding 的类上的一系列静态函数。新功能是Encoding::toUTF8()

    【讨论】:

    • 好吧,如果你看一下代码,fixUTF8 只是一次又一次地调用 forceUTF8 直到字符串原样返回。一次调用 fixUTF8() 所花费的时间至少是调用 forceUTF8() 的时间的两倍,因此它的性能要低得多。我做了 fixUTF8() 只是为了创建一个可以修复“编码损坏”文件的命令行程序,但在实时环境中很少需要。
    • 如何将非 UTF8 字符转换为 UTF8,而不知道无效字符的编码是什么?
    • 它假定 ISO-8859-1,答案已经说明了这一点。 forceUTF8() 和 utf8_encode() 的唯一区别是 forceUTF8() 识别 UTF8 字符并保持不变。
    • “你不需要知道你的字符串的编码是什么。” - 我非常不同意。猜测和尝试可能会奏效,但你迟早会遇到行不通的极端情况。
    • 我完全同意。事实上,我并不是说作为一般规则,只是说明如果你碰巧遇到这种情况,这门课可能会对你有所帮助。
    【解决方案11】:

    有点抬头。您说“ß”应该在数据库中显示为“Ÿ”。

    这可能是因为您使用的是 Latin-1 字符编码的数据库,或者您的 PHP-MySQL 连接设置错误,也就是说,P 认为您的 MySQL 设置为使用 UTF-8,所以它发送数据为UTF-8,但是你的 MySQL 认为 PHP 发送的是 ISO 8859-1 编码的数据,所以它可能会再次尝试将你发送的数据编码为 UTF-8,造成这种麻烦。

    看看mysql_set_charset。它可能会帮助你。

    【讨论】:

      【解决方案12】:

      @harpax 对我有用。就我而言,这已经足够了:

      if (isUTF8($str)) { 
          echo $str; 
      }
      else
      {
          echo iconv("ISO-8859-1", "UTF-8//TRANSLIT", $str);
      }
      

      【讨论】:

        【解决方案13】:

        当您尝试处理日语和韩语等多种语言时,您可能会遇到麻烦。带有 'auto' 参数的 mb_convert_encoding 不能正常工作。设置 mb_detect_order('ASCII,UTF-8,JIS,EUC-JP,SJIS,EUC-KR,UHC') 没有帮助,因为它会错误地检测到 EUC-*。

        我的结论是,只要输入字符串来自 HTML,它就应该在元元素中使用 'charset'。我使用Simple HTML DOM Parser,因为它支持无效的 HTML。

        下面的 sn-p 从网页中提取标题元素。如果您想转换整个页面,那么您可能需要删除一些行。

        <?php
        require_once 'simple_html_dom.php';
        
        echo convert_title_to_utf8(file_get_contents($argv[1])), PHP_EOL;
        
        function convert_title_to_utf8($contents)
        {
            $dom = str_get_html($contents);
            $title = $dom->find('title', 0);
            if (empty($title)) {
                return null;
            }
            $title = $title->plaintext;
            $metas = $dom->find('meta');
            $charset = 'auto';
            foreach ($metas as $meta) {
                if (!empty($meta->charset)) { // html5
                    $charset = $meta->charset;
                } else if (preg_match('@charset=(.+)@', $meta->content, $match)) {
                    $charset = $match[1];
                }
            }
            if (!in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) {
                $charset = 'auto';
            }
            return mb_convert_encoding($title, 'UTF-8', $charset);
        }
        

        【讨论】:

          【解决方案14】:

          年龄以来,我一直在寻找编码解决方案,而这个页面可能是多年搜索的结论!我测试了你提到的一些建议,这是我的笔记:

          这是我的测试字符串:

          这是一个“wròng wrìtten”字符串但我需要 pù 'sòme' 特别 chàrs 看到 thèm,由 fùnctìon 转换!!就是这样!

          我执行 INSERT 以将此字符串保存在数据库中设置为 utf8_general_ci 的字段中

          我的页面的字符集是 UTF-8。

          如果我这样做插入,在我的数据库中,我有一些字符可能来自火星......

          所以我需要将它们转换成一些“正常”的 UTF-8。我试过utf8_encode(),但外星人字符仍在入侵我的数据库...

          所以我尝试使用 8 号发布的函数 forceUTF8,但在数据库中保存的字符串如下所示:

          这是一个“wròng wrìtten”字符串 bùt I nèed to pù 'sòme' special chà rs 看到 thèm,由 fùnctìon 转换!就是这样!

          因此,在此页面上收集更多信息并将它们与其他页面上的其他信息合并,我用这个解决方案解决了我的问题:

          $finallyIDidIt = mb_convert_encoding(
            $string,
            mysql_client_encoding($resourceID),
            mb_detect_encoding($string)
          );
          

          现在在我的数据库中,我的字符串编码正确。

          注意: 唯一需要注意的是函数mysql_client_encoding! 你需要连接到数据库,因为这个函数需要一个资源ID作为参数。

          不过,我只是在 INSERT 之前重新编码,所以对我来说这不是问题。

          【讨论】:

          • 你为什么不首先为 mysql 使用UTF-8 客户端编码?这种方式不需要手动转换
          【解决方案15】:

          您需要测试输入的字符集,因为响应可以使用不同的编码进行编码。

          我通过使用以下函数进行检测和翻译来强制将所有内容发送到 UTF-8:

          function fixRequestCharset()
          {
            $ref = array(&$_GET, &$_POST, &$_REQUEST);
            foreach ($ref as &$var)
            {
              foreach ($var as $key => $val)
              {
                $encoding = mb_detect_encoding($var[$key], mb_detect_order(), true);
                if (!$encoding)
                  continue;
                if (strcasecmp($encoding, 'UTF-8') != 0)
                {
                  $encoding = iconv($encoding, 'UTF-8', $var[$key]);
                  if ($encoding === false)
                    continue;
                  $var[$key] = $encoding;
                }
              }
            }
          }
          

          该例程会将来自远程主机的所有 PHP 变量转换为 UTF-8。

          如果无法检测或转换编码,则忽略该值。

          您可以根据需要对其进行自定义。

          在使用变量之前调用它。

          【讨论】:

          • 在没有传入编码列表的情况下使用 mb_detect_order() 的目的是什么?
          • 目的是返回系统配置的有序排列的编码数组,定义在 php.ini 中使用。这是 mb_detect_encoding 填充第三个参数所必需的。
          【解决方案16】:

          整理好你的 php 脚本后,别忘了告诉 mysql 你正在传递什么字符集并希望接收。

          示例:设置字符集 utf8

          在 latin1 I/O 会话中将 utf8 数据传递到 latin1 表会产生那些讨厌的鸟脚。我每隔一天就会在 oscommerce 商店看到这个。回来和第四似乎是正确的。但是 phpmyadmin 会显示真相。通过告诉 mysql 你传递什么字符集,它会为你处理 mysql 数据的转换。

          如何恢复现有的乱码mysql数据是另一个讨论的主题。 :)

          【讨论】:

            【解决方案17】:

            此版本适用于德语,但您可以修改 $CHARSETS 和 $TESTCHARS

            class CharsetDetector
            {
            private static $CHARSETS = array(
            "ISO_8859-1",
            "ISO_8859-15",
            "CP850"
            );
            private static $TESTCHARS = array(
            "€",
            "ä",
            "Ä",
            "ö",
            "Ö",
            "ü",
            "Ü",
            "ß"
            );
            public static function convert($string)
            {
                return self::__iconv($string, self::getCharset($string));
            }
            public static function getCharset($string)
            {
                $normalized = self::__normalize($string);
                if(!strlen($normalized))return "UTF-8";
                $best = "UTF-8";
                $charcountbest = 0;
                foreach (self::$CHARSETS as $charset) {
                    $str = self::__iconv($normalized, $charset);
                    $charcount = 0;
                    $stop   = mb_strlen( $str, "UTF-8");
            
                    for( $idx = 0; $idx < $stop; $idx++)
                    {
                        $char = mb_substr( $str, $idx, 1, "UTF-8");
                        foreach (self::$TESTCHARS as $testchar) {
            
                            if($char == $testchar)
                            {
            
                                $charcount++;
                                break;
                            }
                        }
                    }
                    if($charcount>$charcountbest)
                    {
                        $charcountbest=$charcount;
                        $best=$charset;
                    }
                    //echo $text."<br />";
                }
                return $best;
            }
            private static function __normalize($str)
            {
            
            $len = strlen($str);
            $ret = "";
            for($i = 0; $i < $len; $i++){
                $c = ord($str[$i]);
                if ($c > 128) {
                    if (($c > 247)) $ret .=$str[$i];
                    elseif ($c > 239) $bytes = 4;
                    elseif ($c > 223) $bytes = 3;
                    elseif ($c > 191) $bytes = 2;
                    else $ret .=$str[$i];
                    if (($i + $bytes) > $len) $ret .=$str[$i];
                    $ret2=$str[$i];
                    while ($bytes > 1) {
                        $i++;
                        $b = ord($str[$i]);
                        if ($b < 128 || $b > 191) {$ret .=$ret2; $ret2=""; $i+=$bytes-1;$bytes=1; break;}
                        else $ret2.=$str[$i];
                        $bytes--;
                    }
                }
            }
            return $ret; 
            }
            private static function __iconv($string, $charset)
            {
                return iconv ( $charset, "UTF-8" , $string );
            }
            }
            

            【讨论】:

              【解决方案18】:

              mb_detect_encodingmb_convert_encoding 的有趣之处在于您建议的编码顺序很重要:

              // $input is actually UTF-8
              
              mb_detect_encoding($input, "UTF-8", "ISO-8859-9, UTF-8");
              // ISO-8859-9 (WRONG!)
              
              mb_detect_encoding($input, "UTF-8", "UTF-8, ISO-8859-9");
              // UTF-8 (OK)
              

              因此,您可能希望在指定预期编码时使用特定顺序。不过,请记住,这并非万无一失。

              【讨论】:

              • 发生这种情况是因为 ISO-8859-9 实际上会接受任何二进制输入。 Windows-1252 和朋友也是如此。您必须首先测试可能无法接受输入的编码。
              • @MikkoRantalainen,是的,我猜这部分文档说的类似:php.net/manual/en/function.mb-detect-order.php#example-2985
              • 考虑到 WHATWG HTML 规范将 Windows 1252 定义为默认编码,假设if ($input_is_not_UTF8) $input_is_windows1252 = true; 应该是相当安全的。另见:html.spec.whatwg.org/multipage/…
              【解决方案19】:

              phpQueryISO-8859-1 而不是 UTF-8)我遇到了同样的问题,这个 hack 帮助了我:

              $html = '<?xml version="1.0" encoding="UTF-8" ?>' . $html;
              

              mb_internal_encoding('UTF-8')phpQuery::newDocumentHTML($html, 'utf-8')mbstring.internal_encoding等操作没有任何效果。

              【讨论】:

                【解决方案20】:

                从标头中获取编码并将其转换为 utf-8。

                $post_url='http://website.domain';
                
                /// Get headers ////////////////////////////////////////////////////////////
                function get_headers_curl($url) 
                { 
                    $ch = curl_init(); 
                
                    curl_setopt($ch, CURLOPT_URL,            $url); 
                    curl_setopt($ch, CURLOPT_HEADER,         true); 
                    curl_setopt($ch, CURLOPT_NOBODY,         true); 
                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
                    curl_setopt($ch, CURLOPT_TIMEOUT,        15); 
                
                    $r = curl_exec($ch); 
                    return $r; 
                }
                $the_header = get_headers_curl($post_url);
                /// check for redirect /////////////////////////////////////////////////
                if (preg_match("/Location:/i", $the_header)) {
                    $arr = explode('Location:', $the_header);
                    $location = $arr[1];
                
                    $location=explode(chr(10), $location);
                    $location = $location[0];
                
                $the_header = get_headers_curl(trim($location));
                }
                /// Get charset /////////////////////////////////////////////////////////////////////
                if (preg_match("/charset=/i", $the_header)) {
                    $arr = explode('charset=', $the_header);
                    $charset = $arr[1];
                
                    $charset=explode(chr(10), $charset);
                    $charset = $charset[0];
                    }
                ///////////////////////////////////////////////////////////////////////////////
                // echo $charset;
                
                if($charset && $charset!='UTF-8') { $html = iconv($charset, "UTF-8", $html); }
                

                【讨论】:

                  【解决方案21】:

                  ÂŸß 的 Mojibake。在您的数据库中,您可能有十六进制

                  DF if the column is "latin1",
                  C39F if the column is utf8 -- OR -- it is latin1, but "double-encoded"
                  C383C5B8 if double-encoded into a utf8 column
                  

                  您不应该在 PHP 中使用任何编码/解码函数;相反,您应该正确设置数据库及其连接。

                  如果涉及 MySQL,请参阅:Trouble with utf8 characters; what I see is not what I stored

                  【讨论】:

                    【解决方案22】:

                    我在这里找到解决方案http://deer.org.ua/2009/10/06/1/

                    class Encoding
                    {
                        /**
                         * http://deer.org.ua/2009/10/06/1/
                         * @param $string
                         * @return null
                         */
                        public static function detect_encoding($string)
                        {
                            static $list = ['utf-8', 'windows-1251'];
                    
                            foreach ($list as $item) {
                                try {
                                    $sample = iconv($item, $item, $string);
                                } catch (\Exception $e) {
                                    continue;
                                }
                                if (md5($sample) == md5($string)) {
                                    return $item;
                                }
                            }
                            return null;
                        }
                    }
                    
                    $content = file_get_contents($file['tmp_name']);
                    $encoding = Encoding::detect_encoding($content);
                    if ($encoding != 'utf-8') {
                        $result = iconv($encoding, 'utf-8', $content);
                    } else {
                        $result = $content;
                    }
                    

                    我认为 @ 是错误的决定,并从 deer.org.ua 对解决方案进行一些更改;

                    【讨论】:

                      【解决方案23】:

                      投票最多的答案无效。这是我的,希望对您有所帮助。

                      function toUTF8($raw) {
                          try{
                              return mb_convert_encoding($raw, "UTF-8", "auto"); 
                          }catch(\Exception $e){
                              return mb_convert_encoding($raw, "UTF-8", "GBK"); 
                          }
                      }
                      

                      【讨论】:

                      • 您是否知道您的文件为何或有何不同?哪些部分不适合您?例如:大写德语字符未正确转换。好奇,什么是“GBK”?
                      【解决方案24】:

                      尝试不使用“自动”

                      即:

                      mb_detect_encoding($text)
                      

                      代替:

                      mb_detect_encoding($text, 'auto')
                      

                      更多信息可以在这里找到:mb_detect_encoding

                      【讨论】:

                        【解决方案25】:

                        尝试使用这个...所有不是 UTF-8 的文本都会被翻译。

                        function is_utf8($str) {
                            return (bool) preg_match('//u', $str);
                        }
                        
                        $myString = "Fußball";
                        
                        if(!is_utf8($myString)){
                            $myString = utf8_encode($myString);
                        }
                        
                        // or 1 line version ;) 
                        $myString = !is_utf8($myString) ? utf8_encode($myString) : trim($myString);
                        

                        【讨论】:

                          猜你喜欢
                          • 2020-09-15
                          • 2013-10-30
                          • 1970-01-01
                          • 1970-01-01
                          • 2010-10-26
                          • 2012-04-26
                          • 1970-01-01
                          • 2017-04-20
                          相关资源
                          最近更新 更多