Erkennen Sie die Codierung und machen Sie alles UTF-8

304

Ich lese viele Texte aus verschiedenen RSS-Feeds vor und füge sie in meine Datenbank ein.

Natürlich werden in den Feeds verschiedene Zeichencodierungen verwendet, z. B. UTF-8 und ISO 8859-1.

Leider gibt es manchmal Probleme mit der Kodierung der Texte. Beispiel:

  1. Das "ß" in "Fußball" sollte in meiner Datenbank so aussehen: "Ÿ". Wenn es sich um ein "" handelt, wird es korrekt angezeigt.

  2. Manchmal sieht das "ß" in "Fußball" in meiner Datenbank so aus: "ß". Dann wird es natürlich falsch angezeigt.

  3. In anderen Fällen wird das "ß" als "ß" gespeichert - also ohne Änderung. Dann wird es auch falsch angezeigt.

Was kann ich tun, um die Fälle 2 und 3 zu vermeiden?

Wie kann ich alles gleich codieren, vorzugsweise UTF-8? Wann muss ich verwenden utf8_encode(), wann muss ich verwenden utf8_decode()(es ist klar, was der Effekt ist, aber wann muss ich die Funktionen verwenden?) Und wann darf ich nichts mit der Eingabe tun?

Wie mache ich alles gleich codiert? Vielleicht mit der Funktion mb_detect_encoding()? Kann ich dafür eine Funktion schreiben? Meine Probleme sind also:

  1. Wie finde ich heraus, welche Codierung der Text verwendet?
  2. Wie konvertiere ich es in UTF-8 - unabhängig von der alten Codierung?

Würde eine solche Funktion funktionieren?

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

Ich habe es getestet, aber es funktioniert nicht. Was stimmt damit nicht?

krächzen
quelle
36
"Das" ß "in" Fußball "sollte in meiner Datenbank so aussehen:" Ÿ "." Nein, es sollte wie ß aussehen. Stellen Sie sicher, dass Ihre Sortierung und Verbindung korrekt eingerichtet sind. Andernfalls wird das Sortieren und Suchen für Sie unterbrochen.
Rich Bradshaw
5
Ihre Datenbank ist schlecht eingerichtet. Wenn Sie Unicode-Inhalte speichern möchten, konfigurieren Sie sie einfach dafür. Anstatt zu versuchen, das Problem in Ihrem PHP-Code zu umgehen, sollten Sie zuerst die Datenbank reparieren.
Dolmen
2
USE: $ from = mb_detect_encoding ($ text); $ text = mb_convert_encoding ($ text, 'UTF-8', $ from);
Informate.it

Antworten:

363

Wenn Sie utf8_encode()eine bereits UTF-8-Zeichenfolge anwenden , wird eine verstümmelte UTF-8-Ausgabe zurückgegeben.

Ich habe eine Funktion erstellt, die all diese Probleme behebt. Es heißt Encoding::toUTF8().

Sie müssen nicht wissen, wie Ihre Zeichenfolgen codiert sind. Es kann Latin1 ( ISO 8859-1) , Windows-1252 oder UTF-8 sein, oder die Zeichenfolge kann eine Mischung aus diesen haben. Encoding::toUTF8()konvertiert alles in UTF-8.

Ich habe es getan, weil ein Dienst mir einen Daten-Feed gab, der alle durcheinander brachte und UTF-8 und Latin1 in derselben Zeichenfolge mischte.

Verwendungszweck:

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

Herunterladen:

https://github.com/neitanod/forceutf8

Ich habe eine weitere Funktion Encoding::fixUFT8()hinzugefügt, die jede UTF-8-Zeichenfolge repariert, die verstümmelt aussieht.

Verwendungszweck:

require_once('Encoding.php');
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::fixUTF8($garbled_utf8_string);

Beispiele:

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");

wird ausgegeben:

Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football

Ich habe die Funktion ( forceUTF8) in eine Familie statischer Funktionen für eine Klasse namens umgewandelt Encoding. Die neue Funktion ist Encoding::toUTF8().

