如何通过php检测和修复mysql数据库中的字符编码?

时间:2009-10-01 09:59:05

标签: php mysql character-encoding special-characters

我收到的这个数据库中充满了法语的人名和数据,这意味着使用é,è,ö,û等字符。大约3000个条目。

显然,内部数据有时使用utf8_encode()编码,有时则不编码。这导致混乱的输出:在某些地方,角色显示正常,而在其他地方,他们没有。

起初我试图在UI中找到出现这些问题的每个地方,并在必要时使用utf8_decode(),但这实际上不是一个切实可行的解决方案。

我做了一些测试,没有理由首先使用utf8_encode,所以我宁愿删除所有这些,只是在UTF8中工作 - 在浏览器,中间件和数据库级别。所以我需要清理数据库,通过清理后的版本转换所有错误编码的数据。

问题:是否有可能在php中创建一个函数来检查utf8字符串是否正确编码(没有utf8_encode)或者没有(使用utf8_encode),如果是,则将其转换回原始状态?

换句话说:我想知道如何检测utf8内容已经utf8_encode()到utf8内容,而不是utf8_encode()d。

**更新:示例**

这是一个很好的例子:你带一个充满特殊字符的字符串并获取该字符串的副本和utf8_encode()它。我梦寐以求的函数接受两个字符串,第一个字符串保持不变,第二个字符串现在与字符串一相同。

我试过了:

$loc_fr = setlocale(LC_ALL, 'fr_BE.UTF8','fr_BE@euro', 'fr_BE', 'fr', 'fra', 'fr_FR');
$str1= "éèöûêïà ";
$str2 = utf8_encode($str1);

function convert_charset($str) {
    $charset=  mb_detect_encoding($str);
    if( $charset=="UTF-8" ) {
        return utf8_decode($str);
    }
    else {
        return $str;
    }
}
function correctString($str) {
    echo "\nbefore: $str";
    $str= convert_charset($str);
    echo "\nafter: $str"; 
}

correctString($str1);
echo('<hr/>'."\n");
correctString($str2);

这让我:

before: éèöûêïà after: ������� 
before: éèöûêïà  after: éèöûêïà 

谢谢,

亚历

5 个答案:

答案 0 :(得分:6)

从问题中你正在查看的字符编码镜头(这取决于文本编辑器的默认值,浏览器标题,数据库配置等)以及数据具有哪些字符编码转换,这一点并不完全清楚。经历了。例如,通过调整数据库配置,可能会纠正所有内容,这比对数据进行零碎的更改要好得多。

看起来这可能是utf8双重编码的问题,如果是这种情况,原始和损坏的数据都将在utf8中,因此编码检测不会为您提供所需的信息。在这种情况下的方法需要假设你的数据中哪些字符可以合理地出现:就PHP和Mysql而言,“é”是完全合法的utf8,所以你必须根据你对你的了解做出判断。数据及其作者必须被破坏。如果您只是技术人员,这些都是有风险的假设。幸运的是,如果您知道数据是法语的,并且只有3000条记录,那么可以做出这些假设。

下面是一个脚本,您可以首先调整以检查数据,然后进行更正,最后再次检查。它所做的只是将字符串处理为utf8,将其分解为字符,并将字符与预期的法语字符的白名单进行比较。如果字符串不在utf8中或包含法语中通常不期望的字符,则表示存在问题,例如:

PROBABLY OK     Côte d'Azur
HAS NON-WHITELISTED CHAR        Côte d'Azur    195,180 ô
NON-UTF8        C�e d'Azur

这是脚本,您需要从http://hsivonen.iki.fi/php-utf8/下载相关的unicode函数

<?php

// Download from http://hsivonen.iki.fi/php-utf8/
require "php-utf8/utf8.inc";

$my_french_whitelist = array_merge(
  range(0,127), // throw in all the lower ASCII chars
  array(
    0xE8, // small e-grave
    0xE9, // small e-acute
    0xF4, // small o-circumflex
    //... Will need to add other accented chars,
    // Euro sign, and whatever other chars
    // are normally expected in the data.
  )
);

// NB, whether this string literal is in utf8
// depends on the encoding of the text editor
// used to write the code
$str1 = "Côte d'Azur";
$test_data = array(
  $str1,
  utf8_encode($str1),
  utf8_decode($str1),
);

