エンコーディングを検出してすべてをUTF-8にする


304

さまざまなRSSフィードから多くのテキストを読み取ってデータベースに挿入しています。

もちろん、フィードではいくつかの異なる文字エンコードが使用されています(UTF-8やISO 8859-1など)。

残念ながら、テキストのエンコーディングに問題がある場合があります。例:

  1. 「サッカー」の「ß」は、私のデータベースでは「Ÿ」のようになります。「Ÿ」の場合、正しく表示されます。

  2. 「サッカー」の「ß」が私のデータベースでは「likeƒÂŸ」のようになることがあります。もちろん、誤って表示されます。

  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
「サッカー」の「ß」は、私のデータベースでは「Ÿ」のようになります。いいえ、ßのように見えるはずです。照合と接続が正しく設定されていることを確認してください。そうしないと、並べ替えや検索が機能しなくなります。
リッチブラッドショー

5
データベースが正しく設定されていません。Unicodeコンテンツを保存する場合は、そのために構成します。したがって、PHPコードで問題を回避しようとする代わりに、まずデータベースを修正する必要があります。
ドルメン2014年

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

回答:


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()を1回呼び出すと、forceUTF8()を呼び出す時間の少なくとも2倍の時間がかかるため、パフォーマンスが大幅に低下します。「エンコードが破損した」ファイルを修正するコマンドラインプログラムを作成するためだけにfixUTF8()を作成しましたが、ライブ環境ではほとんど必要ありません。
セバスティアングリニョーリ2010

3
無効な文字のエンコーディングを知らなくても、UTF8以外の文字をUTF8に変換するにはどうすればよいですか?
philfreo 2010

4
それはISO-8859-1を前提としています、答えはすでにこれを言っています。forceUTF8()とutf8_encode()の唯一の違いは、forceUTF8()がUTF8文字を認識し、変更しないことです。
セバスティアンGrignoli

28
「文字列のエンコーディングが何であるかを知る必要はありません。」-私は非常に同意しません。推測して試してもうまくいくかもしれませんが、いつかはいつでもうまくいかないエッジケースに遭遇します。
だます

4
全くもって同じ意見です。実際、私は、原則として、このクラスがあなたがたまたま見つけた状況である場合に役立つかもしれないことを説明するだけであると説明するつもりはありませんでした。
SebastiánGrignoli 2013年

74

最初に、使用されているエンコーディングを検出する必要があります。RSSフィードを解析しているときに(おそらくHTTP経由で)、HTTPヘッダーフィールドのcharsetパラメーターからエンコードを読み取る必要がありContent-Typeます。存在しない場合encodingは、XML処理命令の属性からエンコーディングを読み取ります。それもない場合は、仕様で定義されているUTF-8を使用してください


編集    これはおそらく私がすることです:

cURLを使用して、応答を送信およびフェッチします。これにより、特定のヘッダーフィールドを設定し、応答ヘッダーも取得できます。応答をフェッチした後、HTTP応答を解析し、ヘッダーと本文に分割する必要があります。ヘッダーにContent-Typeは、MIMEタイプと(うまくいけば)charsetエンコーディング/文字セットを含むパラメーターを含むヘッダーフィールドを含める必要があります。そうでない場合は、encoding属性の存在についてXML PIを分析し、そこからエンコーディングを取得します。それも欠落している場合、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の属性に誤ったエンコードが指定されています。
2009年

25
繰り返しますが、それはあなたの問題ではありません。そのようなトラブルを避けるための基準が制定されました。他の人がそれに従わない場合、それはあなたの問題ではなく、あなたの問題です。
ガンボ

わかりました、あなたは今やっと私を納得させたと思います。:)
2009年

コードをありがとう。しかし、なぜこれを単に使用しないのですか?paste.bradleygill.com/index.php?paste_id=9651コードははるかに複雑ですが、コードの何が優れていますか?
2009年

さて、まず、HTTPヘッダー用とデータ用の2つのリクエストを作成します。第二に、あなたは、任意の外観を探しているcharset=と、encoding=ちょうど適切な位置ではありません。そして3番目に、宣言されたエンコーディングが受け入れられるかどうかをチェックしていません。
ガンボ