Sebastián Grignoli
quelle
1
Wenn Sie sich den Code ansehen, ruft fixUTF8 einfach immer wieder forceUTF8 auf, bis die Zeichenfolge unverändert zurückgegeben wird. Ein Aufruf von fixUTF8 () dauert mindestens doppelt so lange wie ein Aufruf von forceUTF8 (), ist also viel weniger performant. Ich habe fixUTF8 () erstellt, um ein Befehlszeilenprogramm zu erstellen, das "verschlüsselte" Dateien reparieren kann, aber in einer Live-Umgebung wird dies selten benötigt.
Sebastián Grignoli
3
Wie werden Nicht-UTF8-Zeichen in UTF8 konvertiert, ohne zu wissen, in welcher Codierung sich die ungültigen Zeichen befinden?
Philfreo
4
Es wird ISO-8859-1 angenommen, die Antwort sagt dies bereits. Der einzige Unterschied zwischen forceUTF8 () und utf8_encode () besteht darin, dass forceUTF8 () UTF8-Zeichen erkennt und unverändert lässt.
Sebastián Grignoli
28
"Sie müssen nicht wissen, wie Ihre Zeichenfolgen codiert sind." - Ich bin sehr anderer Meinung. Raten und Versuchen mag funktionieren, aber Sie werden immer früher oder später auf Randfälle stoßen, in denen dies nicht der Fall ist.
Täuschung
4
Ich bin vollkommen einverstanden. Tatsächlich wollte ich nicht sagen, dass diese Klasse Ihnen in der Regel helfen könnte, wenn dies die Situation ist, in der Sie sich gerade befinden
Sebastián Grignoli,
74

Sie müssen zuerst feststellen, welche Codierung verwendet wurde. Während Sie RSS-Feeds analysieren (wahrscheinlich über HTTP), sollten Sie die Codierung aus dem charsetParameter des Content-TypeHTTP-Headerfelds lesen . Wenn es nicht vorhanden ist, lesen Sie die Codierung aus dem encodingAttribut der XML-Verarbeitungsanweisung . Wenn dies ebenfalls fehlt, verwenden Sie UTF-8 wie in der Spezifikation definiert .


Bearbeiten    Hier ist, was ich wahrscheinlich tun würde:

Ich würde cURL verwenden , um die Antwort zu senden und abzurufen. Auf diese Weise können Sie bestimmte Headerfelder festlegen und auch den Antwortheader abrufen. Nach dem Abrufen der Antwort müssen Sie die HTTP-Antwort analysieren und in Header und Body aufteilen. Der Header sollte dann das Content-TypeHeaderfeld enthalten , das den MIME-Typ und (hoffentlich) auch den charsetParameter mit der Codierung / dem Zeichensatz enthält. Wenn nicht, analysieren wir den XML-PI auf das Vorhandensein des encodingAttributs und erhalten von dort die Codierung. Wenn dies ebenfalls fehlt, definieren die XML-Spezifikationen die Verwendung von UTF-8 als Codierung.

$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();
    }
}
Gumbo
quelle
Vielen Dank. Das wäre einfach. Aber würde es wirklich funktionieren? In den HTTP-Headern oder in den Attributen von XML sind häufig falsche Codierungen angegeben.
Caw
25
Nochmals: Das ist nicht dein Problem. Es wurden Standards festgelegt, um solche Probleme zu vermeiden. Wenn andere ihnen nicht folgen, ist es ihr Problem, nicht deins.
Gumbo
Ok, ich denke du hast mich jetzt endlich überzeugt. :)
Caw
Danke für den Code. Aber warum nicht einfach nutzen? paste.bradleygill.com/index.php?paste_id=9651 Ihr Code ist viel komplexer. Was ist besser daran?
Caw
Zunächst stellen Sie zwei Anforderungen, eine für den HTTP-Header und eine für die Daten. Zweitens Sie suchen jeden Anschein von charset=und encoding=nicht nur an den entsprechenden Stellen. Und drittens überprüfen Sie nicht, ob die deklarierte Codierung akzeptiert wird.
Gumbo
39