foreach($test_data as $str){
  $questionable_chars = non_whitelisted(
    $my_french_whitelist,
    $str
  );
  if($questionable_chars===true){
    p("NON-UTF8", $str);
  }else if ($questionable_chars){
    p(
      "HAS NON-WHITELISTED CHAR",
      $str,
      implode(",", $questionable_chars),
      unicodeToUtf8($questionable_chars)
    );
  }else{
    p("PROBABLY OK", $str);
  }
}

function non_whitelisted($whitelist, $utf8_str){
  $codepoints = utf8ToUnicode($utf8_str);
  if($codepoints===false){ // has non-utf8 char
    return true;
  }
  return array_diff(
    array_unique($codepoints),
    $whitelist
  );
}


function p(){
  $args = func_get_args();
  echo implode("\t", $args), "\n";
}

答案 1 :(得分:2)

我认为你可能会采取更多的编译方法。几个星期前我收到了一个在DB中动态编码的保加利亚数据库,但是当它移动到另一个数据库时,我得到了时髦的颜色???

我解决这个问题的方法是转储数据库,将数据库设置为utf8排序规则,然后将数据导入为二进制文件。这自动将所有内容转换为utf8并且不再给我了。

这是在MySQL

答案 2 :(得分:2)

连接数据库时,请记住始终使用mysql_set_charset('utf8',$ db_connection);

它将解决所有问题,它解决了我所有的问题。

请参阅:http://phpanswer.com/store-french-characters-into-mysql-db-and-display/

答案 3 :(得分:0)

正如您所说,您的数据有时会使用utf8_encode转换,您的数据会使用UTF-8或ISO 8859-1进行编码(因为utf8_encode会将ISO 8859-1转换为UTF-8 )。由于UTF-8将字符从128到255编码,其中两个字节以1100001x开头,因此您只需测试您的数据是否为有效的UTF-8,如果没有则进行转换。

如果数据已经是UTF-8(请参阅多个is_utf8函数),请扫描所有数据;如果数据不是UTF-8,请使用utf8_encode

答案 4 :(得分:0)

我的问题是,不知何故,我在我的数据库中加入了像普通格式的à,é,ê或utf8编码的字符串。经过调查,我得出结论,一些浏览器(我不知道IE或FF或其他)正在编码提交的输入数据,因为没有故意添加utf8编码来处理提交表单。所以,如果我用utf8_encode读取数据,我将改变其他普通字符,反之亦然。

我的解决方案,在我研究上面给出的解决方案后: 1.我使用charset utf8创建了一个新数据库 2.导入数据库后我将sql dump文件中CREATE TABLE语句中的charset定义从Latin ....更改为UTF8。 3.从原始数据库导入数据 (直到这里可能只是为了更改现有数据库和表上的字符集,这只有在原始数据库不是utf8时) 4.使用简单格式替换utf8编码的字符,直接更新数据库中的内容,如

UPDATE `clients` SET `name` = REPLACE(`name`,"é",'é' )  WHERE `name` LIKE CONVERT( _latin1 '%é%' USING utf8 ); 
  1. 我把这个行放入db类(用于php代码)以确保它们是UTF8通信

    $ this-&gt; query('SET CHARSET UTF8');

  2. 那么,请更新? (第4步) 我已经构建了一个带有可能编码的字符的数组

    $special_chars = array(
      'ù','û','ü',
      'ÿ',
      'à','â','ä','å','æ',
      'ç',
      'é','è','ê','ë',
      'ï','î',
      'ô','','ö','ó','ø',
      'ü');
    

    我用一对表格来建立一个数组,应该更新的字段

    $where_to_look = array(
        array("table_name" , "field_name"),
            ..... );
    

    比,

        foreach($special_chars as $char)
        {
          foreach($where_to_look as $pair)
          {
            //$table = $pair[0]; $field = $pair[1]
            $sql = "SELECT id , `" . $pair[1] . "` FROM " .$pair[0] . " WHERE `" . $pair[1] . "` LIKE CONVERT( _latin1 '%" . $char . "%' USING utf8 );";
    
        if($db->num_rows() > 0){
             $sql1 = "UPDATE " . $pair[0] . " SET `" . $pair[1] . "` = REPLACE(`" . $pair[1] . "`,CONVERT( _latin1 '" . $char . "' USING utf8 ),'" . $char . "' )  WHERE `" . $pair[1] . "` LIKE CONVERT( _latin1 '%" . $char . "%' USING utf8 )";
             $db1->query($sql1);
            }
        }
     }
    

    基本的ideea是使用mysql的编码功能来避免在mysql,apache,browser和back之间进行编码; 注意:我没有可用的php函数,如mb _....

    最佳