it-swarm.com.de

UTF-8 bis zum Ende

Ich richte einen neuen Server ein und möchte UTF-8 in meiner Webanwendung vollständig unterstützen. Ich habe dies in der Vergangenheit auf vorhandenen Servern versucht und muss immer wieder auf ISO-8859-1 zurückgreifen.

Wo genau muss ich die Kodierung/Zeichensätze einstellen? Ich bin mir bewusst, dass ich Apache, MySQL und PHP konfigurieren muss, um dies zu tun. Gibt es eine Standardcheckliste, der ich folgen kann, oder kann ich möglicherweise Fehler beheben, bei denen die Abweichungen auftreten?

Dies ist für einen neuen Linux-Server, auf dem MySQL 5, PHP 5 und Apache 2 ausgeführt werden.

1133
mercutio

Datenspeicherung :

  • Geben Sie den Zeichensatz _utf8mb4_ für alle Tabellen und Textspalten in Ihrer Datenbank an. Dadurch speichert und ruft MySQL Werte ab, die nativ in UTF-8 codiert sind. Beachten Sie, dass MySQL implizit die Codierung _utf8mb4_ verwendet, wenn eine Sortierung _utf8mb4_*_ angegeben wird (ohne expliziten Zeichensatz).

  • In älteren Versionen von MySQL (<5.5.3) werden Sie leider gezwungen sein, einfach _utf8_ zu verwenden, das nur eine Teilmenge von Unicode-Zeichen unterstützt. Ich wünschte, ich mache Witze.

Datenzugriff :

  • In Ihrem Anwendungscode (z. B. PHP) müssen Sie bei jeder von Ihnen verwendeten DB-Zugriffsmethode den Verbindungszeichensatz auf _utf8mb4_ setzen. Auf diese Weise führt MySQL keine Konvertierung von seinem nativen UTF-8 durch, wenn Daten an Ihre Anwendung übergeben werden und umgekehrt.

  • Einige Treiber stellen einen eigenen Mechanismus zum Konfigurieren des Verbindungszeichensatzes bereit, der sowohl den eigenen internen Status aktualisiert als auch MySQL über die für die Verbindung zu verwendende Codierung informiert. Dies ist normalerweise der bevorzugte Ansatz. In PHP:

    • Wenn Sie die Abstraktionsebene PDO mit PHP ≥ 5.3.6 verwenden, können Sie charset in DSN angeben:

      _$dbh = new PDO('mysql:charset=utf8mb4');
      _
    • Wenn Sie mysqli verwenden, können Sie set_charset() aufrufen:

      _$mysqli->set_charset('utf8mb4');       // object oriented style
      mysqli_set_charset($link, 'utf8mb4');  // procedural style
      _
    • Wenn Sie mit plain mysql nicht weiterkommen, aber zufällig PHP ≥ 5.2.3 ausführen, können Sie mysql_set_charset aufrufen.

  • Wenn der Treiber keinen eigenen Mechanismus zum Festlegen des Verbindungszeichensatzes bereitstellt, müssen Sie möglicherweise eine Abfrage absetzen, um MySQL mitzuteilen, wie Ihre Anwendung Daten für die zu verschlüsselnde Verbindung erwartet: SET NAMES 'utf8mb4' .

  • Die gleiche Überlegung bezüglich _utf8mb4_/_utf8_ gilt wie oben.

Ausgabe :

  • Wenn Ihre Anwendung Text an andere Systeme überträgt, müssen diese auch über die Zeichenkodierung informiert werden. Bei Webanwendungen muss der Browser über die Codierung informiert werden, in der Daten gesendet werden (über HTTP-Antwortheader oder HTML-Metadaten ).

  • In PHP können Sie die Option default_charset php.ini verwenden oder den MIME-Header _Content-Type_ manuell ausgeben, was nur mehr Arbeit ist, aber den gleichen Effekt hat.

  • Wenn Sie die Ausgabe mit json_encode() codieren, fügen Sie _JSON_UNESCAPED_UNICODE_ als zweiten Parameter hinzu.