Das Erkennen der Codierung ist schwierig.

mb_detect_encodingfunktioniert durch Raten, basierend auf einer Reihe von Kandidaten, die Sie bestehen. In einigen Codierungen sind bestimmte Byte-Sequenzen ungültig und können daher zwischen verschiedenen Kandidaten unterscheiden. Leider gibt es viele Codierungen, bei denen dieselben Bytes gültig sind (aber unterschiedlich). In diesen Fällen gibt es keine Möglichkeit, die Codierung zu bestimmen. In diesen Fällen können Sie Ihre eigene Logik implementieren, um Vermutungen anzustellen. Beispielsweise haben Daten, die von einer japanischen Site stammen, möglicherweise eher eine japanische Codierung.

Solange Sie nur mit westeuropäischen Sprachen beschäftigen, die drei großen Kodierungen zu berücksichtigen sind utf-8, iso-8859-1und cp-1252. Da dies für viele Plattformen Standardeinstellungen sind, wird auch am wahrscheinlichsten falsch darüber berichtet. Z.B. Wenn Leute unterschiedliche Codierungen verwenden, sind sie wahrscheinlich offen darüber, da sonst ihre Software sehr oft kaputt gehen würde. Daher ist es eine gute Strategie, dem Anbieter zu vertrauen, es sei denn, die Codierung wird als eine dieser drei angegeben. Sie sollten immer noch überprüfen, ob es tatsächlich gültig ist, indem Sie mb_check_encoding(beachten Sie, dass gültig nicht dasselbe ist wie sein - dieselbe Eingabe kann für viele Codierungen gültig sein). Wenn es eines davon ist, können Sie es verwendenmb_detect_encodingzwischen ihnen zu unterscheiden. Zum Glück ist das ziemlich deterministisch; Sie müssen nur die richtige Erkennungssequenz verwenden UTF-8,ISO-8859-1,WINDOWS-1252.

Sobald Sie die Codierung erkannt haben, müssen Sie sie in Ihre interne Darstellung konvertieren ( UTF-8ist die einzig vernünftige Wahl). Die Funktion utf8_encodeTransformationen ISO-8859-1auf UTF-8, so kann es nur für diesen bestimmten Eingabetyp verwendet. Verwenden Sie für andere Codierungen mb_convert_encoding.

troelskn
quelle
Vielen Dank! Was ist besser: mb-convert-encoding () oder iconv ()? Ich weiß nicht, was die Unterschiede sind. Ja, ich muss nur westeuropäische Sprachen analysieren, insbesondere Englisch, Deutsch und Französisch.
Caw
7
Ich habe gerade gesehen: mb-detect-encoding () ist nutzlos. Es werden nur UTF-8, UTF-7, ASCII, EUC-JP, SJIS, eucJP-win, SJIS-win, JIS und ISO-2022-JP unterstützt. Die für mich wichtigsten, ISO-8859-1 und WINDOWS-1252, werden nicht unterstützt. Daher kann ich mb-detect-encoding () nicht verwenden.
Caw
1
Du hast recht. Es ist schon eine Weile her, seit ich es benutzt habe. Sie müssen dann Ihren eigenen Erkennungscode schreiben oder ein externes Dienstprogramm verwenden. UTF-8 kann ziemlich zuverlässig bestimmt werden, da seine Escape-Sequenzen sehr charakteristisch sind. wp-1252 und iso-8859-1 können unterschieden werden, da wp-1252 möglicherweise Bytes enthält, die in iso-8859-1 unzulässig sind. Verwenden Sie Wikipedia, um die Details abzurufen, oder schauen Sie im Kommentarbereich von php.net unter verschiedenen Zeichensatzfunktionen nach.
Troelskn
Ich denke, Sie können die verschiedenen Kodierungen unterscheiden, wenn Sie sich die Formen ansehen, in denen die speziellen Singen auftauchen: Das deutsche "ß" taucht in verschiedenen Formen auf: Manchmal "Ÿ", manchmal "Ã" und manchmal "ß". Warum?
Caw
Ja, aber dann müssen Sie den Inhalt der Zeichenfolge kennen, bevor Sie sie vergleichen, und diese Art von Fehler macht den Zweck in erster Linie zunichte. Das deutsche ß erscheint anders, weil es in verschiedenen Codierungen unterschiedliche Werte hat. Einige Zeichen werden in verschiedenen Codierungen auf dieselbe Weise dargestellt (z. B. werden alle Zeichen im ASCII-Zeichensatz in utf-8, iso-8859- * und wp-1252 auf dieselbe Weise codiert), solange Sie sie verwenden Nur diese Charaktere sehen alle gleich aus. Deshalb werden sie manchmal als ASCII-kompatibel bezeichnet.
troelskn
14

