检测编码并制作一切UTF-8


304

我正在从各种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;
}

我已经测试过了,但是没有用。它出什么问题了?


36
在我的数据库中,“Fußball”中的“ß”应如下所示:“Ÿ”。不,它应该看起来像ß。确保排序规则和连接设置正确。否则,排序和搜索将为您带来麻烦。
Rich Bradshaw

5
您的数据库设置错误。如果要存储Unicode内容,只需对其进行配置。因此,而不是尝试解决PHP代码中的问题,您应该首先修复数据库。
dolmen 2014年

2
使用:$ from = mb_detect_encoding($ text); $ text = mb_convert_encoding($ text,'UTF-8',$ from);
Informate.it

Answers:


363

如果应用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()


1
好吧,如果您看一下代码,fixUTF8只会一次又一次调用forceUTF8,直到字符串返回不变。一次调用fixUTF8()至少要花费两次调用forceUTF8()的时间,因此其性能要差很多。我制作了fixUTF8()只是为了创建一个命令行程序,该程序可以修复“已编码损坏”的文件,但是在实时环境中几乎不需要。
塞巴斯蒂安·格里戈诺利

3
如何在不知道无效字符以什么编码开始的情况下将非UTF8字符转换为UTF8?
philfreo

4
它假定为ISO-8859-1,答案已经说明了这一点。forceUTF8()和utf8_encode()之间的唯一区别是forceUTF8()可以识别UTF8字符并使其保持不变。
塞巴斯蒂安·格里戈诺利

28
“您不需要知道字符串的编码是什么。” -我非常不同意。猜测和尝试可能会奏效,但您总是迟早会遇到不可行的情况。
deceze

4
我完全同意。实际上,我并不是要说一般地说,只是解释一下,如果您碰巧遇到这种情况,该课程对您有帮助。
SebastiánGrignoli 2013年

74

首先,您必须检测使用了哪种编码。在解析RSS feed(可能通过HTTP)时,您应该从HTTP标头字段charset参数中读取编码。如果不存在,请从XML处理指令的属性中读取编码。如果也缺少该属性,请使用规范中定义的UTF-8Content-Typeencoding


编辑    这是我可能会做的:

我会使用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属性中通常给出错误的编码。
caw

25
再说一遍:那不是你的问题。建立了避免此类麻烦的标准。如果其他人不关注他们,那是他们的问题,而不是您的问题。
浓汤

好的,我想您现在终于说服了我。:)
caw

感谢您的代码。但是为什么不简单地使用它呢?paste.bradleygill.com/index.php?paste_id=9651您的代码复杂得多,有什么更好的选择?
caw

好吧,首先,您要发出两个请求,一个请求用于HTTP标头,一个请求用于数据。其次,您正在寻找的任何外观,charset=encoding=不仅仅是在适当的位置。第三,您不检查声明的编码是否被接受。
Gumbo

39

检测编码很困难。

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

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

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


非常感谢你!哪个更好:mb-convert-encoding()或iconv()?我不知道有什么区别。是的,我只需要解析西欧语言,尤其是英语,德语和法语。
caw

7
我刚刚看到: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()。
caw

1
我的,你是对的。自从我使用它已经有一段时间了。然后,您必须编写自己的检测代码,或使用外部实用程序。可以相当可靠地确定UTF-8,因为它的转义序列非常有特征。可以区分wp-1252和iso-8859-1,因为wp-1252可能包含iso-8859-1中非法的字节。使用Wikipedia获取详细信息,或在各种与字符集相关的功能下查看php.net的comments部分。
troelskn

我认为,当您查看特殊唱法出现的形式时,您可以区分不同的编码:德语的“ß”以不同的形式出现:有时是“Ÿ”,有时是“ß”,有时是“ß”。为什么?
caw

是的,但是在比较之前,您需要知道字符串的内容,这样一来就破坏了目的。德语ß的显示方式有所不同,因为它在不同的编码中具有不同的值。Somce字符碰巧以不同的编码方式表示(例如,ascii字符集中的所有字符在utf-8,iso-8859- *和wp-1252中都以相同的方式编码),只要您使用只是那些字符,它们看起来都一样。这就是为什么它们有时被称为ascii兼容的原因。
troelskn