39

エンコーディングの検出は困難です。

mb_detect_encodingあなたがそれを渡す多くの候補者に基づいて、推測によって動作します。一部のエンコーディングでは、特定のバイトシーケンスが無効であるため、さまざまな候補を区別できます。残念ながら、同じバイトが有効である(ただし異なる)エンコーディングはたくさんあります。これらの場合、エンコーディングを決定する方法はありません。独自のロジックを実装して、これらのケースで推測を行うことができます。たとえば、日本語のサイトからのデータには、日本語のエンコーディングが含まれている可能性が高くなります。

限り、あなたは唯一の西ヨーロッパ言語に対処するよう、考慮すべき3つの主要なエンコーディングがあるutf-8iso-8859-1cp-1252。これらは多くのプラットフォームのデフォルトであるため、誤って報告される可能性が最も高くなります。例えば。人々が異なるエンコーディングを使用していると、ソフトウェアが非常に頻繁に壊れてしまうため、率直に表現する可能性があります。したがって、エンコードがこれら3つのうちの1つとして報告されない限り、プロバイダーを信頼することをお勧めします。あなたはまだそれが本当に有効であることを再確認する必要がありますmb_check_encoding有効こと同じではないことに注意して -同じ入力は、多くのエンコーディングのために有効です)。それらのいずれかであれば、次に使用できますmb_detect_encodingそれらを区別するために。幸い、これはかなり確定的です。適切な検出シーケンスであるを使用する必要があるだけですUTF-8,ISO-8859-1,WINDOWS-1252

エンコーディングを検出したら、それを内部表現に変換する必要があります(UTF-8唯一の正しい選択です)。関数utf8_encode変換ISO-8859-1にはUTF-8、それだけ、その特定の入力タイプに使用することができます。他のエンコーディングについては、を使用してくださいmb_convert_encoding


どうもありがとうございました!何が良いですか:mb-convert-encoding()またはiconv()?何が違うのかわかりません。はい、西ヨーロッパの言語、特に英語、ドイツ語、フランス語のみを解析する必要があります。
2009年

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()は使用できません。
2009年

1
私、あなたは正しいです。使って久しぶりです。次に、独自の検出コードを記述するか、外部ユーティリティを使用する必要があります。UTF-8は、エスケープシーケンスが非常に特徴的であるため、かなり確実に決定できます。wp-1252にはiso-8859-1では無効なバイトが含まれている可能性があるため、wp-1252とiso-8859-1を区別できます。Wikipediaを使用して詳細を取得するか、さまざまな文字セット関連の関数の下にあるphp.netのコメントセクションを参照してください。
troelskn 2009年

特別な歌が現れる形を見ると、さまざまなエンコーディングを区別できると思います。ドイツ語の "ß"は、さまざまな形で現れます。時々 "Ÿ"、時々 "ß"、時々 "ß"があります。どうして?
2009年

はい、しかしそれからあなたはそれを比較する前に文字列の内容を知る必要があります、そしてそのようなことはそもそも目的を無効にします。ドイツ語のßは、エンコーディングによって値が異なるため、表示が異なります。Somce文字は、たまたま使用する限り、異なるエンコーディングで同じように表現されます(たとえば、ASCII文字セットのすべての文字は、utf-8、iso-8859- *、およびwp-1252でも同じようにエンコードされます)。それらの文字だけで、それらはすべて同じに見えます。そのため、ASCII互換と呼ばれることもあります。
troelskn 2009年

14

- 関数を実装する本当に良い方法isUTF8php.netにあります:

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

16
残念ながら、これは、文字列がISO-8859-1に含まれている文字のみで構成されている場合にのみ機能します。しかし、これは機能する可能性があります:@iconv( 'utf-8'、 'utf-8 // IGNORE'、$ str)== $ str
ChristianDavénAug

@Christian:確かに、それはHigh Performance MySQLの作者も推奨していることです。
Alix Axel

1
正しく機能しません:echo(int)isUTF8( 'z'); #1エコー(int)isUTF8(NULL); #1
Yousha Aleayoub

