Patrick V.: Sonderzeichen umwandeln in Datenbank und wieder auslesen

Hi, seid gegrüßt,

Ich bin ziemlich neu hier und hoffe Ihr könnt mir bei einem Problem welches ich seid längerer Zeit habe helfen.

Ich habe ein loginscript gebastelt in PHP welches die Benutzerdaten in einer MySQL Datenbank ablegt.

Dabei sollen die Daten nach charset utf-8 verarbeitet werden.
Dies sage ich der Seite mit:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

Ich schicke die Daten durch ein HTML-Formular an meine Seite login.php

Code:
<form name="loginForm" method="post" action="login.php"> ...usw </form>

Danach greife ich meine Daten folgendem Code ab:
(Code steht ganz am Anfang vor dem eigentlichen HTML Quellcode)

$BENUTZER_NAME = $_POST['BENUTZER_NAME'];
$BENUTZER_NAME = addslashes(htmlspecialchars(trim($BENUTZER_NAME)));
$BENUTZER_NAME = ucfirst(strtolower($BENUTZER_NAME));

Die daten werden danach in die Datenbank geschrieben.

Die 'Kollation' der datenbankspalte in die der Benutzernamen geschrieben wird ist 'latin1_general_ci'

Nun ein Beispiel anhand eines Namens mit umlauten oder Sonderzeichen:
In der Datenbank steht nun 'Kã¤rstin' statt 'Kärstin'

Nach dem auslesen der Daten aus der DB (diese werden nicht nochmals umgewandelt oder auf Sonderzeichen etc überprüft) steht nun im Quelltext: 'K�rstin' aber ich möchte haben das dort K&auml;rstin steht.

