*Markus: (LINUX / BASH / MYSQL) Verständnisproblem bei Zeichenkodierung

Hallo,

vor kurzem hatte ich hier wegen Problemen bei der Kodierung gepostet, bei denen Umlaute beim Speichern von Daten über ein Servlet in eine MySQL-DB falsch dargestellt wurde.
Ich glaube, dass ich es fürs erste mal so weit hinbiegen konnte, indem ich einfach im Servlet bei jedem Aufruf ein

  
   request.setCharacterEncoding("UTF-8");  
   response.setCharacterEncoding("UTF-8");  

setze. Die Vorgehensweise kommt mir zwar noch ein wenig dilettantisch vor, aber sie scheint zu funktionieren.
Dabei gibt es aber doch noch einen Störfaktor:
Die Daten werden in der JSP-Seite richtig dargestellt. Die Daten werden im MySQL Query Browser ebenfalls richtig dargestellt. Rufe ich die Daten aber über die Bash (Gnome-Terminal) ab, also wenn ich mit mysql schema -u user -p und mit einem select * die Daten aus der DB abrufe, habe ich anstatt der Umlaute nur bemalte Fragezeichen.
Die im System verwendete Kodierung ist definitiv UTF-8:

locale

root@archy markus # locale
LANG=de_AT.utf8
LC_CTYPE="de_AT.utf8"
LC_NUMERIC="de_AT.utf8"
LC_TIME="de_AT.utf8"
LC_COLLATE=C
LC_MONETARY="de_AT.utf8"
LC_MESSAGES="de_AT.utf8"
LC_PAPER="de_AT.utf8"
LC_NAME="de_AT.utf8"
LC_ADDRESS="de_AT.utf8"
LC_TELEPHONE="de_AT.utf8"
LC_MEASUREMENT="de_AT.utf8"
LC_IDENTIFICATION="de_AT.utf8"
LC_ALL=

Das Gnome-Terminal habe ich ebenfalls standardmäßig auf UTF-8, aber jetzt kommt's. Schalte ich im Gnome-Terminal auf ISO-8859-15, werden in der MySQL-Tabelle die Umlaute plötzlich richtig dargestellt. Das ist doch meiner Meinung nach nicht logisch. Es müsste doch genau umgekehrt sein, oder täusche ich mich? Was kann da los sein?
Das Eintippen von Umlauten in der Bash (Gnome-Terminal) funktioniert jedenfalls mit beiden Einstellungen.

Markus