Eingabe :

  • Leider sollten Sie jede empfangene Zeichenfolge als gültige UTF-8-Zeichenfolge überprüfen, bevor Sie versuchen, sie zu speichern oder irgendwo zu verwenden. PHP's mb_check_encoding() macht den Trick, aber Sie müssen ihn religiös verwenden. Daran führt kein Weg vorbei, da böswillige Clients Daten in der von ihnen gewünschten Codierung übermitteln können, und ich habe keinen Trick gefunden, um PHP dazu zu bringen, dies zuverlässig für Sie zu tun.

  • Nach meiner Lektüre der aktuellen HTML-Spezifikation sind die folgenden Unterpunkte für modernes HTML nicht mehr erforderlich oder sogar gültig. Mein Verständnis ist, dass Browser mit Daten im für das Dokument angegebenen Zeichensatz arbeiten und diese übermitteln. Wenn Sie jedoch auf ältere HTML-Versionen (XHTML, HTML4 usw.) abzielen, können diese Punkte dennoch nützlich sein:

    • Nur für HTML vor HTML5 : Sie möchten, dass alle von Browsern an Sie gesendeten Daten in UTF-8 vorliegen. Wenn Sie dies jedoch nur zuverlässig tun, fügen Sie das _accept-charset_ -Attribut zu allen Ihren _<form>_ -Tags hinzu: _<form ... accept-charset="UTF-8">_.
    • Nur für HTML vor HTML5 : Beachten Sie, dass in der W3C-HTML-Spezifikation festgelegt ist, dass Clients standardmäßig Formulare an den Server senden sollen, und zwar in dem Zeichensatz, den der Server bereitgestellt hat. Dies ist jedoch anscheinend nur der Fall eine Empfehlung, daher die Notwendigkeit, auf jedem einzelnen _<form>_ -Tag explizit anzugeben.

Weitere Überlegungen zum Code :

  • Selbstverständlich sollten alle von Ihnen bereitgestellten Dateien (PHP, HTML, JavaScript usw.) in gültigem UTF-8 codiert sein.

  • Sie müssen sicherstellen, dass Sie dies bei jeder Verarbeitung einer UTF-8-Zeichenfolge sicher tun. Dies ist leider der schwierige Teil. Sie werden wahrscheinlich die PHP-Erweiterung mbstring umfassend nutzen wollen.

  • Die in PHP eingebauten Zeichenfolgenoperationen sind nicht standardmäßig UTF-8-sicher. Sie haben einige Probleme Kann sicher mit normalen PHP Zeichenkettenoperationen (wie Verkettung) umgehen, aber für die meisten Dinge sollten Sie die entsprechende Funktion mbstring verwenden.

  • Um zu wissen, was Sie tun (lesen Sie: nicht vermasseln), müssen Sie UTF-8 und dessen Funktionsweise auf der niedrigstmöglichen Ebene wirklich kennen. Schauen Sie sich einen der Links von tf8.com an, um zu erfahren, was Sie wissen müssen.

972
chazomaticus

Ich möchte eine Sache hinzufügen zu chazomaticus 'exzellente Antwort :

Vergessen Sie auch nicht das META-Tag (wie dieses oder die HTML4- oder XHTML-Version davon ):

<meta charset="utf-8">

Das scheint trivial zu sein, aber der IE7 hat mir vorher Probleme damit gemacht.

Ich habe alles richtig gemacht; Die Datenbank, die Datenbankverbindung und der Content-Type-HTTP-Header wurden alle auf UTF-8 festgelegt. In allen anderen Browsern funktionierte dies einwandfrei, Internet Explorer bestand jedoch weiterhin auf der Verwendung der "westeuropäischen" Codierung.

Es stellte sich heraus, dass auf der Seite das META-Tag fehlt. Das Hinzufügen löste das Problem.

Bearbeiten:

Das W3C hat tatsächlich ein ziemlich großes Abschnitt für I18N . Es gibt eine Reihe von Artikeln zu diesem Thema, die die HTTP-, (X) HTML- und CSS-Seite der Dinge beschreiben:

Sie empfehlen die Verwendung des HTTP-Headers und des HTML-Metatags (oder der XML-Deklaration im Fall von XHTML als XML).

