web-dev-qa-db-ja.com

UTF8エンコードの問題-良い例

次の文字エンコードの問題があり、何らかの方法で異なる文字エンコードのデータをデータベース(UTF8)に保存できました。以下のコードと出力は、2つのサンプル文字列とその出力方法を示しています。それらの1つはUTF8に変更する必要があり、もう1つは既に変更されています。

文字列をエンコードする必要があるかどうかを確認するにはどうすればよいですか?たとえば、各文字列を正しく出力する必要があるので、すでにutf8であるか、変換する必要があるかどうかを確認するにはどうすればよいですか?

PHP 5.2、mysql myisam tables:

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

これについてお時間をいただきありがとうございます。文字エンコーディングと私はあまりうまくいきません!

PDATE:

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
23
Lizard

これは mb_detect_encoding() 関数のジョブかもしれません。

私の限られた経験では、一般的な「エンコーディングスニファー」として使用する場合、100%信頼性がありません-特定の文字とバイト値の存在をチェックして、経験に基づいた推測を行います-しかし、この狭いケースでは( UTF-8とISO-8859-1を区別する))should.

<?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 />";

?>

特殊文字を含まない文字列に対して誤った結果が得られる場合がありますが、それは問題ではありません。

27
Pekka 웃

このすべての問題に対処する関数を作成しました。 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
9

別の方法、より高速で信頼性の低いもの:

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を返す必要があります

6
Dr.Molle

UTF-8およびISO-8859-1の検出/変換でうまく機能するこれらの小さな2つの関数を作成しました...

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');
1
AlexV

ここではサンプルを試しませんでしたが、過去の経験から、これに対する簡単な修正があります。データベース接続の直後に、他のクエリを実行する前に次のクエリを実行します。

SET NAMES UTF8;

これはSQL標準に準拠しており、FirebirdやPostgreSQLなどの他のデータベースで適切に機能します。

ただし、アプリケーションを正常に動作させるには、他の場所でもUTF-8宣言を確実に行う必要があることを忘れないでください。簡単なチェックリストに従ってください。

  • すべてのファイルはUTF-8として保存する必要があります(BOM [Byte Order Mask]なしで推奨)
  • HTTPサーバーはエンコードヘッダーUTF-8を送信する必要があります。 FirebugまたはLive HTTPヘッダーを使用して検査します。
  • サーバーが応答を圧縮および/またはトークン化すると、ヘッダーコンテンツがチャンクまたはgzip圧縮されたものとして表示される場合があります。ファイルをUTF-8として保存し、
  • 適切なメタタグを使用して、HTMLヘッダーへのエンコードを宣言します。
  • すべてのアプリケーション(ソケット、ファイルシステム、データベースなど)で、可能な限りUTF-8にフラグを立てることを忘れないでください。データベース接続などを開くときにこれを行うと、常にエンコード/デコード/デバッグする必要がなくなります。ルートでグラブエム。
0
Dave