14

可以在php.net上找到实现-function的一种非常好的方法:isUTF8

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

16
不幸的是,这仅在字符串仅由ISO-8859-1中包含的字符组成时才起作用。但这可能有效:@iconv('utf-8','utf-8 // IGNORE',$ str)== $ str
ChristianDavén11年

@克里斯蒂安:的确,这也是高性能MySQL的作者所推荐的。
Alix Axel

1
它不能正常工作:echo(int)isUTF8('z'); #1 echo(int)isUTF8(NULL); #1
Yousha Aleayoub'8

1
尽管并不完美,但我认为这是实施粗略的UTF-8检查的好方法。
Mateng

1
mb_check_encoding($string, 'UTF-8')
deceze

13

该备忘单列出了一些与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);
}


2
我认为这不能正常工作:echo detectUTF8('3٣3'); #1
Yousha Aleayoub'8

10

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

这可能是因为您使用的是采用Latin-1字符编码的数据库,或者您的PHP-MySQL连接设置有误,这就是说,P认为您的MySQL设置为使用UTF-8,因此它以UTF-8的形式发送数据,但是您的MySQL认为PHP正在发送编码为ISO 8859-1的数据,因此它可能再次尝试将发送的数据编码为UTF-8,从而引起这种麻烦。

看一下mysql_set_charset。它可能会帮助您。


4

您的编码看起来就像您两次编码到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)。


3

关于mb_detect_encoding和的有趣之处mb_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)

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


2
发生这种情况是因为ISO-8859-9实际上将接受任何二进制输入。Windows-1252和朋友也是如此。您必须首先测试可能无法接受输入的编码。
米科·兰塔莱宁

@MikkoRantalainen,是啊,我猜文档的这一部分说类似的话:php.net/manual/en/function.mb-detect-order.php#example-2985
哈利勒Özgür的

考虑到WHATWG HTML规范将Windows 1252定义为默认编码,因此假设它应该很安全if ($input_is_not_UTF8) $input_is_windows1252 = true;。参见:html.spec.whatwg.org/multipage/...
米克Rantalainen

3

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

我通过使用以下功能进行检测和翻译来强制将所有内容发送到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()的目的是什么?
giorgio79 2014年

目的是返回使用的php.ini中定义的系统配置的有序编码数组。mb_detect_encoding要求此参数填充第三个参数。
cavila 2015年

2

计算RSS提要的字符编码似乎很复杂。即使是普通的网页也经常会省略或撒谎。

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


我不想从提要信息中读取编码。因此,如果提要信息错误,则等同。我想从文本中检测编码。
Caw

@ marco92w:如果声明的编码错误,这不是您的问题。娱乐性标准尚未建立。
浓汤

1
@Gumbo:但是,如果您在现实世界中工作,则必须能够处理不正确的已声明编码等问题。问题在于仅凭某些文本很难(正确地)猜测编码。标准很棒,但是那里的许多(大多数)页面/供稿不符合它们。
凯文·奥鲁克

@Kevin ORourke:好的,对。那是我的问题。@Gumbo:是的,这是我的问题。我想读出提要并汇总它们。因此,我必须纠正错误的编码。
caw

@ marco92w:但是,如果您不知道正确的编码和当前的编码,则无法校正编码。这就是charset/ encoding声明的含义:描述数据的编码格式
。– Gumbo,

2

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

这是我的解决方案。在处理之前,它会在每次加载页面时擦洗您的GET / POST / REQUEST(我省略了cookie,但可以根据需要添加它们)。它在标头中运行良好。如果PHP无法自动检测源编码,则会抛出警告,因此这些警告将被@禁止显示。

//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()是我们在这里已经拥有的,对吧?;)因此,您的答案中唯一的新事物是更改所有变量的编码的循环。
caw 2010年

2

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

这是我的测试字符串:

这是一个“wròngwrìtten”字符串,我需要转到'sòme'特殊的频道来查看,由fùnctìon转换!& 而已!

我执行INSERT将该字符串保存在数据库中设置为 utf8_general_ci

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

如果我那样做一次INSERT,在我的数据库中,我可能有一些来自火星的角色...

因此,我需要将它们转换为一些“健全的” UTF-8。我尝试过utf8_encode(),但外星人的chars仍在入侵我的数据库...

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