1
完璧ではありませんが、これは大まかなUTF-8チェックを実装する良い方法だと思います。
Mateng 2013

1
mb_check_encoding($string, 'UTF-8')
だます

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

10

少しヘッドアップ。"ß"はデータベースで "Ÿ"として表示されるべきだと言っていました。

これはおそらく、Latin-1文字エンコーディングのデータベースを使用しているか、PHP-MySQL接続が正しく設定されていない可能性があります。これは、MySQLがUTF-8を使用するように設定されているため、データがUTF-8として送信されるためですただし、MySQLはPHPがISO 8859-1としてエンコードされたデータを送信していると信じているため、送信されたデータをもう一度UTF-8としてエンコードしようとする可能性があり、この種の問題を引き起こします。

mysql_set_charsetを見てください。それはあなたを助けるかもしれません。


4

エンコーディングは、UTF-8に2回エンコードしたように見えます。つまり、他のエンコーディングから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)に変換します。元のソースエンコーディングは重要ではありません。欠陥のある2番目の変換で使用したものです。

これは何が起こったのか私の推測です。1つの拡張ASCIIバイトの代わりに4バイトを取得するために他にできることはほとんどありません。

ドイツ語もISO 8859-2およびWindows-1250(Latin-2)を使用しています。


3

興味深い事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)

そのため、予期されるエンコーディングを指定するときに特定の順序を使用したい場合があります。それでも、これは絶対に確実なことではないことに注意してください。


2
これは、ISO-8859-9が実際には任意のバイナリ入力を受け入れるために発生します。Windows-1252やその他の友人にも同じことが言えます。まず、入力の受け入れに失敗する可能性のあるエンコーディングをテストする必要があります。
Mikko Rantalainen

@MikkoRantalainen、そうです、ドキュメントのこの部分は同様のことを言っていると思い
HalilÖzgürDec

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が3番目のパラメーターを満たすために必要です。
cavila 2015年

2

RSSフィードの文字エンコーディングの計算が複雑に思われるです。通常のWebページでさえ、多くの場合、それらのエンコードが省略されています。

したがって、正しい方法を使用してエンコーディングを検出し、何らかの形の自動検出(推測)にフォールバックすることを試みることができます。


フィード情報からエンコーディングを読みたくありません。したがって、フィード情報が間違っていても同じです。テキストからエンコーディングを検出したいのですが。
2009年

@ marco92w:宣言されたエンコーディングが間違っていても問題ありません。楽しみのための基準は確立されていません。
ガンボ

1
@ガンボ:しかし、実際の世界で作業している場合は、宣言されたエンコードが正しくないなどの問題に対処できる必要があります。問題は、一部のテキストだけからエンコーディングを(正しく)推測することが非常に難しいことです。標準は素晴らしいですが、そこにある多くの(ほとんどの)ページ/フィードは標準に準拠していません。
ケビンORourke 2009年

@Kevin ORourke:その通りです。それが私の問題です。@ガンボ:はい、それは私の問題です。フィードを読み取って集約したい。だから私は間違ったエンコーディングを修正しなければなりません。
2009年

@ marco92w:ただし、正しいエンコーディングと現在のエンコーディングがわからない場合は、エンコーディングを修正できません。そして、それの何charset/のencoding宣言のための場合:データがでエンコードされたエンコードを記述する。
ガンボ

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){}

答えをありがとう、ジョクル。関数mb_convert_encoding()は、すでにここにあるものですよね?;)したがって、答えの中で唯一新しいのは、すべての変数のエンコーディングを変更するループです。
2010年

2

私は古くからエンコーディングの解決策をチェックしていました、このページはおそらく何年もの検索の結果です!私はあなたが言及した提案のいくつかをテストしました、そしてこれが私のメモです:

これは私のテスト文字列です:

これは "wròngwrìtten"文字列ですが、fùnctìonによって変換された、pù 'sòme'の特別なチャールを参照する必要があります!! & それでおしまい!

INSERTを実行して、この文字列をデータベースの次のように設定されているフィールドに保存します utf8_general_ci

私のページの文字セットはUTF-8です。

