Jörg: mysql, Umlautproblem

Hallo,

ich hole mir aus einer utf-8-Tabelle einer DB Daten.

Anschließend welchsle ich in eine andere DB, dort habe ich eine latin1 codierte Tabelle und habe in einer varchar-Spalte ein Umlautproblem, das ich wie folgt lösen wollte, aber nicht konnte:

$iso88591 = utf8_decode($utf8);
$iso88591 = iconv('UTF-8', 'ISO-8859-1', $utf8);
$iso88591 = mb_convert_encoding($utf8, 'ISO-8859-1', 'UTF-8');

Keine dieser 3 Optionen hat gegriffen.

Was kann ich noch tun?

Jörg

  1. Hallo Jörg,

    habe in einer varchar-Spalte ein Umlautproblem

    Aha. Soso. 🤔🔮 🤷‍♂️

    Was kann ich noch tun?

    Mehr Infos geben. Alle drei deiner Funktionen führen zum gleichen Ergebnis. D.h. deine Voraussetzungen sind falsch.

    Guckst Du (flugs mit der PHP Sandbox getestet):

    <?php
    $utf8 = "Hällo";
    dump_bytes($utf8);
    
    $latin1 = utf8_decode($utf8);
    dump_bytes($latin1);
    
    $latin2 = iconv('UTF-8', 'ISO-8859-1', $utf8);
    dump_bytes($latin2);
    
    $latin3 = mb_convert_encoding($utf8, 'ISO-8859-1', 'UTF-8');
    dump_bytes($latin3);
    
    function dump_bytes($src) {
        echo $src . " - " . bin2hex($src) . "\n";
    }
    

    Ausgabe ist:

    Hällo - 48c3a46c6c6f
    H�llo - 48e46c6c6f
    H�llo - 48e46c6c6f
    H�llo - 48e46c6c6f
    

    D.h. alle 3 Funktionen übersetzen den UTF-8 codierten String in einen Latin-1 codierten String. C3A4 ist die UTF-8 Codierung und E4 die Latin-1 Codierung von ä.

    Wenn diese Konvertierung dein Problem nicht löst, hast Du ein anderes Problem, als Du vermutest. Welches? Keine Ahnung. Prüfe mit bin2hex genauer, welche DB-Abfragen welche Codierung liefern. Außer der Codierung, die für DB, Schema, Table oder Column gesetzt ist, kann auch die Connection eine Codierung haben. Ich bin jetzt nicht sicher, welche Codierung PHP per Default einstellt, das kannst Du aber abfragen.

    Ich nehme an, damit kommst Du der Sache näher.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      D.h. alle 3 Funktionen übersetzen den UTF-8 codierten String in einen Latin-1 codierten String. C3A4 ist die UTF-8 Codierung und E4 die Latin-1 Codierung von ä.

      Wenn diese Konvertierung dein Problem nicht löst, hast Du ein anderes Problem, als Du vermutest. Welches? Keine Ahnung. Prüfe mit bin2hex genauer, welche DB-Abfragen welche Codierung liefern. Außer der Codierung, die für DB, Schema, Table oder Column gesetzt ist, kann auch die Connection eine Codierung haben. Ich bin jetzt nicht sicher, welche Codierung PHP per Default einstellt, das kannst Du aber abfragen.

      Ich nehme an, damit kommst Du der Sache näher.

      Du hast vollkommen recht. Aber ohne Deine Hilfe hätte ich das nie gefunden. Ich war schlicht ganz doof in den Spalten verrutscht und habe die falschen Werte konvertiert. Das ist mir dann über das Testen mit Deinen Beispielen aufgefallen. 1000 Dank! 👍 Jetzt, wo ich die korrekten Spalten konvertiere, ist auch das Umlautproblem verschwunden.

      Jörg

    2. Tach!

          echo $src . " - " . bin2hex($src) . "\n";
      

      Am besten missbraucht man urlencode() dafür, das ist übersichtlicher als bin2hex() Der Vorteil ist, dass die ASCII-Buchstaben und Ziffern lesbar bleiben, denn die sind üblicherweise nicht das Problem. Und durch die Prozentzeichen sind die Bytes auch bei längeren Passagen einfacher zu erkennen.

      Außer der Codierung, die für DB, Schema, Table oder Column gesetzt ist, kann auch die Connection eine Codierung haben.

      Kann nicht nur, sondern sie ist neben der Feldkodierung die wichtigste Angabe.

      Ich bin jetzt nicht sicher, welche Codierung PHP per Default einstellt,

      Gar keine. Wenn die Verbindungskodierung nicht explizit im Script gesetzt wird, geht MySQL von einem bei ihm konfigurierten Defaultwert aus.

      dedlfix.

  2. Tach!

    ich hole mir aus einer utf-8-Tabelle einer DB Daten.

    Dabei kann man viel falsch machen, weil es mehrere Stellen gibt, an denen man eine Kodierung einstellen/verhandeln kann.

    Die wichtigsten Stellen sind Kodierung der Felder, Kodierung der Datenbank und die Kodierung der Verbindung. Die Kodierung der Datenbank spielt dann eine Rolle, wenn mehrere Kodierungen verwendet werden und MySQL umkodieren soll. MySQL kodiert vor allem dann um, wenn die Verbindungskodierung auf einer anderen Kodierung steht als das Feld, in das geschrieben oder von dem gelesen werden soll. Jeses Feld kann dabei seine eigene Kodierung haben.

    Anschließend welchsle ich in eine andere DB, dort habe ich eine latin1 codierte Tabelle und habe in einer varchar-Spalte ein Umlautproblem, das ich wie folgt lösen wollte, aber nicht konnte:

    Mit PHP muss man da nichts weiter machen, als die Kodierung der Verbindung nach dem Connect zu setzen. MySQL kodiert das selbständig für die entsprechenden Felder um.

    Voraussetzung, dass es keine Kodierungsprobleme gibt, ist natürlich, dass in Vergangenheit, Gegenwart und Zukunft die Kodierungsangaben ordnungsgemäß gesetzt/verhandelt wurden und die zum DBMS gesendeten Daten auch dementsprechend kodiert waren.

    Das zu testen eignet sich als Hausmittel der phpMyAdmin. Wenn der die Nicht-ASCII-Zeichen korrekt anzeigt, ist üblicherweise alles in Ordnung. Wenn nicht, sollte man erstmal reparieren. Wie das genau zu bewerkstelligen ist, kommt auf das konkrete Fehlerbild an.

    $iso88591 = utf8_decode($utf8);
    $iso88591 = iconv('UTF-8', 'ISO-8859-1', $utf8);
    $iso88591 = mb_convert_encoding($utf8, 'ISO-8859-1', 'UTF-8');
    

    Keine dieser 3 Optionen hat gegriffen.

    Was kann ich noch tun?

    Das System Kodierung zu verstehen ist eine Grundvoraussetzung. Da Unerfahrene dabei häufig Fehler machen, ist es auch zu wissen von Vorteil, wie sich Fehler auswirken. Und wenn man anderen die Probleme schildern möchte, sollte man dabei sehr exakt sein. "Hat nicht gegriffen" ist nichtssagend, daraus kann man nicht entnehmen was schiefläuft.

    dedlfix.

    1. Hallo dedlfix,

      Mit PHP muss man da nichts weiter machen, als die Kodierung der Verbindung nach dem Connect zu setzen. MySQL kodiert das selbständig für die entsprechenden Felder um.

      Mein Problem ist, dass ich in einem einzigen Script zuerst die utf-8 kodierten daten und dann die latin1 Daten behandle.

      Voraussetzung, dass es keine Kodierungsprobleme gibt, ist natürlich, dass in Vergangenheit, Gegenwart und Zukunft die Kodierungsangaben ordnungsgemäß gesetzt/verhandelt wurden und die zum DBMS gesendeten Daten auch dementsprechend kodiert waren.

      Ich komme eigentlich mit beiden Datenbanken ganz gut klar, wenn ich sie einzeln behandle, es war nur jetzt ein Problem, da ich Daten von der ersten für die zweite DB brauche. Aber nachdem ich jetzt die Daten von utf-8 in iso konvertiere, spielen beide DBs mit.

      Das System Kodierung zu verstehen ist eine Grundvoraussetzung. Da Unerfahrene dabei häufig Fehler machen, ist es auch zu wissen von Vorteil, wie sich Fehler auswirken. Und wenn man anderen die Probleme schildern möchte, sollte man dabei sehr exakt sein. "Hat nicht gegriffen" ist nichtssagend, daraus kann man nicht entnehmen was schiefläuft.

      Zugegeben, ich blicke da nicht 100% durch, aber dachte eigentlich schon, die entscheidenden Stelle zu kennen, wo man aufpassen muss. Einmal den html-header, dann die db-connection und dann natürlich die Codierung der DB selber und die Codierung der Textspalten selber.

      Wenn man an diesen Schnittstellen einheitlich dieselbe Codierung verwendet, sollte man eigentlich sicher sein.

      Jörg

      1. Hallo Jörg,

        du kannst aber beim Herstellen der DB-Connection das Charset einstellen. Ich hatte Dir oben verlinkt, wie man es abfragt - man kann es auch einstellen.

        Wenn Du für beide Connections das gleiche Charset einstellst, solltest Du doch am manüllen Konvertieren der Daten vorbeikommen können.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hi Rolf,

          du kannst aber beim Herstellen der DB-Connection das Charset einstellen. Ich hatte Dir oben verlinkt, wie man es abfragt - man kann es auch einstellen.

          Mache ich ja. UTF-8 für DB1 und latin1 für die andere. Aber wenn ich dann Daten von DB1 in DB2 schreibe, muss ich die UTF-8-Daten konvertieren, sonst gibts Probleme. Und ich könnte auch nicht mit latin1 in DB1 gehen, weil die Daten dort utf-8-konvertiert sind.

          Ich komme also am manuelle Konvertieren nicht vorbei.

          Wenn Du für beide Connections das gleiche Charset einstellst, solltest Du doch am manüllen Konvertieren der Daten vorbeikommen können.

          Jörg

          1. Hi,

            Mache ich ja. UTF-8 für DB1 und latin1 für die andere. Aber wenn ich dann Daten von DB1 in DB2 schreibe, muss ich die UTF-8-Daten konvertieren, sonst gibts Probleme.

            Bedenke dabei, daß die meisten Unicode-Zeichen keine Entsprechung in ISO-8859-1 haben (ISO-8859-1 kann weniger als 257 Zeichen, Unicode einige Zigtausend)

            cu,
            Andreas a/k/a MudGuard

          2. Ich komme also am manuelle Konvertieren nicht vorbei.

            Du solltest dringend aufräumen und Dich dabei endlich UTF-8 zuwenden, statt das zu versuchen, was bei Microsoft schon seit Jahren schief geht: bunt zu mixen.

            Aber mal ein Wort zu dem Kodierungen und mysql/mysql:

            • Du hast da einerseits die Kodierung für die Tabellen-Daten. Diese hat Einfluss darauf, wie MySQL/MariaDB die Daten speichert (ISO-8859-1 kann dann z.B. nichts mit dem €-Symbol anfangen, das gibt es in ISO-8859-15 oder UTF-8) und sortiert.
            • Du hast andererseits die Kodierung, die MySQL/MariaDB für die Kommunikation verwendet.
            • Dann wäre da noch die Kodierung für Namen…

            Wie auch immer - die immer wieder hier genannten Probleme kenne ich gar nicht mehr, seit ich konsequent auf UTF-8 (genauer: utf8mb4) setze.

            Lesestoff:

            Andererseits bietet mir meine Linux-Installation auch genügend schöne Möglichkeiten um Datenbestände die in antik kodierten (ISO-, IBM_, WINDOWS-, CP) Textdateien (csv, Dumps, ...) vorliegen, zu konvertieren: iconv, dann sed um Metadaten zu ändern...

            Warum diese Konsequenz? Weil ich „keinen an der Waffel“ habe:

            iconv -l „sagt“:


            Die folgende Liste enthält alle bekannten Zeichensatzkodierungen. Das bedeutet nicht, dass zwischen allen Kombinationen dieser Namen als FROM und TO Parameter konvertiert werden kann. Eine Zeichensatzkodierung kann unter verschiedenen Namen aufgeführt sein (sog. Aliasnamen).

            437, 500, 500V1, 850, 851, 852, 855, 856, 857, 858, 860, 861, 862, 863, 864, 865, 866, 866NAV, 869, 874, 904, 1026, 1046, 1047, 8859_1, 8859_2, 8859_3, 8859_4, 8859_5, 8859_6, 8859_7, 8859_8, 8859_9, 10646-1:1993, 10646-1:1993/UCS4, ANSI_X3.4-1968, ANSI_X3.4-1986, ANSI_X3.4, ANSI_X3.110-1983, ANSI_X3.110, ARABIC, ARABIC7, ARMSCII-8, ARMSCII8, ASCII,

            [… stark gekürzt (10KB Text). Grund: technische Beschränkungen des Forums …]

            US-ASCII, US, UTF-7, UTF-8, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, UTF-32LE, UTF7, UTF8, UTF16, UTF16BE, UTF16LE, UTF32, UTF32BE, UTF32LE, VISCII, WCHAR_T, WIN-SAMI-2, WINBALTRIM, WINDOWS-31J, WINDOWS-874, WINDOWS-936, WINDOWS-1250, WINDOWS-1251, WINDOWS-1252, WINDOWS-1253, WINDOWS-1254, WINDOWS-1255, WINDOWS-1256, WINDOWS-1257, WINDOWS-1258, WINSAMI2, WS2, YU


            Wer, bitte, will damit hantieren?

            1. Tach!

              • Du hast da einerseits die Kodierung für die Tabellen-Daten. Diese hat Einfluss darauf, wie MySQL/MariaDB die Daten speichert …

              Diese Aussage ist nicht eindeutig. Man kann Tabellen einen Kodierungswert zuweisen, aber das ist nur ein Defaultwert, wenn man Feldern keine Kodierung zuweist. Ansonsten ist ausschlaggebend, was für das einzelne Feld konfiguriert ist.

              … und sortiert.

              Zum Sortieren - genauer gesagt Vergleichen - sind die in einer Collation definierten Regeln zuständig, nicht die Kodierung. Eine Collation setzt aber auf eine bestimmte Kodierung auf, weil mit der Kodierung auch der Zeichensatz definiert ist. Es ist ja zum Beispiel nicht sinnvoll, Regel für kyrillische Zeichen in einer Collation für Latin1 zu haben.

              (ISO-8859-1 kann dann z.B. nichts mit dem €-Symbol anfangen, das gibt es in ISO-8859-15 oder UTF-8)

              ISO-8859-1 und ISO-8859-15 (und alle anderen der ISO-8859-Familie) sind für MySQL uninteressant. Es kennt stattdessen "latin1", was cp1252/Windows-1252 entspricht und eine Erweiterung von ISO-8859-1 ist. Und darin ist auch das €-Zeichen enthalten, aber an anderer Position als in ISO-8859-15.

              • Du hast andererseits die Kodierung, die MySQL/MariaDB für die Kommunikation verwendet.

              Genau genommen sind das drei Werte, mit unterschiedlicher Bedeutung.

              • Dann wäre da noch die Kodierung für Namen…

              Es gibt noch ein paar mehr.

              dedlfix.

                • Du hast da einerseits die Kodierung für die Tabellen-Daten. Diese hat Einfluss darauf, wie MySQL/MariaDB die Daten speichert …

                Diese Aussage ist nicht eindeutig. Man kann Tabellen einen Kodierungswert zuweisen, aber das ist nur ein Defaultwert, wenn man Fehttps://dev.mysql.com/doc/refman/8.0/en/charset-syntax.htmlldern keine Kodierung zuweist. Ansonsten ist ausschlaggebend, was für das einzelne Feld konfiguriert ist.

                Das ist natürlich richtig. Ich bin wohl etwas voreilig davon ausgegangen, dass man die einzelnen Spalten bezüglich der Kodierung nur aus sehr wichtigen und seltenen Gründen (und also recht selten) unterschiedlich kodiert. Völlig undenkbar sind solche Szenarien aber nicht.

                • Dann wäre da noch die Kodierung für Namen…

                Es gibt noch ein paar mehr.

                Für mich heißt das: „Noch mehr Gründe nicht zu mixen.“

            2. Hallo Raketen*,

              Ich komme also am manuelle Konvertieren nicht vorbei.

              Du solltest dringend aufräumen und Dich dabei endlich UTF-8 zuwenden, statt das zu versuchen, was bei Microsoft schon seit Jahren schief geht: bunt zu mixen.

              Nichts wäre mir lieber, aber ich habe sicher 30 Scripte, die PDFs generieren und da komme ich trotz tcpdf, fpdi und Konsorten leider nicht an fdpf (inkl. eigener umgeschriebener Klassen) vorbei. Und fpdf kennt (leider) kein utf-8. 😕

              Insofern wird es vorerst ein frommer Wunsch bleiben, auf utf-8 umzusteigen.

              In anderen Anwendungen, die ich heute stricke, bin ich selbstredend längst umgestiegen (daher übrigens auch das Problem dieses Threads). Aber in dieser Anwendung kann ich nicht so easy umsteigen, weil das unendlich viel Arbeit wäre, deren zufriedenstellendes Gelingen nicht mal garantiert wäre.

              Da ist es weitaus einfacher und sicherer die Umlautproblemchen zu lösen, wenn sie mal auftauchen. (so wie jetzt)

              Jörg

      2. Tach!

        Zugegeben, ich blicke da nicht 100% durch, aber dachte eigentlich schon, die entscheidenden Stelle zu kennen, wo man aufpassen muss.

        Als verallgemeinerte Faustregel gilt:

        1. Ein System muss mit der Kodierung der Daten klarkommen, außer es reicht nur unverändert durch.
        2. Das sendende System muss dem empfangenden System die verwendete Kodierung mitteilen.

        Ein Beispiel für 1. ist die Stringverarbeitung. Ansonsten steckt der Teufel natürlich im Detail. Um die beiden Punkte mit Leben zu füllen, muss man einerseits die jeweilgen Systeme kennen und andererseits den entsprechende Teil der Dateiformate und Übertragungsprotokolle.

        Wenn man an diesen Schnittstellen einheitlich dieselbe Codierung verwendet, sollte man eigentlich sicher sein.

        Ja, aber wie gesagt, kodiert MySQL auch selbständig um. Es ist also kein Problem, UTF-8 auf der Verbindung auszuhandeln, UTF-8-kodierte Daten zu senden und dabei Felder anzusprechen, die für Latin1 konfiguriert sind. Natürlich muss man sich dann auf die in Latin1 enthaltenen Zeichen beschränken, sonst landen Fragezeichen in den Daten, die ohne Intelligenz nicht wieder korrigiert werden können.

        dedlfix.

        1. Hi dedlfix,

          1. Das sendende System muss dem empfangenden System die verwendete Kodierung mitteilen.

          Das sendende System? Wie denn, wenn mysql die Daten an php sendet? Wo ist da die Mitteilung der verwendeten Kodierung?

          Ja, aber wie gesagt, kodiert MySQL auch selbständig um. Es ist also kein Problem, UTF-8 auf der Verbindung auszuhandeln, UTF-8-kodierte Daten zu senden und dabei Felder anzusprechen, die für Latin1 konfiguriert sind. Natürlich muss man sich dann auf die in Latin1 enthaltenen Zeichen beschränken, sonst landen Fragezeichen in den Daten, die ohne Intelligenz nicht wieder korrigiert werden können.

          Ok, verstehe. Soll heißen, wenn ich sicherstellen kann, dass ich ausschließlich latin1-Zeichen verwende, könnte ich alles connection, db und Felder konfigurieren, wie ich will? Interessant.

          Jörg

          1. Tach!

            Das sendende System? Wie denn, wenn mysql die Daten an php sendet? Wo ist da die Mitteilung der verwendeten Kodierung?

            Die Angabe der Verbindungskodierung gilt für beide Richtungen. Wenn du UTF-8 aushandelst, dann sendet dir MySQL auch die Daten in dieser Kodierung. Und wenn die Daten in den Feldern anders kodiert sind, konvertiert MySQL das jeweils entsprechend.

            Natürlich muss man sich dann auf die in Latin1 enthaltenen Zeichen beschränken, [...]

            Soll heißen, wenn ich sicherstellen kann, dass ich ausschließlich latin1-Zeichen verwende, könnte ich alles connection, db und Felder konfigurieren, wie ich will?

            Nein. Die Zeichen oberhalb von x80 sind in UTF-8 anders kodiert als in Latin1. Du kannst nicht UTF-8 angeben und dann Latin1 senden oder umgekehrt. Das "auf die in Latin1 enthaltenen Zeichen beschränken" heißt, sich zwar auf diese Zeichen zu beschränken, sie müssen aber trotzdem für UTF-8 entsprechend korrekt kodiert werden.

            Es ist wichtig, zwischen Zeichensatz und Zeichenkodierung zu unterscheiden. In der Aussage meinte ich den Zeichensatz. Latin1 ist jedoch Zeichensatz und Zeichenkodierung in einem. Das kann verwirren.

            dedlfix.

          2. Hallo Jörg,

            Soll heißen, wenn ich sicherstellen kann, dass ich ausschließlich latin1-Zeichen verwende, könnte ich alles connection, db und Felder konfigurieren, wie ich will?

            Du hast diverse Freiheiten, aber abhängig von deinen Entscheidungen musst Du natürlich die Folgen tragen.

            Wenn Du Dir sicher bist, dass Du nur Zeichen aus dem Latin-1 Zeichensatz verwendest, dann:

            • kannst Dir in der DB aussuchen, ob Du DB oder Felder latin-1 oder UTF-8 einstellst.
            • kannst Dir in PHP aussuchen, ob Du Strings in Latin-1 oder UTF-8 verarbeiten möchtest.

            Und abhängig von deiner PHP Entscheidung sagst Du MySQL, welchen Zeichensatz es auf der Connection verwenden soll. Der MySQL Treiber und Server führen dann die erforderlichen Konvertierungen durch.

            Wenn Du Dich im PHP für Latin-1 entscheidest, hast Du den Vorteil, jegliche Strings mit den "klassischen" Stringfunktionen verarbeiten zu können.

            Du hast aber den Nachteil, dass Du jegliche Kommunikation mit dem Browser zwischen UTF-8 und Latin-1 wandeln musst, oder Du setzt Dich über die HTML 5 Spezifikation hinweg. Die verlangt nämlich charset=UTF8. Eine ISO-8859-1 (Latin-1) Kommunikation mit dem Browser funktioniert zwar, weil die Browser sich die Bits und Bytes für Kompatibilität verbiegen, aber man weiß nie, wann aus "deprecated" oder "obsolete" ein "removed" wird.

            Die bessere Lösung ist meiner Meinung nach, im PHP sauber und modern mit UTF-8 zu arbeiten und auch per UTF-8 mit der DB zu kommunizieren. Du musst dann allerdings jeden String, den Du zur latin-1 DB schicken möchtest, darauf überprüfen, ob er Zeichen außerhalb des Latin-1 Zeichensatzes enthält. Oder eben die Fragezeichen in Kauf nehmen, die bei einem ungültigen Zeichen in der Datenbank erscheinen.

            Eine Überprüfung kannst Du mit iconv durchführen. Das liefert FALSE, wenn ein unkonvertierbares Zeichen drin ist.

            Man könnte nun einwenden, dass man dann auch gleich die Connection zur latin1 DB auf latin1 setzen. Sehe ich anders. Denn dann musst Du auch alles, was Du von dort liest, und jedes SQL Statement, das Du hinschickst, durch den iconv jagen.

            Rolf

            --
            sumpsi - posui - obstruxi