146
mercator

Zusätzlich zur Einstellung von default_charset in der php.ini können Sie den korrekten Zeichensatz mit header() aus Ihrem Code heraus vor jeder Ausgabe senden:

header('Content-Type: text/html; charset=utf-8');

Die Arbeit mit Unicode in PHP ist einfach, solange Sie feststellen, dass die meisten Zeichenfolgenfunktionen mit Unicode nicht funktionieren und einige Zeichenfolgen möglicherweise vollständig entstellen. PHP betrachtet "Zeichen" als 1 Byte lang. Manchmal ist dies in Ordnung (zum Beispiel sucht explode() nur nach einer Byte-Sequenz und verwendet sie als Trennzeichen - es spielt also keine Rolle, nach welchen tatsächlichen Zeichen Sie suchen). In anderen Fällen, in denen die Funktion für die Verarbeitung von Zeichen entwickelt wurde, hat PHP keine Ahnung, dass Ihr Text Multibyte-Zeichen enthält, die mit Unicode gefunden werden .

Eine gute Bibliothek zum Einchecken ist phputf8 . Dadurch werden alle "schlechten" Funktionen neu geschrieben, sodass Sie sicher an UTF8-Zeichenfolgen arbeiten können. Es gibt Erweiterungen wie die mbstring-Erweiterung, die versuchen, dies auch für Sie zu tun, aber ich bevorzuge die Verwendung der Bibliothek, weil sie portabler ist (aber ich schreibe Massenmarktprodukte, das ist für mich wichtig). Phputf8 kann jedoch hinter den Kulissen mbstring verwenden, um die Leistung zu steigern.

61
chroder

Ich habe ein Problem mit jemandem gefunden, der PDO verwendet, und die Antwort lautete, dies für die PDO-Verbindungszeichenfolge zu verwenden:

$pdo = new PDO(
    'mysql:Host=mysql.example.com;dbname=example_db',
    "username",
    "password",
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

Die Seite, von der ich dies genommen habe, ist nicht erreichbar, aber ich konnte es glücklicherweise über den Google-Cache abrufen.

28
Brad F Jacobs

In meinem Fall habe ich mb_split verwendet, das Regex verwendet. Daher musste ich auch manuell sicherstellen, dass die Regex-Codierung utf-8 war, indem ich mb_regex_encoding('UTF-8'); ausführte.

Als Randnotiz habe ich auch durch Ausführen von mb_internal_encoding() festgestellt, dass die interne Codierung nicht utf-8 ist, und das habe ich durch Ausführen von mb_internal_encoding("UTF-8"); geändert.

22
JDelage

Zuallererst, wenn Sie in <5.3PHP sind, dann nein. Sie haben eine Menge Probleme zu lösen.

Ich bin überrascht, dass niemand die intl -Bibliothek erwähnt hat, die gute Unterstützung für Unicode bietet. , Grapheme , Zeichenkettenoperationen , Lokalisierung und viele mehr, siehe unten.

Ich werde einige Informationen zur Unicode-Unterstützung in PHP von Elizabeth Smiths Folien unter PHPBenelux'14 zitieren

INTL

Gut:

  • Wrapper um ICU Bibliothek
  • Standardisierte Gebietsschemas, Gebietsschema per Skript festlegen
  • Zahlenformatierung
  • Währungsformatierung
  • Nachrichtenformatierung (ersetzt gettext)
  • Kalender, Daten, Zeitzone und Uhrzeit
  • Übersetzer
  • Spoofchecker
  • Ressourcenpakete
  • Konverter
  • IDN-Unterstützung
  • Grapheme
  • Kollation
  • Iteratoren

Schlecht:

  • Unterstützt nicht zend_multibite
  • Unterstützt keine HTTP-Eingabe/Ausgabe-Konvertierung
  • Unterstützt keine Funktionsüberladung

mb_string

  • Aktiviert die Unterstützung von zend_multibyte
  • Unterstützt transparente HTTP-In/Out-Codierung
  • Bietet einige Wrapper für die Funktionalität, z. B. strtoupper

ICONV

  • Primär für Zeichensatzkonvertierung
  • Ausgabepuffer-Handler
  • mIME-Codierungsfunktionalität
  • umwandlung
  • einige String-Helfer (len, substr, strpos, strrpos)
  • Stream Filter stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

DATENBANKEN

  • mysql: Zeichensatz und Kollatierung von Tabellen und Verbindungen (nicht die Kollatierung). Verwenden Sie auch kein mysql - msqli oder PDO
  • postgresql: pg_set_client_encoding
  • sqlite (3): Stellen Sie sicher, dass es mit Unicode- und Intl-Unterstützung kompiliert wurde

Einige andere Fallstricke

  • Sie können Unicode-Dateinamen nicht mit PHP und Windows verwenden, es sei denn, Sie verwenden eine Erweiterung des dritten Teils.
  • Senden Sie alles in ASCII, wenn Sie exec, proc_open und andere Befehlszeilenaufrufe verwenden
  • Klartext ist kein Klartext, Dateien haben Kodierungen
  • Mit dem iconv-Filter können Sie Dateien im Handumdrehen konvertieren

Ich werde diese Antwort aktualisieren, falls sich die Funktionen ändern und so weiter.

21
Jimmy Kane

Ich habe kürzlich festgestellt, dass die Verwendung von strtolower() zu Problemen führen kann, bei denen die Daten nach einem Sonderzeichen abgeschnitten werden.

Die Lösung war zu verwenden

mb_strtolower($string, 'UTF-8');

mb_ verwendet MultiByte. Es unterstützt mehr Zeichen, ist aber im Allgemeinen etwas langsamer.

13
Notflip

Das einzige, was ich zu diesen erstaunlichen Antworten hinzufügen möchte, ist das Speichern Ihrer Dateien in utf8-Codierung. Ich habe bemerkt, dass Browser diese Eigenschaft akzeptieren, indem sie utf8 als Ihre Codierung festlegen. Jeder anständige Texteditor zeigt Ihnen dies an. Notepad ++ verfügt beispielsweise über eine Menüoption für die Dateicodierung, zeigt Ihnen die aktuelle Codierung an und ermöglicht Ihnen, diese zu ändern. Für alle meine PHP-Dateien verwende ich utf8 ohne BOM.

Vor einiger Zeit hatte ich jemanden gebeten, utf8-Unterstützung für eine von jemand anderem entwickelte PHP/MySQL-Anwendung hinzuzufügen. Ich bemerkte, dass alle Dateien in ANSI codiert waren, also musste ich ICONV zum Konvertieren aller Dateien verwenden und die Datenbanktabellen ändern, um die zu verwenden utf8 charset und utf8_general_ci sortieren, fügen Sie nach der Verbindung 'SET NAMES utf8' zur Datenbankabstraktionsschicht hinzu (wenn Sie 5.3.6 oder früher verwenden, müssen Sie andernfalls charset = utf8 in der Verbindungszeichenfolge verwenden) und ändern Sie die Zeichenfolgenfunktionen, um das PHP-Multibyte zu verwenden String-Funktionen äquivalent.

12
Puerto AGP

Ich habe gerade dasselbe Problem durchlaufen und in PHP Handbüchern eine gute Lösung gefunden.

Ich habe meine gesamte Dateicodierung in UTF8 geändert und dann die Standardcodierung für meine Verbindung. Dies löste alle Probleme.

if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
   printf("Current character set: %s\n", $mysqli->character_set_name());
}

Quelltext anzeigen

8

In PHP müssen Sie entweder Multibyte-Funktionen verwenden oder mbstring.func_overload aktivieren. Auf diese Weise funktionieren Dinge wie strlen, wenn Sie Zeichen haben, die mehr als ein Byte benötigen.

Sie müssen auch den Zeichensatz Ihrer Antworten identifizieren. Sie können entweder AddDefaultCharset wie oben verwenden oder PHP Code schreiben, der den Header zurückgibt. (Oder Sie können Ihren HTML-Dokumenten ein META-Tag hinzufügen.)

8
JW.