そのようにINSERTを実行すると、私のデータベースには、おそらく火星からの文字がいくつかあります...

だから私はそれらをいくつかの「健全な」UTF-8に変換する必要があります。私は試したutf8_encode()が、それでもエイリアンの文字が私のデータベースに侵入していました...

そこでforceUTF8、8番に投稿された関数を使用しようとしましたが、データベースでは、保存された文字列は次のようになります。

これは、「wròngwrìtten」文字列です。これを見るには、「sòme」という特別なチャンネルを使用する必要があります。fùnctìonによって変換されます。& それでおしまい!

したがって、このページでさらに情報を収集し、それらを他のページの他の情報とマージして、この解決策で問題を解決しました。

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

これで、データベースに正しいエンコーディングの文字列ができました。

注: 機能するのは、注意することだけmysql_client_encodingです。この関数はパラメーターとしてリソースIDを必要とするため、データベースに接続する必要があります。

しかし、まあ、私はINSERTの前に再エンコードを実行するだけなので、問題はありません。


1
そもそもなぜUTF-8mysqlのクライアントエンコーディングを使用しないのですか?このように手動で変換する必要はありません
エサイリヤ2012

2

それは簡単です:あなたはUTF-8ではない何かを得るとき、あなたはしなければならないエンコードというの UTF-8。

したがって、ISO 8859-1である特定のフィードをフェッチする場合は、それを解析しますutf8_encode

ただし、UTF-8フィードを取得する場合は、何もする必要はありません。


ありがとう!OK、mb-detect-encoding()を使用してフィードがどのようにエンコードされているかを確認できますよね?しかし、フィードがASCIIの場合はどうすればよいですか?utf8-encode()は、ISO-8859-1からUTF-8だけに対応していませんか?
2009年

ASCIIはISO-8859-1およびUTF-8のサブセットであるため、utf8-encode()を使用しても変更は行われません-実際にはASCIIのみの場合
Michael Borgwardt

それで、UTF-8でない場合は常にutf8_encodeを使用できますか?これは本当に簡単でしょう。mb-detect-encoding()によるとASCIIであったテキストに「&#228;」が含まれていました。これはASCII文字ですか?それともHTMLですか?
2009年

それがHTMLです。実際にはエンコードされているので、特定のページに印刷すると問題ありません。必要な場合は、最初にut8_encode()、次にhtml_entity_decode()を使用できます。
セブ

1
文字ßは、バイトシーケンス0xC39FでUTF-8にエンコードされます。Windows-1252で解釈すると、そのシーケンスは2つの文字Â(0xC3)とŸ(0x9F)を表します。また、このバイトシーケンスをUTF-8で再度エンコードすると、Windows-1252の「ƒƒ」を表す0xC383 0xC29Fが得られます。したがって、あなたの間違いは、この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機能するか。


自動更新は「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;
    }
}
?>

私はそれをテストしていないので、保証はありません。おそらくもっと簡単な方法があるでしょう。


ありがとうございました。2番目の引数としての「auto」と「UTF-8、ASCII、ISO-8859-1」の違いは何ですか?「自動」はより多くのエンコーディングを備えていますか?それなら、「auto」を使うほうがいいのでは?バグなしで実際に機能する場合は、「ASCII」または「ISO-8859-1」を「UTF-8」に変更するだけです。どうやって?
2009年

2
関数はすべての場合にうまく機能しません。時々私はエラーを受け取ります:Notice:iconv():...の入力文字列で不正な文字を検出しました
caw

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ショップでこれを1日おきに見ます。前後にそれは正しいように見えるかもしれません。しかし、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

Ÿのもじばけですß。あなたのデータベースでは、あなたは16進数を持っているかもしれません

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からのものである限り、メタ要素で「charset」を使用する必要があると私は結論付けました。Simple HTML DOM Parserは、無効なHTMLをサポートしているため使用しています。

以下のスニペットは、Webページからタイトル要素を抽出します。ページ全体を変換したい場合は、いくつかの行を削除することができます。

<?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

私はphpQueryUTF-8の代わりにISO-8859-1)で同じ問題があり、このハックは私を助けました:

$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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.