Was mache ich falsch?
Habe ich irgendetwas vergessen?
Muss ich statt 'htmlspecialchars' 'htmlentities' benutzen? (wenn ich 'htmlentities' benutze steht in der DB 'K&atilde;&curren;rstin' und später im quelltext auch 'K&atilde;&curren;rstin'
Muss ich für die Datenbank eine andere 'Kollation' benutzen?
Muss ich PHP nochmal speziell sagen das es nach utf-8 umwandeln soll?

Es gibt sicherlich viele Fragen die ähnlich sind wie meine nur konnte mir bisher kein Ergebnis welches ich in Foren gefunden hatte weiterhelfen.

Vielen Dank schon mal.

  1. echo $begrüßung;

    Dabei sollen die Daten nach charset utf-8 verarbeitet werden.
    Dies sage ich der Seite mit:
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

    Nein, damit sagst du dem Browser, dass die Daten der Seite UTF-8-kodiert seien, falls es im gleichnamigen HTTP-Header keine charset-Angabe gibt, denn diese wäre verbindlicher als die Meta-Angabe.

    Du musst dich an diese Angabe dann auch halten, ansonsten fehlinterpretiert der Browser die Byte-Werte.

    $BENUTZER_NAME = $_POST['BENUTZER_NAME'];
    $BENUTZER_NAME = addslashes(htmlspecialchars(trim($BENUTZER_NAME)));
    $BENUTZER_NAME = ucfirst(strtolower($BENUTZER_NAME));

    trim() ist ok. strtolower() und ucfirst() kann man gelten lassen, wenn es mir auch nicht gefallen würde. Aber addslashes() ist für MySQL eine unzureichende Maßnahme, weil damit nicht alle MySQL-Sonderzeichen berücksichtigt werden. mysql_real_escape_string() solltest du stattdessen anwenden, und das auch erst zu dem Zeitpunkt, wenn du das SQL-Statement erzeugst. Andere String-Bearbeitungsschritte haben danach nichts mehr zu suchen. htmlspecialchars() ist der völlig falsche Zeitpunkt. Daten müssen erst dann HTML-gerecht aufbereitet werden, wenn du sie in einen HTML-Kontext ausgibst. In der Datenbank haben HTML-Maskierungen nichts verloren. Sie behindern nur die Anwendung von Stringfunktionen und Sortierungen und stören auch, wenn die Daten mal in einen anderen Kontext als HTML überführt werden sollen.

    Die daten werden danach in die Datenbank geschrieben.

    Vermutlich hast du dabei, wie so viele Fragende mit ähnlichen Problemen, nicht beachtet, dem Server mitzuteilen, in welcher Kodierung du dich mit ihm unterhalten möchtest. Dieser Schritt ist so wichtig, dass er bereits auf der Einstiegsseite des MySQL-Handbuchkapitels Character Set Support erwähnt wird. (Stichwort SET NAMES)

    Die 'Kollation' der datenbankspalte in die der Benutzernamen geschrieben wird ist 'latin1_general_ci'

    Warum dies? Damit kannst du nur die 256 Latin1-Zeichen speichern, aber nicht den kompletten Umfang von UTF-8.

    Nun ein Beispiel anhand eines Namens mit umlauten oder Sonderzeichen:
    In der Datenbank steht nun 'Kã¤rstin' statt 'Kärstin'

    Das sieht so aus, als ob der Server von einer Default-Einstellung für Client-Server-Verbindungen von Latin1 ausgeht, du ihm aber UTF-8 sendest. Er betrachtet dann die zwei Bytes eines UTF-8-kodierten ä als zwei Latin1-Zeichen.

    Nach dem auslesen der Daten aus der DB (diese werden nicht nochmals umgewandelt oder auf Sonderzeichen etc überprüft) steht nun im Quelltext: 'K�rstin' aber ich möchte haben das dort K&auml;rstin steht.

    Entities (&auml;) oder numerische Zeichenreferenzen (NCR) (&#xE4;) brauchst du nicht mehr, denn mit UTF-8 können diese Zeichen direkt verwendet werden. Das � deutet darauf hin, dass der Browser UTF-8 erwartet, aber keine gültige UTF-8-Sequenz erhält, z.B. weil ein Latin1-ä ausgeliefert wird. Mir scheint, hier hast du doch irgendwo eine Konvertierung nach Latin1 drin.

    Muss ich statt 'htmlspecialchars' 'htmlentities' benutzen?

    Für eine Ausgabe im HTML-Kontext reicht es, htmlspecialchars() zu benutzen. HTML-Entities oder NCR brauchst du nur dann, wenn du Zeichen verwendest, die in der gewählten Kodierung nicht vorkommen, was aber bei UTF-8 nicht der Fall sein kann.

    Muss ich für die Datenbank eine andere 'Kollation' benutzen?

    Es ist empfehlenswert, eine UTF-8-Kollation zu verwenden, wenn du keinen Zeichenverlust haben möchtest. Wenn die Datenbank UTF-8-kodierte Zeichen bekommt, die mit Latin1 nicht darstellbar sind, kann MySQL daraus nur ein ? machen.

    Muss ich PHP nochmal speziell sagen das es nach utf-8 umwandeln soll?

    Wenn du in Richtung Browser und in Richtung Datenbank UTF-8 sprichst, muss PHP nur noch durchreichen (Kontextabhängige Maskierungen dabei nicht vergessen!).

    Es gibt sicherlich viele Fragen die ähnlich sind wie meine nur konnte mir bisher kein Ergebnis welches ich in Foren gefunden hatte weiterhelfen.

    Das glaube ich eher nicht, oder du hast das Archiv dieses Forums ausgelassen. Denn dann müsste ich in der Vergangenheit zu oft solche Fragen unverständlich beantwortet haben. :-)

    echo "$verabschiedung $name";

    1. Danke dir dedlfix,

      Das glaube ich eher nicht, oder du hast das Archiv dieses Forums ausgelassen. Denn dann müsste ich in der Vergangenheit zu oft solche Fragen unverständlich beantwortet haben. :-)

      Du hast recht ich hab leider mehr gegoogled statt hier im Forum direkt danach zu suchen. *duck*

      strtolower() und ucfirst() wollte ich eigentlich deswegen einsetzen da ich nicht wollte das Benutzer sich zum Beispiel SKiLOEr oder ähnlich nennen können aber ich habe nun festgestellt das dadurch auch Sonderzeichen evtl. falsch umgesetzt werden und habe mich davon nun entfernt.

      Danke für den Tipp mit mysql_real_escape_string(), schein wirklich besser zu funktionieren.

      Die 'Kollation' der datenbankspalte in die der Benutzernamen geschrieben wird ist 'latin1_general_ci'

      Hatte hier im Forum gelesen das es zwischen 'latin1_general_ci' und UTF-8 keine Komplikationen geben soll, auch als ich die Datenbank auf UTF-8 umgestellt hatte bekam ich immer das gleiche falsche Ergebnis.

      Vermutlich hast du dabei, wie so viele Fragende mit ähnlichen Problemen, nicht beachtet, dem Server mitzuteilen, in welcher Kodierung du dich mit ihm unterhalten möchtest. Dieser Schritt ist so wichtig, dass er bereits auf der Einstiegsseite des MySQL-Handbuchkapitels Character Set Support erwähnt wird. (Stichwort SET NAMES)

      Ich ähm *KOPFDICK* habs einfach vergessen gehabt und eigentlich ist dies auch der einzigste Grund warum mir Sonderzeichen falsch abgespeichert und ausgegeben wurden.

      Für eine Ausgabe im HTML-Kontext reicht es, htmlspecialchars() zu benutzen. HTML-Entities oder NCR brauchst du nur dann, wenn du Zeichen verwendest, die in der gewählten Kodierung nicht vorkommen, was aber bei UTF-8 nicht der Fall sein kann.

      Danke dir! Jetzt muss ich mir darum auch keinen Kopf mehr machen.

      Schlusswort:

      Funktioniert nun bestens!
      Vielen Dank für die schnelle Hilfe dedlfix!

      1. echo $begrüßung;

        Hatte hier im Forum gelesen das es zwischen 'latin1_general_ci' und UTF-8 keine Komplikationen geben soll, auch als ich die Datenbank auf UTF-8 umgestellt hatte bekam ich immer das gleiche falsche Ergebnis.

        Wenn du die Kodierung eines Felds änderst, dann kodiert MySQL den Inhalt um. Wenn in einem als Latin1 deklariertem Feld zwei Bytes stehen, die als UTF-8 interpretiert eine gültige Sequenz darstellen bzw. ein Zeichen, in Latin1 aber als zwei Zeichen angesehen werden, und du die Kodierung von Latin1 auf UTF-8 änderst, dann werden die zwei Zeichen jedes für sich zu zwei UTF-8-Sequenzen umkodiert. Das ergibt dann 4 Bytes, bleiben aber zwei Zeichen, diesmal jedoch UTF-8-kodiert. Umgekehrt ist das natürlich genauso.
        Nachvollziehen kann man das mit der Funktion LENGTH(), die immer die Länge in Bytes liefert. (Die Länge in Zeichen bekommt man mit CHAR_LENGTH().)

        echo "$verabschiedung $name";