Die Unicode-Unterstützung in PHP ist immer noch ein großes Durcheinander. Es ist zwar in der Lage, eine ISO8859-Zeichenfolge (die intern verwendet wird) in utf8 zu konvertieren, es fehlt jedoch die Fähigkeit, mit Unicode-Zeichenfolgen nativ zu arbeiten, was bedeutet, dass alle Zeichenfolgenverarbeitungsfunktionen Ihre Zeichenfolgen entstellen und beschädigen. Sie müssen entweder eine separate Bibliothek für die ordnungsgemäße Unterstützung von utf8 verwenden oder alle Funktionen zur Zeichenfolgenbehandlung selbst neu schreiben.

Der einfache Teil ist nur die Angabe des Zeichensatzes in HTTP-Headern und in der Datenbank und dergleichen, aber nichts davon ist von Bedeutung, wenn Ihr PHP Code kein gültiges UTF8 ausgibt. Das ist der schwierige Teil, und PHP gibt Ihnen dort praktisch keine Hilfe. (Ich denke, PHP6 soll das Schlimmste beheben, aber das ist noch eine Weile entfernt.)

6
jalf

Wenn Sie möchten, dass der MySQL-Server über den Zeichensatz entscheidet und nicht PHP als Client (altes Verhalten; meiner Meinung nach bevorzugt), versuchen Sie, skip-character-set-client-handshake zu Ihrem my.cnf unter [mysqld] hinzuzufügen und mysql neu zu starten. .

Dies kann Probleme verursachen, wenn Sie etwas anderes als UTF8 verwenden.

5

Die Top-Antwort ist hervorragend. Hier ist, was ich bei einem regulären Debian/PHP/MySQL-Setup machen musste:

// storage
// debian. apparently already utf-8

// retrieval
// the mysql database was stored in utf-8, 
// but apparently php was requesting iso. this worked: 
// ***notice "utf8", without dash, this is a mysql encoding***
mysql_set_charset('utf8');

// delivery
// php.ini did not have a default charset, 
// (it was commented out, shared Host) and
// no http encoding was specified in the Apache headers.
// this made Apache send out a utf-8 header
// (and perhaps made php actually send out utf-8)
// ***notice "utf-8", with dash, this is a php encoding***
ini_set('default_charset','utf-8');

// submission
// this worked in all major browsers once Apache
// was sending out the utf-8 header. i didnt add
// the accept-charset attribute.

// processing
// changed a few commands in php, like substr,
// to mb_substr

das war alles !

4
commonpike

Nur eine Notiz:

Sie haben das Problem, dass Ihre nicht-lateinischen Charaktere als ????????? angezeigt werden. Sie haben eine Frage gestellt und sie wurde mit einem Verweis auf diese kanonische Frage geschlossen. Sie haben alles versucht und egal, was Sie tun, Sie erhalten immer noch ?????????? von MySQL.

Dies liegt hauptsächlich daran, dass Sie Ihre alten Daten testen, die mit dem falschen Zeichensatz in die Datenbank eingefügt und in die Fragezeichenzeichen ? konvertiert und dort gespeichert wurden. Was bedeutet, dass Sie Ihren Originaltext für immer verloren haben und unabhängig davon, was Sie versuchen, ??????? erhalten.

wenn Sie das, was Sie aus den Antworten auf diese Frage gelernt haben, auf neue Daten anwenden, können Sie Ihr Problem lösen.

0
Accountant م

wenn Sie eine MySQL-Lösung möchten, hatte ich nach einer Servermigration ähnliche Probleme mit zwei meiner Projekte. Nachdem ich eine Menge Lösungen gesucht und ausprobiert hatte, stieß ich auf diese/nichts, bevor diese funktionierte.

mysqli_set_charset($con,"utf8");

Nach dem Hinzufügen dieser Zeile zu meiner Konfigurationsdatei funktioniert alles einwandfrei!

Ich fand diese Lösung https://www.w3schools.com/PHP/func_mysqli_set_charset.asp als ich suchte, eine Einfügung von der HTML-Abfrage zu lösen

viel Glück!

0
castro_pereira