--
  1. echo $begrüßung;

    Die Daten werden in der JSP-Seite richtig dargestellt. Die Daten werden im MySQL Query Browser ebenfalls richtig dargestellt. Rufe ich die Daten aber über die Bash (Gnome-Terminal) ab, also wenn ich mit mysql schema -u user -p und mit einem select * die Daten aus der DB abrufe, habe ich anstatt der Umlaute nur bemalte Fragezeichen.

    Welche Kodierung hast du mit dem MySQL-Server für diese Verbindung ausgehandelt? Wenn keine, wie ist die Default-Einstellung? show variables like 'char%'; Aussschlaggebend für das Ergebnis ist character_set_results. Für das Senden zum Server sind character_set_connection und character_set_client zuständig.

    Das Gnome-Terminal habe ich ebenfalls standardmäßig auf UTF-8, aber jetzt kommt's. Schalte ich im Gnome-Terminal auf ISO-8859-15, werden in der MySQL-Tabelle die Umlaute plötzlich richtig dargestellt.

    Nicht "in der Tabelle" sondern das was dir der Server liefert. Er liefert stets das Ergebnis in der explizit vereinbarten oder voreingestellten Kodierung. Wenn die gespeicherten Daten in anderer Kodierung vorliegen (Feldkodierungseinstellung) dann kodiert es das in die für die Verbindung geforderte Kodierung um.

    (ISO-8859-15 (und auch ISO-8859-1) wäre nicht ganz richtig, MySQLs Latin1 entspricht Win-1252.)

    Das Eintippen von Umlauten in der Bash (Gnome-Terminal) funktioniert jedenfalls mit beiden Einstellungen.

    Wetten, dass bei einem Daten schreibenden Statement, dem du UTF-8-kodierte Daten mitgibst, Müll in dem Feld landet (genauer gesagt doppelt UTF-8-Kodiertes - zu kontrollieren mit einem, der das Thema Kodierung richtig umsetzt, phpMyAdmin beispielsweise)?

    echo "$verabschiedung $name";

    1. Hallo,

      ich glaube, dass es jetzt richtig funktioniert. Da du den Server erwähnt hast, ging ich nochmal in die my.cnf und fügte folgende Zeilen hinzu:

      collation_server=utf8_unicode_ci
      character_set_server=utf8

      #to enforce using of utf8 encoding in db
      skip-character-set-client-handshake

      Nun werden die Daten immer und überall richtig kodiert dargestellt. Allerdings muss ich zugeben, dass ich glaube mit "skip-character-set-client-handshake" das Problem nur unter den Teppich gekehrt zu haben.
      Leider habe ich nur sehr wenig Zeit, um mich mit dem Problem tiefgründiger zu befassen, aber irgendwann mal werde ich schon dazu kommen.

      Markus

      --
      1. echo $begrüßung;

        ich glaube, dass es jetzt richtig funktioniert. Da du den Server erwähnt hast, ging ich nochmal in die my.cnf und fügte folgende Zeilen hinzu:
        collation_server=utf8_unicode_ci
        character_set_server=utf8

        Damit ist nun UTF-8 die Voreinstellung für alles was keine explizite Angabe macht.

        #to enforce using of utf8 encoding in db
        skip-character-set-client-handshake
        Nun werden die Daten immer und überall richtig kodiert dargestellt. Allerdings muss ich zugeben, dass ich glaube mit "skip-character-set-client-handshake" das Problem nur unter den Teppich gekehrt zu haben.

        Wenn nun doch noch ein Client explizit für sich was aushandeln will, ignoriert der Server das. Wenn du diese Einstellung nötig hast, damit UTF-8 richtig behandelt wird, dann versucht irgendwo noch ein Client was anderes auszuhandeln. Suche in dem Fall lieber die Stelle in der API des Client, an der die zu verwendende Kodierung eingestellt werden kann.

        echo "$verabschiedung $name";

        1. Hallo,

          Wenn nun doch noch ein Client explizit für sich was aushandeln will, ignoriert der Server das. Wenn du diese Einstellung nötig hast, damit UTF-8 richtig behandelt wird, dann versucht irgendwo noch ein Client was anderes auszuhandeln. Suche in dem Fall lieber die Stelle in der API des Client, an der die zu verwendende Kodierung eingestellt werden kann.

          Ok, wenn ich in der my.cnf zusätzlich diese Einstellung vornehme:

          [client]
          default-character-set=utf8

          kann ich die handshake-Zeile weglassen, falls sie vorher etwas bewirkt hat.

          Aussschlaggebend für das Ergebnis ist character_set_results. Für das Senden zum Server sind character_set_connection und character_set_client zuständig.

          Ich habe versucht, diese Werte zu setzen, aber nur folgende Werte funktionieren:

          default-character-set=utf8
          default-collation=utf8_general_ci
          collation_server=utf8_unicode_ci
          character_set_server=utf8
          character_set_client=utf8

          Füge ich die zwei o.g. nicht eingesetzten Werte ein, kann der MySQL-Daemon nicht mehr starten ([FAIL] und ohne Meldung im Logfile).

          Selbst bei all diesen gesetzten Werten muss ich in dem Java-Servlet

            
             request.setCharacterEncoding("UTF-8");  
             response.setCharacterEncoding("UTF-8");  
          
          

          stehen lassen. Sonst würden die Umlaute zerstört werden. Allerdings kann ich einen Effekt beobachten. Sind die Umlaute zerstört, benötigen sie ungewöhnlich viele Leerzeichen nach dne eigentlichen Zeichen. Ist das ein Indiz dafür, dass es ev. doppelt kodiert wurde?

          Jetzt liegt es wohl nur noch am JDBC-Treiber, denn ich glaube, die DB funktioniert jetzt mit UTF8 problemlos. Ich kann auch durch den MySQl-Client per Insert Umlaute über die Bash einfügen.

          Markus

          --
          1. echo $begrüßung;

            Wenn nun doch noch ein Client explizit für sich was aushandeln will, ignoriert der Server das. Wenn du diese Einstellung nötig hast, damit UTF-8 richtig behandelt wird, dann versucht irgendwo noch ein Client was anderes auszuhandeln. Suche in dem Fall lieber die Stelle in der API des Client, an der die zu verwendende Kodierung eingestellt werden kann.

            Ok, wenn ich in der my.cnf zusätzlich diese Einstellung vornehme:
            [client]
            default-character-set=utf8

            Du suchst, so scheint es mir, immer noch auf dem Server, nicht beim Client. Wenn die Anwendung auch mal mit MySQL-Servern laufen soll, auf die du keinen Einfluss hast, musst du mit dem Client nach dem Verbindungsaufbau die zu verwendende Kodierung aushandeln können.

            Aussschlaggebend für das Ergebnis ist character_set_results. Für das Senden zum Server sind character_set_connection und character_set_client zuständig.
            Ich habe versucht, diese Werte zu setzen, aber nur folgende Werte funktionieren:

            Die lassen sich nicht konfigurieren, nur pro Verbindung setzen.

            default-character-set=utf8
            default-collation=utf8_general_ci

            Die beiden sind deprecated. Die beiden folgenden sind die zu verwendenden.

            collation_server=utf8_unicode_ci
            character_set_server=utf8

            Und sie wirken auch als Default-Werte für Client-Verbindungen.

            Selbst bei all diesen gesetzten Werten muss ich in dem Java-Servlet
               request.setCharacterEncoding("UTF-8");
               response.setCharacterEncoding("UTF-8");
            stehen lassen. Sonst würden die Umlaute zerstört werden.

            In welche Richtung? Von und zum Browser, nehm ich an. Die Kommunikation mit der Datenbank sollte davon nicht betroffen sein. (Vorausgesetzt, du reichst nicht "falsch" kodierte Daten weiter.)

            Allerdings kann ich einen Effekt beobachten. Sind die Umlaute zerstört, benötigen sie ungewöhnlich viele Leerzeichen nach dne eigentlichen Zeichen. Ist das ein Indiz dafür, dass es ev. doppelt kodiert wurde?

            Eher, dass sie als ISO-8859-1 kodiert sind. Schau dir den Quelltext im Browser an, stell dabei die Kodierung der Quelltextansicht auf ISO-8859-1 (geht zumindest im Firefox), dann siehst du ja pro Byte ein Zeichen und kannst auch die typischen UTF-8-Sequenzen sehen - oder auch nicht, wenn du kein UTF-8 hast.

            echo "$verabschiedung $name";

            1. Hallo,

              Du suchst, so scheint es mir, immer noch auf dem Server, nicht beim Client. Wenn die Anwendung auch mal mit MySQL-Servern laufen soll, auf die du keinen Einfluss hast, musst du mit dem Client nach dem Verbindungsaufbau die zu verwendende Kodierung aushandeln können.

              Mein MySQL-Client ist einfach das MySQL-Konsolenprogramm. Ich habe zwar auch den MySQL Query Browser, aber in beiden Fällen kann man nirgendwo Kodierungen einstellen.

              default-character-set=utf8
              default-collation=utf8_general_ci

              Die beiden sind deprecated. Die beiden folgenden sind die zu verwendenden.

              collation_server=utf8_unicode_ci
              character_set_server=utf8

              Gut. Ich habe sie entfernt.

              Allerdings kann ich einen Effekt beobachten. Sind die Umlaute zerstört, benötigen sie ungewöhnlich viele Leerzeichen nach dne eigentlichen Zeichen. Ist das ein Indiz dafür, dass es ev. doppelt kodiert wurde?

              Eher, dass sie als ISO-8859-1 kodiert sind. Schau dir den Quelltext im Browser an, stell dabei die Kodierung der Quelltextansicht auf ISO-8859-1 (geht zumindest im Firefox), dann siehst du ja pro Byte ein Zeichen und kannst auch die typischen UTF-8-Sequenzen sehen - oder auch nicht, wenn du kein UTF-8 hast.

              Also wenn ich im Browser auf iso8859-1 stelle, erkenne ich die UTF-8-Sequenzen.

              Markus

              --
              1. echo $begrüßung;

                Du suchst, so scheint es mir, immer noch auf dem Server, nicht beim Client. Wenn die Anwendung auch mal mit MySQL-Servern laufen soll, auf die du keinen Einfluss hast, musst du mit dem Client nach dem Verbindungsaufbau die zu verwendende Kodierung aushandeln können.
                Mein MySQL-Client ist einfach das MySQL-Konsolenprogramm. Ich habe zwar auch den MySQL Query Browser, aber in beiden Fällen kann man nirgendwo Kodierungen einstellen.

                Das sind ja nur Hilfsprogramme, mit denen du die Daten bearbeitest. Ich meinte eher in Richtung Java (JDBC?) gehend, denn da ist ja dein Hauptproblem angesiedelt.

                Allerdings kann ich einen Effekt beobachten. Sind die Umlaute zerstört, benötigen sie ungewöhnlich viele Leerzeichen nach dne eigentlichen Zeichen. Ist das ein Indiz dafür, dass es ev. doppelt kodiert wurde?
                Eher, dass sie als ISO-8859-1 kodiert sind. Schau dir den Quelltext im Browser an, stell dabei die Kodierung der Quelltextansicht auf ISO-8859-1 (geht zumindest im Firefox), dann siehst du ja pro Byte ein Zeichen und kannst auch die typischen UTF-8-Sequenzen sehen - oder auch nicht, wenn du kein UTF-8 hast.
                Also wenn ich im Browser auf iso8859-1 stelle, erkenne ich die UTF-8-Sequenzen.

                Wenn du auf ISO-8859-1 gestellt die UTF-8-Sequenzen zu sehen glaubst, und auf UTF-8 gestellt "Zerstörtes" (wie sieht das konkret aus?), dann sieht mir das nicht nach gültigen UTF-8-Sequenzen aus. Zumal nach deiner Beschreibung nach einem UTF-8-Zeichen noch nachfolgede Zeichen verschwinden, was darauf schließen lässt, dass die UTF-8-Sequenz ungültig ist und nachfolgende Zeichen von UTF-8-Dekodierer gefressen werden, weil der noch mehr Bytes zur Sequenz zugehörig erwartet.

                Öffne die Seite mal im Browser, speichere sie als Datei (oder wget von der Linux-Konsole), lass sie durch hexdump -C laufen und such dir eine defekte Sequenz, poste das Ergebnis bitte hier oder versuch es selbst zu analysieren.

                echo "$verabschiedung $name";

                1. Hallo,

                  Wenn du auf ISO-8859-1 gestellt die UTF-8-Sequenzen zu sehen glaubst, und auf UTF-8 gestellt "Zerstörtes" (wie sieht das konkret aus?), dann sieht mir das nicht nach gültigen UTF-8-Sequenzen aus. Zumal nach deiner Beschreibung nach einem UTF-8-Zeichen noch nachfolgede Zeichen verschwinden, was darauf schließen lässt, dass die UTF-8-Sequenz ungültig ist und nachfolgende Zeichen von UTF-8-Dekodierer gefressen werden, weil der noch mehr Bytes zur Sequenz zugehörig erwartet.

                  Nein. Die Kodierung sieht überall richtig aus. Sowohl die JSP-Seite im Browser, als auch die Ausgabe mit mysql in der Konsole, sowie auch die Ausgabe mit dem MySql Query Browser.
                  Schalte ich im Browser auf iso8859-1, wird diese natürlich falsch angezeigt.
                  Schalte ich wieder auf UTF-8, ist alles wieder in Ordnung.

                  Ich muss nur in Java serverseitig folgendes schreiben:

                    
                    
                     request.setCharacterEncoding("UTF-8");  
                     response.setCharacterEncoding("UTF-8");  
                    
                  
                  

                  Liegt es einfach nur daran, dass in POST einfach nichts UTF-8-kodiert wird, und ich es deswegen immer so machen muss? Allerdings muss man das natürlich irgendwo umstellen können.

                  Markus

                  --
                  1. echo $begrüßung;

                    Ich muss nur in Java serverseitig folgendes schreiben:
                       request.setCharacterEncoding("UTF-8");
                       response.setCharacterEncoding("UTF-8");
                    Liegt es einfach nur daran, dass in POST einfach nichts UTF-8-kodiert wird, und ich es deswegen immer so machen muss? Allerdings muss man das natürlich irgendwo umstellen können.

                    Ich vermute eher, dass der POST die Daten UTF-8-kodiert sendet, das Java aber die request-Einstellung braucht, um diesen Datenstrom richtig zu interpretieren. Wird wohl eine andere Default-Einstellung haben. Und die respose-Einstellung braucht es, damit es die Daten auch UTF-8-kodiert ausliefert. Wird wohl eine andere Default-Einstellung haben. Der Browser bekommt hoffentlich eine charset-Angabe im Content-Type-HTTP-Header oder als Ersatz ein Meta-Element. Wenn du gut bist, hast du dem Formular auch noch ein accept-charset-Attribut spendiert.

                    echo "$verabschiedung $name";