Eine wirklich gute Möglichkeit, eine Funktion zu implementieren isUTF8, finden Sie auf php.net :

function isUTF8($string) {
    return (utf8_encode(utf8_decode($string)) == $string);
}
Harpax
quelle
16
Leider funktioniert dies nur, wenn die Zeichenfolge nur aus Zeichen besteht, die in ISO-8859-1 enthalten sind. Aber das könnte funktionieren: @iconv ('utf-8', 'utf-8 // IGNORE', $ str) == $ str
Christian Davén
@Christian: Genau das empfehlen auch die Autoren von High Performance MySQL.
Alix Axel
1
Es funktioniert nicht richtig: echo (int) isUTF8 ('z'); # 1 echo (int) isUTF8 (NULL); # 1
Yousha Aleayoub
1
Obwohl nicht perfekt, denke ich, dass dies eine gute Möglichkeit ist, eine skizzenhafte UTF-8-Prüfung durchzuführen.
Mateng
1
mb_check_encoding($string, 'UTF-8')
Täuschung
13

Dieses Cheatsheet listet einige häufige Vorbehalte im Zusammenhang mit der UTF-8-Handhabung in PHP auf: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

Diese Funktion zum Erkennen von Multibyte-Zeichen in einer Zeichenfolge kann sich ebenfalls als hilfreich erweisen ( Quelle ):


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

miek
quelle
2
Ich denke, das funktioniert nicht richtig: echo detectUTF8 ('3٣3'); # 1
Yousha Aleayoub
10

Ein bisschen Kopf hoch. Sie sagten, dass das "ß" in Ihrer Datenbank als "Ÿ" angezeigt werden sollte.

Dies liegt wahrscheinlich daran, dass Sie eine Datenbank mit Latin-1-Zeichencodierung verwenden oder dass Ihre PHP-MySQL-Verbindung möglicherweise falsch eingestellt ist. Dies bedeutet, dass P glaubt, dass MySQL UTF-8 verwendet, sodass Daten als UTF-8 gesendet werden Ihr MySQL glaubt jedoch, dass PHP Daten sendet, die als ISO 8859-1 codiert sind, und versucht daher möglicherweise erneut, Ihre gesendeten Daten als UTF-8 zu codieren, was zu solchen Problemen führt.

Schauen Sie sich mysql_set_charset an . Es kann Ihnen helfen.

Krynble
quelle
4

Ihre Codierung sieht so aus, als hätten Sie zweimal in UTF-8 codiert . das heißt, von einer anderen Codierung in UTF-8 und wieder in UTF-8. Als ob Sie ISO 8859-1 hätten, von ISO 8859-1 nach UTF-8 konvertiert und die neue Zeichenfolge für eine weitere Konvertierung in UTF-8 als ISO 8859-1 behandelt hätten.

Hier ist ein Pseudocode von dem, was du getan hast:

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

Du solltest es versuchen:

  1. Erkennen Sie die Codierung mit mb_detect_encoding()oder was auch immer Sie verwenden möchten
  2. Wenn es sich um UTF-8 handelt, konvertieren Sie in ISO 8859-1 und wiederholen Sie Schritt 1
  3. Schließlich wieder in UTF-8 konvertieren