这是一个“wrÃngngwrÃtten”字符串,我要去给p''sme'特殊的孩子们看,然后由fÃnctìon转换!& 而已!

因此,在此页面上收集了更多信息,并将其与其他页面上的其他信息合并在一起,由此解决了我的问题:

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

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

注意: 只有注意事项才起作用mysql_client_encoding!您需要连接到数据库,因为此函数需要一个资源ID作为参数。

但是好吧,我只是在INSERT之前进行了重新编码,所以对我来说这不是问题。


1
为什么不首先只UTF-8为mysql 使用客户端编码?不需要这种方式的手动转换
Esailija

2

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

因此,当您获取某个ISO 8859-1的供稿时,将其解析为utf8_encode

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


谢谢!好的,我可以使用mb-detect-encoding()找出提要是如何编码的,对吗?但是,如果提要是ASCII怎么办?utf8-encode()仅适用于ISO-8859-1至UTF-8,不是吗?
caw

ASCII是ISO-8859-1和UTF-8的子集,因此使用utf8-encode()不应进行更改-如果它实际上只是ASCII
Michael Borgwardt,2009年

因此,如果不是UTF-8,我总是可以使用utf8_encode吗?这真的很容易。根据mb-detect-encoding()为ASCII的文本包含“&#228;”。这是ASCII字符吗?还是HTML?
caw

那是HTML。实际上,它是经过编码的,因此当您在给定页面中打印它时,它会显示确定。如果需要,您可以先ut8_encode(),然后html_entity_decode()。
勒布

1
字符ß以UTF-8编码,字节序列为0xC39F。用Windows-1252解释,该序列表示两个字符Â(0xC3)和Ÿ(0x9F)。并且,如果再次使用UTF-8对该字节序列进行编码,则会得到0xC383 0xC29F,它表示Windows-1252中的ƒ。因此,您的错误是将UTF-8编码的数据作为UTF-8以外的编码来处理。该字节序列显示为您所看到的字符只是解释的问题。如果您使用其他编码/字符集,则可能会看到其他字符。
浓汤

1

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可行。

update
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'有什么区别?“自动”功能是否具有更多编码?那么使用“自动”会更好,不是吗?如果确实没有任何错误,那么我只能将“ ASCII”或“ ISO-8859-1”更改为“ UTF-8”。怎么样?
caw

2
您的功能在所有情况下都无法正常运行。有时我会出错:注意:iconv():在...中的输入字符串中检测到非法字符

1

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

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

0

整理完php脚本后,不要忘记告诉mysql您正在传递什么字符集并希望接收哪些字符集。

示例:设置字符集utf8

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

如何恢复现有的已加扰的mysql数据是另一个需要讨论的话题。:)


0

该版本适用于德语,但是您可以修改$ 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 );
}
}


0

从标题获取编码并将其转换为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); }

0

Ÿ是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,请参阅:utf8字符有问题;我看到的不是我存储的


0

我在这里找到解决方法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的解决方案进行一些更改;


0

投票最多的答案不起作用。这是我的,希望对您有所帮助。

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

1
您是否了解原因,或文件有何不同?哪些零件对您不起作用?例如:大写德语字符转换不正确。很好奇,什么是“ GBK”?
SherylHohman

-1

当您尝试使用日文和韩文等多种语言时,您可能会遇到麻烦。带有“ auto”参数的mb_convert_encoding效果不佳。设置mb_detect_order('ASCII,UTF-8,JIS,EUC-JP,SJIS,EUC-KR,UHC')无济于事,因为它将错误地检测EUC- *。

我得出的结论是,只要输入字符串来自HTML,它就应该在meta元素中使用“字符集”。我使用简单HTML DOM解析器,因为它支持无效的HTML。

下面的代码段从网页中提取标题元素。如果要转换整个页面,则可能要删除一些行。

<?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);
}

-1

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

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

mb_internal_encoding('UTF-8')phpQuery::newDocumentHTML($html, 'utf-8')mbstring.internal_encoding和其他操作没有采取任何影响。


-1

尝试不使用“自动”

那是:

mb_detect_encoding($text)

代替:

mb_detect_encoding($text, 'auto')

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.