Dies setzt voraus, dass Sie bei der "mittleren" Konvertierung ISO 8859-1 verwendet haben. Wenn Sie Windows-1252 verwendet haben, konvertieren Sie in Windows-1252 (latin1). Die ursprüngliche Quellcodierung ist nicht wichtig. Die, die Sie bei der fehlerhaften zweiten Konvertierung verwendet haben, ist.

Dies ist meine Vermutung, was passiert ist; Sie hätten kaum etwas anderes tun können, um vier Bytes anstelle eines erweiterten ASCII-Bytes zu erhalten.

Die deutsche Sprache verwendet auch ISO 8859-2 und Windows-1250 (Latein-2).

Ivan Vučica
quelle
3

Das Interessante an mb_detect_encodingund mb_convert_encodingist, dass die Reihenfolge der von Ihnen vorgeschlagenen Codierungen eine Rolle spielt:

// $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)

Daher möchten Sie möglicherweise eine bestimmte Reihenfolge verwenden, wenn Sie die erwarteten Codierungen angeben. Beachten Sie jedoch, dass dies nicht kinderleicht ist.

Halil Özgür
quelle
2
Dies liegt daran, dass ISO-8859-9 in der Praxis alle Binäreingaben akzeptiert. Gleiches gilt für Windows-1252 und Freunde. Sie müssen zuerst nach Codierungen suchen, die die Eingabe möglicherweise nicht akzeptieren.
Mikko Rantalainen
@ MikkoRantalainen, ja, ich denke, dieser Teil der Dokumentation sagt etwas Ähnliches: php.net/manual/en/function.mb-detect-order.php#example-2985
Halil Özgür
In Anbetracht der Tatsache, dass die WHATWG-HTML-Spezifikation Windows 1252 als Standardcodierung definiert, sollte die Annahme ziemlich sicher sein if ($input_is_not_UTF8) $input_is_windows1252 = true;. Siehe auch: html.spec.whatwg.org/multipage/…
Mikko Rantalainen
3

Sie müssen den Zeichensatz bei der Eingabe testen, da Antworten mit unterschiedlichen Codierungen codiert werden können.

Ich erzwinge, dass alle Inhalte an UTF-8 gesendet werden, indem die Erkennung und Übersetzung mit der folgenden Funktion durchgeführt wird:

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

Diese Routine wandelt alle PHP-Variablen, die vom Remote-Host kommen, in UTF-8 um.

Oder ignorieren Sie den Wert, wenn die Codierung nicht erkannt oder konvertiert werden konnte.

Sie können es an Ihre Bedürfnisse anpassen.

Rufen Sie es einfach auf, bevor Sie die Variablen verwenden.

Cavila
quelle
Was ist der Zweck der Verwendung von mb_detect_order () ohne übergebene Codierungsliste?
Giorgio79
Der Zweck besteht darin, das vom System konfigurierte geordnete Array von Codierungen zurückzugeben, die in der verwendeten php.ini definiert sind. Dies wird von mb_detect_encoding benötigt, um den dritten Parameter zu füllen.
Cavila
2

Das Ausarbeiten der Zeichenkodierung von RSS-Feeds scheint kompliziert zu sein . Selbst normale Webseiten lassen ihre Kodierung oft aus oder lügen darüber.

Sie könnten also versuchen, die Codierung auf die richtige Weise zu erkennen und dann auf eine Form der automatischen Erkennung (Vermutung) zurückzugreifen.

Kevin ORourke
quelle
Ich möchte die Codierung nicht aus den Feed-Informationen auslesen. Es ist also gleich, wenn die Feed-Informationen falsch sind. Ich möchte die Kodierung aus dem Text erkennen.
Caw
@ marco92w: Es ist nicht dein Problem, wenn die deklarierte Codierung falsch ist. Zum Spaß wurden keine Standards festgelegt.
Gumbo
1
@Gumbo: Aber wenn Sie in der realen Welt arbeiten, müssen Sie in der Lage sein, mit Dingen wie falsch deklarierten Codierungen umzugehen. Das Problem ist, dass es sehr schwierig ist, die Codierung nur aus einem Text (richtig) zu erraten. Standards sind wunderbar, aber viele (die meisten?) Der Seiten / Feeds entsprechen nicht diesen Standards.
Kevin ORourke
@ Kevin ORourke: Genau richtig. Das ist mein Problem. @ Gumbo: Ja, das ist mein Problem. Ich möchte die Feeds vorlesen und aggregieren. Also muss ich die falschen Kodierungen korrigieren.
Caw
@ marco92w: Sie können die Codierung jedoch nicht korrigieren, wenn Sie die korrekte Codierung und die aktuelle Codierung nicht kennen. Und das ist, was die charset/ encodingDeklaration ist, wenn für: Beschreiben Sie die Codierung, in der die Daten codiert sind.
Gumbo
2

Ich weiß, dass dies eine ältere Frage ist, aber ich denke, eine nützliche Antwort tut nie weh. Ich hatte Probleme mit der Codierung zwischen einer Desktopanwendung, SQLite und GET / POST-Variablen. Einige wären in UTF-8, andere in ASCII, und im Grunde würde alles durcheinander geraten, wenn fremde Charaktere involviert würden.

Hier ist meine Lösung. Es bereinigt Ihr GET / POST / REQUEST (ich habe Cookies weggelassen, aber Sie können sie bei Bedarf hinzufügen) bei jedem Laden der Seite vor der Verarbeitung. Es funktioniert gut in einem Header. PHP gibt Warnungen aus, wenn die Quellcodierung nicht automatisch erkannt werden kann. Daher werden diese Warnungen mit @ unterdrückt.

//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){}
scherzhaft
quelle
Danke für die Antwort, Jocull. Die Funktion mb_convert_encoding () ist das, was wir hier schon hatten, oder? ;) Das einzig Neue in deiner Antwort sind also die Schleifen, um die Codierung in allen Variablen zu ändern.
Caw
2

Ich habe seit Ewigkeiten nach Lösungen für die Codierung gesucht, und diese Seite ist wahrscheinlich das Ergebnis jahrelanger Suche! Ich habe einige der von Ihnen erwähnten Vorschläge getestet und hier sind meine Notizen:

Dies ist meine Testzeichenfolge:

Dies ist eine "wròng wrìtten" -Saite, aber ich muss spezielle Chàrs machen, um sie zu sehen, konvertiert von fùnctìon !! & das ist es!

Ich mache ein INSERT, um diese Zeichenfolge in einer Datenbank in einem Feld zu speichern, das als festgelegt ist utf8_general_ci

Der Zeichensatz meiner Seite ist UTF-8.

Wenn ich einfach so ein INSERT mache, habe ich in meiner Datenbank einige Charaktere, die wahrscheinlich vom Mars kommen ...

Also muss ich sie in ein "vernünftiges" UTF-8 konvertieren. Ich habe es versucht utf8_encode(), aber immer noch dringen außerirdische Zeichen in meine Datenbank ein ...

Also habe ich versucht, die forceUTF8auf Nummer 8 angegebene Funktion zu verwenden , aber in der Datenbank sieht die gespeicherte Zeichenfolge folgendermaßen aus:

Dies ist eine "geschriebene" Saite, die ich nicht unbedingt sehen muss, um sie zu sehen, konvertiert von fincton !! & das ist es!

Nachdem ich weitere Informationen auf dieser Seite gesammelt und mit anderen Informationen auf anderen Seiten zusammengeführt habe, habe ich mein Problem mit dieser Lösung gelöst:

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

Jetzt habe ich in meiner Datenbank meine Zeichenfolge mit korrekter Codierung.

HINWEIS: Nur der zu beachtende Hinweis ist funktionsfähig mysql_client_encoding! Sie müssen mit der Datenbank verbunden sein, da diese Funktion eine Ressourcen-ID als Parameter benötigt.

Aber gut, ich mache diese Neucodierung einfach vor meinem INSERT, also ist es für mich kein Problem.

Mauro
quelle
1
Warum verwenden Sie nicht einfach die UTF-8Client-Codierung für MySQL? Würde keine manuelle Konvertierung auf diese Weise benötigen
Esailija
2

Es ist ganz einfach: Wenn Sie etwas, das nicht UTF-8 ist, müssen Sie codieren , dass in UTF-8.

Wenn Sie also einen bestimmten Feed abrufen, der ISO 8859-1 entspricht, analysieren Sie ihn utf8_encode.

Wenn Sie jedoch einen UTF-8-Feed abrufen, müssen Sie nichts tun.

Seb
quelle
Vielen Dank! OK, ich kann mithilfe von mb-detect-encoding () herausfinden, wie der Feed codiert wird, oder? Aber was kann ich machen, wenn der Feed ASCII ist? utf8-encode () ist nur für ISO-8859-1 bis UTF-8, nicht wahr?
Caw
ASCII ist eine Teilmenge von ISO-8859-1 UND UTF-8, daher sollte die Verwendung von utf8-encode () keine Änderung vornehmen - WENN es tatsächlich nur ASCII ist
Michael Borgwardt
Also kann ich immer utf8_encode verwenden, wenn es nicht UTF-8 ist? Das wäre wirklich einfach. Der Text, der gemäß mb-detect-encoding () ASCII war, enthielt "& # 228;". Ist das ein ASCII-Zeichen? Oder ist es HTML?
Caw
Das ist HTML. Eigentlich ist das so codiert, dass es beim Drucken auf einer bestimmten Seite in Ordnung ist. Wenn Sie möchten, können Sie zuerst ut8_encode (), dann html_entity_decode ().
Seb
1
Das Zeichen ß wird in UTF-8 mit der Bytefolge 0xC39F codiert. In Windows-1252 interpretiert, repräsentiert diese Sequenz die beiden Zeichen  (0xC3) und Ÿ (0x9F). Und wenn Sie diese Bytesequenz erneut mit UTF-8 codieren, erhalten Sie 0xC383 0xC29F, was in Windows-1252 für ƒ steht. Ihr Fehler besteht also darin, diese UTF-8-codierten Daten als etwas mit einer anderen Codierung als UTF-8 zu behandeln. Dass diese Byte-Sequenz als das Zeichen dargestellt wird, das Sie sehen, ist nur eine Frage der Interpretation. Wenn Sie eine andere Codierung / einen anderen Zeichensatz verwenden, werden wahrscheinlich andere Zeichen angezeigt.
Gumbo
1

php.net/mb_detect_encoding

echo mb_detect_encoding($str, "auto");

oder

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

Ich weiß wirklich nicht, was die Ergebnisse sind, aber ich würde vorschlagen, dass Sie nur einige Ihrer Feeds mit unterschiedlichen Codierungen nehmen und versuchen, ob es mb_detect_encodingfunktioniert oder nicht.

update
auto ist die Abkürzung für "ASCII, JIS, UTF-8, EUC-JP, SJIS". Es gibt den erkannten Zeichensatz zurück, mit dem Sie die Zeichenfolge mit iconv in utf-8 konvertieren können .

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

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

Ich habe es nicht getestet, also keine Garantie. und vielleicht gibt es einen einfacheren Weg.

Stefs
quelle
Danke dir. Was ist der Unterschied zwischen 'auto' und 'UTF-8, ASCII, ISO-8859-1' als zweitem Argument? Verfügt 'Auto' über mehr Codierungen? Dann wäre es besser, 'auto' zu verwenden, nicht wahr? Wenn es wirklich ohne Fehler funktioniert, muss ich nur "ASCII" oder "ISO-8859-1" in "UTF-8" ändern. Wie?
Caw
2
Ihre Funktion funktioniert nicht in allen Fällen gut. Manchmal erhalte ich eine Fehlermeldung: Hinweis: iconv (): Es wurde ein unzulässiges Zeichen in der Eingabezeichenfolge in ...
caw
1

@harpax das hat bei mir funktioniert. In meinem Fall ist das gut genug:

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

Vergessen Sie nach dem Aussortieren Ihrer PHP-Skripte nicht, mysql mitzuteilen, welchen Zeichensatz Sie übergeben und erhalten möchten.

Beispiel: Zeichensatz utf8 setzen

Das Übergeben von utf8-Daten an eine latin1-Tabelle in einer latin1-E / A-Sitzung führt zu diesen unangenehmen Vogelfüßen. Ich sehe das jeden zweiten Tag in Oscommerce-Läden. Zurück und viertens scheint es richtig. Aber phpmyadmin wird die Wahrheit zeigen. Wenn Sie MySQL mitteilen, welchen Zeichensatz Sie übergeben, wird die Konvertierung von MySQL-Daten für Sie durchgeführt.

Wie man vorhandene verschlüsselte MySQL-Daten wiederherstellt, ist ein weiterer zu diskutierender Thread. :) :)

tim
quelle
0

Diese Version ist für die deutsche Sprache, aber Sie können die $ CHARSETS und die $ TESTCHARS ändern

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

Lukas Gottschall
quelle
0

Holen Sie sich die Codierung aus den Headern und konvertieren Sie sie in 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); }
Arsen
quelle
0

Ÿist Mojibake für ß. In Ihrer Datenbank haben Sie möglicherweise hex

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

Sie sollten nicht jede Codierung / Decodierung Funktionen in PHP verwenden; Stattdessen sollten Sie die Datenbank und die Verbindung zu ihr korrekt einrichten.

Wenn MySQL beteiligt ist, lesen Sie: Probleme mit utf8-Zeichen; Was ich sehe, ist nicht das, was ich gespeichert habe

Rick James
quelle
0

Ich finde hier eine Lösung 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;
}

Ich denke, dass @ eine schlechte Entscheidung ist, und nehme einige Änderungen an der Lösung von Deer.org.ua vor.

übertroffen
quelle
0

Die am häufigsten gewählte Antwort funktioniert nicht. Hier ist meins und hoffe es hilft.

function toUTF8($raw) {
    try{
        return mb_convert_encoding($raw, "UTF-8", "auto"); 
    }catch(\Exception $e){
        return mb_convert_encoding($raw, "UTF-8", "GBK"); 
    }
}
ch271828n
quelle
1
Haben Sie einen Einblick, warum oder wie sich Ihre Dateien unterschieden? Welche Teile haben bei Ihnen nicht funktioniert? Beispiel: Deutsche Großbuchstaben wurden nicht korrekt konvertiert. Neugierig, was ist "GBK"?
SherylHohman
-1

Wenn Sie versuchen, mit mehreren Sprachen wie Japanisch und Koreanisch umzugehen, können Probleme auftreten. mb_convert_encoding mit dem Parameter 'auto' funktioniert nicht gut. Das Festlegen von mb_detect_order ('ASCII, UTF-8, JIS, EUC-JP, SJIS, EUC-KR, UHC') hilft nicht, da EUC- * falsch erkannt wird.

Ich kam zu dem Schluss, dass, solange Eingabezeichenfolgen aus HTML stammen, in einem Metaelement 'Zeichensatz' verwendet werden sollte. Ich verwende Simple HTML DOM Parser, weil es ungültiges HTML unterstützt.

Das folgende Snippet extrahiert das Titelelement von einer Webseite. Wenn Sie die gesamte Seite konvertieren möchten, möchten Sie möglicherweise einige Zeilen entfernen.

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

Ich hatte das gleiche Problem mit phpQuery ( ISO-8859-1 anstelle von UTF-8 ) und dieser Hack hat mir geholfen:

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

mb_internal_encoding('UTF-8'), phpQuery::newDocumentHTML($html, 'utf-8'), mbstring.internal_encodingUnd andere Manipulationen irgendeine Wirkung nicht nehmen.

user2448995
quelle
-1

Versuchen Sie es ohne "Auto"

Das ist:

mb_detect_encoding($text)

anstatt:

mb_detect_encoding($text, 'auto')

Weitere Informationen finden Sie hier: mb_detect_encoding

tkartas
quelle