Gunther: Character Encoding - UTF-8 - multibyte String-Funktionen

Hallo Selfgemeinde!

Ich habe da wohl (noch) ein gewisses Verständnisproblem und wäre euch sehr dankbar, wenn ihr mir bei diesem auf die Sprünge helfen könntet.

Aktuell arbeite ich u.a. an einem Script, das eingehende Requests analysiert. Dazu verwende ich einige der $_SERVER Variablen und "behandele" diese auch mit diversen String-Funktionen wie bspw. stripos() und strlen(). Ferner kommen auch einige der PCRE-Funktionen zum Einsatz.

Da ich eigentlich gerne generell und durchgehend UTF-8 codiert arbeiten wollte, müsste ich ja (u.a.) bei den String-Funktionen die jeweiligen mb_xxx Funktionen verwenden und anstelle der PCRE-Funktionen die mb_ereg_xxx Funktionen.
Soweit richtig?

Mein erstes Problem besteht jetzt schonmal darin, dass mir nicht klar ist, in welcher Kodierung denn nun bspw. ein Request (URI + Query String) auf dem Server überhaupt ankommt, wovon das abhängig ist und wie ich das feststellen kann?

Ein Aufruf von mb_internal_encoding() gibt mir ISO-8859-1 als interne Kodierung aus.
Diese müsste ich, wenn ich durchgehend mit UTF-8 arbeiten will, dann entsprechend umstellen?

Stellt sich für mich aber gleich die nächste Frage: Lohnt sich der "Aufwand/ Aufstand" (bei PHP < 6), oder wäre es ggf. sinnvoller/ einfacher, vorhandene Strings in UTF-8 in ISO-8859-1 umzukodieren, diese intern dann "normal" zu verarbeiten und vor einer Ausgabe ggf. wieder in UTF-8 zu kodieren?

Wenn ja, wie stelle ich das so an, dass ich keine Datenverluste/ -verfremdungen erleide und worauf muss ich ggf. achten?

Für Auf- & Erklärungen jeglicher Art meinen besten Dank im Voraus.

Gruß Gunther

  1. Hi!

    Da ich eigentlich gerne generell und durchgehend UTF-8 codiert arbeiten wollte, müsste ich ja (u.a.) bei den String-Funktionen die jeweiligen mb_xxx Funktionen verwenden und anstelle der PCRE-Funktionen die mb_ereg_xxx Funktionen. Soweit richtig?

    Die erste Frage ist, ob du tatsächlich Stringfunktionen in PHP benötigst. Mitunter lassen sich die Anwendungen so gestalten, dass PHP nur Durchreicher ist, womit das Problem der nur eingeschränkt vorhandenen Multibyte-Kodierungen erst einmal keins mehr ist.

    Anscheinend ist dies aber keine Alternative für dich, so dass es die vorhandenen Möglichkeiten auszuloten gilt. Die ereg-Funktionen sind kein Ersatz für PCRE. Das ist aber auch nicht nötig, denn PCRE kann auch in einem UTF-8-Modus arbeiten. Dazu gibt es einen Modifier. Ansonsten gibt es einige wenige Funktionen, die auch auf andere Kodierungen Rücksicht nehmen können, wenn man es ihnen per Zusatzparameter mitteilt (htmlentities() beispielsweise, aber diese Funktion will man nicht verwenden, wenn man UTF-8 haben kann. htmlspecialchars() benötigt für ISO-8859-x und UTF-8 keine charset-Angabe, der Default-Modus ISO-8859-1 ist für diese Kodierungen problemlos und unterschiedslos verwendbar).

    Mein erstes Problem besteht jetzt schonmal darin, dass mir nicht klar ist, in welcher Kodierung denn nun bspw. ein Request (URI + Query String) auf dem Server überhaupt ankommt, wovon das abhängig ist und wie ich das feststellen kann?

    Feststellen, in welcher Kodierung Daten vorliegen, kann man prinzipbedingt nicht. Man kann es höchstens erraten, solange nur wenige Kodierungen eine Rolle spielen. Man kann eine ankommende Bytefolge darauf testen, ob sie die UTF-8-Spielregeln einhält, sprich: ob die Sequenzen der Bytes größer als 0x7f regelgerecht sind. Aber da jegliche Bytefolge auch gültiges ISO-8859-1 sein kann, ist eine eindeutige Erkennung schon daran zum Scheitern verurteilt. Lediglich am interpretierten Inhalt kann man erkennen, ob etwas Sinnvolles rauskommt oder nicht. - Alle Funktionen, die dir eine Zeichenkodierungserkennung versprechen, kochen auch nur mit Wasser und liefern nicht in jedem Fall ein fehlerfreies Ergebnis.

    Es ist auch nicht möglich, einem Client die zu verwendende Kodierung vorzuschreiben. Das geht schon deshalb nicht, weil ein Client auch ohne den Server zu kennen, eine Anforderung absenden können muss. Weiterhin gibt es keine Regel, in welcher Kodierung er zu senden hat und auch keinen Header oder dergleichen, in dem der Client dem Server die verwendete Kodierung mitteilt.

    Dies stellt aber in der Praxis nicht unbedingt ein Problem dar. Initiale Requests kommen oft mit den ASCII-Zeichen aus. Formulardaten werden vom Client in der Regel in der Kodierung abgesendet, die die das Formular enthaltene Seite deklariert hat. Das accept-charset-Attribut eines Formulars trifft hingegen in der Praxis nicht selten auf taube Ohren.

    Kommen die Daten in der URL an, so besteht die Möglichkeit, dass sie ein Mensch eingetippt hat und der Request ein initialier ist. Der Browser verwendet dann seine Default-Einstellung, ohne das dem Server mitteilen zu können. Du kannst nur versuchen, ob die Daten gültiges UTF-8 sind, und wenn nicht, sie von ISO-8859-1 nach UTF-8 umzukodieren und dann zu versuchen, ein Ergebnis für diesen Request zu finden.

    Für die UTF-8-"Erkennung" gibt es einige Funktionen in den Userkommentaren bei den üblichen verdächtigen Funktionen (suche bei utf8_decode, utf8_encode, mb_detect_encoding). Sie können aber lediglich die formale Richtigkeit von UTF-8-Sequenzen ermitteln, nicht aber, ob tatsächlich eine UTF-8-Kodierung vom Autor beabsichtig war oder ob die Bytesequenz nur zufällig gültiges UTF-8 ergibt.

    Übrigens, den Request allein im Hinblick auf UTF-8-Fähigkeit zu beachten, ist oft noch nicht ausreichend. Auch das Speichern der Daten verlangt eine Betrachtung und damit die dafür verwendeten Medien (Dateien oder Datenbanken). Du kannst ja mal aufzählen, welche Stellen dir im Webumfeld einfallen, an denen eine Zeichenkodierung eine Rolle spielt und eingestellt werden kann. Wir™ schauen dann mal, ob du alle kennst. (Das Nachschlagen im hiesigen Archiv ist nicht nur nicht verboten, sondern ausdrücklich erwünscht :-)

    Ein Aufruf von mb_internal_encoding() gibt mir ISO-8859-1 als interne Kodierung aus.
    Diese müsste ich, wenn ich durchgehend mit UTF-8 arbeiten will, dann entsprechend umstellen?

    Diese Frage konnte das PHP-Handbuch nicht klären?

    Stellt sich für mich aber gleich die nächste Frage: Lohnt sich der "Aufwand/ Aufstand" (bei PHP < 6), oder wäre es ggf. sinnvoller/ einfacher, vorhandene Strings in UTF-8 in ISO-8859-1 umzukodieren, diese intern dann "normal" zu verarbeiten und vor einer Ausgabe ggf. wieder in UTF-8 zu kodieren?
    Wenn ja, wie stelle ich das so an, dass ich keine Datenverluste/ -verfremdungen erleide und worauf muss ich ggf. achten?

    Diese Anforderung kann mit dem Ansinnen, alles nach ISO-8859-1 umzukodieren, nicht erfüllt werden. Mit der ISO-8859-Familie sind jeweils nur 256 Zeichen darstellbar. Mit Unicode hingegen können derzeit 1.114.112 Zeichen repräsentiert werden. Eine verlustfreie Umkodierung kann nur dann stattfinden, wenn nur die 256 ISO-8859-x-Zeichen verwendet werden.

    Ob sich der Aufwand lohnt, musst du selbst entscheiden, denn nur du kennst die Anforderungen deines Projekts.

    Lo!

    1. Hi dedlfix!

      Vorab schon mal vielen Dank für deine ausführliche & hilfreiche Antwort.
      Ich hatte ja schon gehofft, dass der "Fachmann" hier antwortet. ;-)

      Die ereg-Funktionen sind kein Ersatz für PCRE. Das ist aber auch nicht nötig, denn PCRE kann auch in einem UTF-8-Modus arbeiten. Dazu gibt es einen Modifier.

      OK, gefunden und verstanden.

      Mein erstes Problem besteht jetzt schonmal darin, dass mir nicht klar ist, in welcher Kodierung denn nun bspw. ein Request (URI + Query String) auf dem Server überhaupt ankommt, wovon das abhängig ist und wie ich das feststellen kann?

      Feststellen, in welcher Kodierung Daten vorliegen, kann man prinzipbedingt nicht. Man kann es höchstens erraten, solange nur wenige Kodierungen eine Rolle spielen. Man kann eine ankommende Bytefolge darauf testen, ob sie die UTF-8-Spielregeln einhält, sprich: ob die Sequenzen der Bytes größer als 0x7f regelgerecht sind. Aber da jegliche Bytefolge auch gültiges ISO-8859-1 sein kann, ist eine eindeutige Erkennung schon daran zum Scheitern verurteilt. Lediglich am interpretierten Inhalt kann man erkennen, ob etwas Sinnvolles rauskommt oder nicht.

      Die Problematik leuchtet mir ein, wenngleich das eine praktische Umsetzung ja irgendwie nicht gerade erleichtert.

      Alle Funktionen, die dir eine Zeichenkodierungserkennung versprechen, kochen auch nur mit Wasser und liefern nicht in jedem Fall ein fehlerfreies Ergebnis.

      Überhaupt scheint das ein "schwieriges" Kapitel zu sein. In den diversen User-Kommentaren zu einigen der PHP-Funktionen liest man immer das Wort "buggy" ...!

      Es ist auch nicht möglich, einem Client die zu verwendende Kodierung vorzuschreiben. Das geht schon deshalb nicht, weil ein Client auch ohne den Server zu kennen, eine Anforderung absenden können muss. Weiterhin gibt es keine Regel, in welcher Kodierung er zu senden hat und auch keinen Header oder dergleichen, in dem der Client dem Server die verwendete Kodierung mitteilt.

      Ja, klasse. Je mehr ich mich mit dem Thema (zwangsweise) befasse, umso erstaunter bin ich, dass das Surfen im Web bisher so "unproblematisch" funktioniert hat.

      Dies stellt aber in der Praxis nicht unbedingt ein Problem dar. Initiale Requests kommen oft mit den ASCII-Zeichen aus. Formulardaten werden vom Client in der Regel in der Kodierung abgesendet, die die das Formular enthaltene Seite deklariert hat. Das accept-charset-Attribut eines Formulars trifft hingegen in der Praxis nicht selten auf taube Ohren.

      Kommen die Daten in der URL an, so besteht die Möglichkeit, dass sie ein Mensch eingetippt hat und der Request ein initialier ist. Der Browser verwendet dann seine Default-Einstellung, ohne das dem Server mitteilen zu können. Du kannst nur versuchen, ob die Daten gültiges UTF-8 sind, und wenn nicht, sie von ISO-8859-1 nach UTF-8 umzukodieren und dann zu versuchen, ein Ergebnis für diesen Request zu finden.

      Hier fangen dann meine Probleme an. Denn welche Funktion muss ich denn nun bspw. verwenden: stripos() (für ISO-8859-x) oder mb_stripos() (für UTF-8)?

      Und es fängt ja schon viel früher an. So ist mir u.a. auch noch nicht klar, wann, wie und warum welcher Browser in der Adresszeile "Klartext" (auch mit Umlauten etc.) und wann URL kodierten Text (also mit %xx) anzeigt? Mein FF 3.5 zeigt bspw. bei einem Klick auf einen Link den "Klartext" in der Adresszeile (in der Statuszeile ebenso) an und bei manueller Eingabe den kodierten Text.

      Lasse ich mir dann bspw. per Script die $_SERVER Variablen anzeigen, dann sind $_SERVER[REQUEST_URI] und $_SERVER[QUERY_STRING] immer kodiert, während $_SERVER[PATH_INFO] im Klartext angezeigt wird.
      Beispiel:
      $_SERVER[REQUEST_URI]: /Umlaute:%20%C3%A4%C3%84%C3%B6%C3%96%C3%BC%C3%9C%C3%9F%20Preis:%20%E2%82%AC%2030,-
      $_SERVER[PATH_INFO]: /Umlaute: äÄöÖüÜß Preis: € 30,-

      Für die UTF-8-"Erkennung" gibt es einige Funktionen in den Userkommentaren bei den üblichen verdächtigen Funktionen (suche bei utf8_decode, utf8_encode, mb_detect_encoding). Sie können aber lediglich die formale Richtigkeit von UTF-8-Sequenzen ermitteln, nicht aber, ob tatsächlich eine UTF-8-Kodierung vom Autor beabsichtig war oder ob die Bytesequenz nur zufällig gültiges UTF-8 ergibt.

      Ja, danke! Mit den ganzen Funktionen komme ich auch noch nicht wirklich zurecht. Solange ich die_nicht_verwende, funktioniert bisher alles (augenscheinlich) wie es soll. Sobald ich mit einer der Funktionen anfange "rumzudoktern", klappt nichts mehr!

      Übrigens, den Request allein im Hinblick auf UTF-8-Fähigkeit zu beachten, ist oft noch nicht ausreichend. Auch das Speichern der Daten verlangt eine Betrachtung und damit die dafür verwendeten Medien (Dateien oder Datenbanken). Du kannst ja mal aufzählen, welche Stellen dir im Webumfeld einfallen, an denen eine Zeichenkodierung eine Rolle spielt und eingestellt werden kann. Wir™ schauen dann mal, ob du alle kennst. (Das Nachschlagen im hiesigen Archiv ist nicht nur nicht verboten, sondern ausdrücklich erwünscht :-)

      Ja wie jetzt? Gibt es denn noch mehr, als die, die du in deinem Artikel genannt hast? Dann wäre der ja unvollständig. ;-)

      Ein Aufruf von mb_internal_encoding() gibt mir ISO-8859-1 als interne Kodierung aus.
      Diese müsste ich, wenn ich durchgehend mit UTF-8 arbeiten will, dann entsprechend umstellen?

      Diese Frage konnte das PHP-Handbuch nicht klären?

      Nein, nicht wirklich. Außerdem müsste ich doch dann nach meinem bisherigen Verständnis sicherstellen, dass meine zu verarbeitenden Strings und Arrays auch wirklich alle definitiv UTF-8 kodiert sind, oder nicht? Ansonsten würde ich ja wiederum "falsche" Ergebnisse bekommen?

      Stellt sich für mich aber gleich die nächste Frage: Lohnt sich der "Aufwand/ Aufstand" (bei PHP < 6), oder wäre es ggf. sinnvoller/ einfacher, vorhandene Strings in UTF-8 in ISO-8859-1 umzukodieren, diese intern dann "normal" zu verarbeiten und vor einer Ausgabe ggf. wieder in UTF-8 zu kodieren?
      Wenn ja, wie stelle ich das so an, dass ich keine Datenverluste/ -verfremdungen erleide und worauf muss ich ggf. achten?

      Diese Anforderung kann mit dem Ansinnen, alles nach ISO-8859-1 umzukodieren, nicht erfüllt werden. Mit der ISO-8859-Familie sind jeweils nur 256 Zeichen darstellbar. Mit Unicode hingegen können derzeit 1.114.112 Zeichen repräsentiert werden. Eine verlustfreie Umkodierung kann nur dann stattfinden, wenn nur die 256 ISO-8859-x-Zeichen verwendet werden.

      Du hast natürlich Recht, dass ein Umkodierung nur von einem Zeichensatz A auf einen Zeichensatz B Sinn macht, wenn B mind. auch alle Zeichen von A beinhaltet.

      Ob sich der Aufwand lohnt, musst du selbst entscheiden, denn nur du kennst die Anforderungen deines Projekts.

      Der Aufwand ist mir eigentlich egal - zuverlässig funktionieren soll es. ;-)
      Hierzu bräuchte ich wirklich mal einen guten Rat, welches die sinnvollste Vorgehensweise ist.

      Anforderung ist im Prinzip "nur", dass aufgrund meiner "sprechenden" URLs eben auch Umlaute korrekt gehandhabt werden können sollen (tolles Satzkonstrukt). Dabei war ich wohl fälschlicherweise davon ausgegangen, bzw. dem Trugschluss erlegen, dass wenn ich eh alles andere UTF-8 kodiere, es dann auch "einfacher" wäre, scriptintern gleich mit UTF-8 kodierten Strings und Arrays zu arbeiten. Dem scheint aber wohl momentan zumindest noch nicht so zu sein.

      (leicht verwirrter)
      Gruß Gunther

      1. Hi,

        Und es fängt ja schon viel früher an. So ist mir u.a. auch noch nicht klar, wann, wie und warum welcher Browser in der Adresszeile "Klartext" (auch mit Umlauten etc.) und wann URL kodierten Text (also mit %xx) anzeigt? Mein FF 3.5 zeigt bspw. bei einem Klick auf einen Link den "Klartext" in der Adresszeile (in der Statuszeile ebenso) an und bei manueller Eingabe den kodierten Text.

        Das kann dir aber egal sein, so lange der Browser den Request vernünftig kodiert sendet.

        Lasse ich mir dann bspw. per Script die $_SERVER Variablen anzeigen, dann sind $_SERVER[REQUEST_URI] und $_SERVER[QUERY_STRING] immer kodiert, während $_SERVER[PATH_INFO] im Klartext angezeigt wird.
        Beispiel:
        $_SERVER[REQUEST_URI]: /Umlaute:%20%C3%A4%C3%84%C3%B6%C3%96%C3%BC%C3%9C%C3%9F%20Preis:%20%E2%82%AC%2030,-
        $_SERVER[PATH_INFO]: /Umlaute: äÄöÖüÜß Preis: € 30,-

        Das hat aber nichts mit UTF-8 vs. ISO-8859-1 zu tun, sondern ist lediglich URL-encoded vs. "plain text".

        MfG ChrisB

        --
        Light travels faster than sound - that's why most people appear bright until you hear them speak.
        1. Hi,

          Das kann dir aber egal sein, so lange der Browser den Request vernünftig kodiert sendet.

          Nein, kann es imho eben leider nicht.

          Lasse ich mir dann bspw. per Script die $_SERVER Variablen anzeigen, dann sind $_SERVER[REQUEST_URI] und $_SERVER[QUERY_STRING] immer kodiert, während $_SERVER[PATH_INFO] im Klartext angezeigt wird.
          Beispiel:
          $_SERVER[REQUEST_URI]: /Umlaute:%20%C3%A4%C3%84%C3%B6%C3%96%C3%BC%C3%9C%C3%9F%20Preis:%20%E2%82%AC%2030,-
          $_SERVER[PATH_INFO]: /Umlaute: äÄöÖüÜß Preis: € 30,-

          Das hat aber nichts mit UTF-8 vs. ISO-8859-1 zu tun, sondern ist lediglich URL-encoded vs. "plain text".

          Ja, schon richtig. Nachfolgend ein hoffentlich etwas besseres Beispiel zur Verdeutlichung meines Problems (zumindest erstmal eins davon).

          Fall 1:
          Habe ich auf einer meiner korrekt als UTF-8 kodierten Seiten einen Link à la .../€?q=€
          dann wird dieser_komplett_UTF-8 kodiert übertragen/ gesendet.
          Ergebnis (URL kodiert) REQUEST_URI = /%E2%82%AC?q=%E2%82%AC

          Fall 2:
          Tippe ich genau denselben Request aber manuell in die Adresszeile des Browsers, sieht das Ergebnis (URL kodiert) so aus REQUEST_URI = /%E2%82%AC?q=%80

          Der "Wahnwitz" daran ist, dass der Pfadteil nachwievor UTF-8 kodiert ist, aber der Query String dann nicht mehr, sondern wie in diesem Beispiel dann auf einmal in Windows-1252.

          Das Problem ist dann ja u.a. auch, dass eine Erkennung der Kodierung per Script defacto nicht möglich ist, da '%80' in vielen Kodierungen "gültig" ist. Und wie dedlfix ja schon angemerkt hat, würde hier nur eine "Sinn-Erkennung" funktionieren, was per Script imho (fast) unmöglich ist.

          Oder hast du eine Idee/ einen Vorschlag zur Lösung des Problems?

          BTW: Ich habe aufgrund dieser Probleme nochmal generell über Sonderzeichen (spez. Umlaute) in der URL nachgedacht. Ist es unter dem Gesichtspunkt der Usability nicht sehr "unfreundlich", denn alle User, die keine dt. Tastatur (bzw. dt. Tastaturlayout) haben, tun sich ja dann sehr schwer mit einer manuellen Eingabe, oder? Selbst wenn man die alternative Schreibweise (also bspw. "ae" anstelle von "ä") ebenfalls akzeptieren würde (z.B. mit dem rel="canonical" Attribut), müsste der jeweilige User das ja auch erstmal wissen. Oder kann man durchaus davon ausgehen, dass eh 99,xx% aller Besucher über SuMa- und sonstige Links kommen?

          Gruß Gunther

          1. Hi!

            Habe ich auf einer meiner korrekt als UTF-8 kodierten Seiten einen Link à la .../€?q=€ [...]
            Tippe ich genau denselben Request aber manuell in die Adresszeile des Browsers, sieht das Ergebnis (URL kodiert) so aus REQUEST_URI = /%E2%82%AC?q=%80

            Schöner Scheibenkleister. Das ist mir in der Form noch nicht begegnet, aber nachvollziehbar. Sonderzeichen in URLs sind einfach Mist.

            Das Problem ist dann ja u.a. auch, dass eine Erkennung der Kodierung per Script defacto nicht möglich ist, da '%80' in vielen Kodierungen "gültig" ist.

            80h ist kein gültiges UTF-8. Eine mit 10 anfangende Bitfolge darf nicht als erstes Byte einer UTF-8-Sequenz stehen.

            Oder hast du eine Idee/ einen Vorschlag zur Lösung des Problems?

            Du kannst in dem Fall nur die Bestandteile der URL einzeln betrachtend verarbeiten, also Pfad getrennt vom Querystring.

            BTW: Ich habe aufgrund dieser Probleme nochmal generell über Sonderzeichen (spez. Umlaute) in der URL nachgedacht. Ist es unter dem Gesichtspunkt der Usability nicht sehr "unfreundlich", denn alle User, die keine dt. Tastatur (bzw. dt. Tastaturlayout) haben, tun sich ja dann sehr schwer mit einer manuellen Eingabe, oder? Selbst wenn man die alternative Schreibweise (also bspw. "ae" anstelle von "ä") ebenfalls akzeptieren würde (z.B. mit dem rel="canonical" Attribut), müsste der jeweilige User das ja auch erstmal wissen. Oder kann man durchaus davon ausgehen, dass eh 99,xx% aller Besucher über SuMa- und sonstige Links kommen?

            Das ist ein weiteres Problem, nicht nur, wenn du auch nicht Deutschsprechende als Zielgruppe im Auge hast sondern auch Touristen an fremden Tastaturen. Und das kannst du serverseitig gar nicht lösen (außer eben in der Beschränkung auf ASCII).

            Lo!

          2. Hi,

            BTW: Ich habe aufgrund dieser Probleme nochmal generell über Sonderzeichen (spez. Umlaute) in der URL nachgedacht. Ist es unter dem Gesichtspunkt der Usability nicht sehr "unfreundlich", denn alle User, die keine dt. Tastatur (bzw. dt. Tastaturlayout) haben, tun sich ja dann sehr schwer mit einer manuellen Eingabe, oder?

            Da verweise ich noch mal auf meine Gedanken zum Thema Sonderzeichen in URLs von neulich;
            dass man den "Kulturkreis" der potentiellen Besucherschaft dabei nicht ausser Acht lassen sollte, halte ich hier mal als sinnvolle ergänzende Anregung zu diesen Bemerkungen fest, Danke.

            Selbst wenn man die alternative Schreibweise (also bspw. "ae" anstelle von "ä") ebenfalls akzeptieren würde (z.B. mit dem rel="canonical" Attribut), müsste der jeweilige User das ja auch erstmal wissen.

            Wenn er kein Ä kennt, dann wird er kein Ae dafür schreiben, das ist wohl richtig. Wenn er aber beides nicht kennt, wieso sollte er dann derartiges Interesse an http://example.com/Äbbelwoi haben, dass er diese Adresse aus eigenem Antrieb manuell in die Adresszeile eintippen wird?

            Oder kann man durchaus davon ausgehen, dass eh 99,xx% aller Besucher über SuMa- und sonstige Links kommen?

            Nimm noch mal das Beispiel aus meinem anderen Gedankengang, http://www.last.fm/music/布袋寅泰 - da bin ich hingekommen, weil ich bei Last.FM nach Tomoyasu Hotei gesucht habe, dem Namen, unter dem ich den Künstler von einer europäischen CD-Veröffentlichung kenne.

            布袋寅泰 sind für mich das, was für den Chinesen vielleicht unsere ä, ö, ü und ß wären.
            Nein, ich wäre nie darauf gekommen, die selber hinter http://www.last.fm/music/ in der Adresszeile einzutippen - obwohl ich das bei anderen Künstlern mit "westlichen" Namen durchaus öfter mal mache, um schnell auf die entsprechend Artist-Seite zu gelangen.

            Ich bin also auch nur durch Nutzung einer Suchfunktion auf diese Seite gekommen.

            Der "Wahnwitz" daran ist, dass der Pfadteil nachwievor UTF-8 kodiert ist, aber der Query String dann nicht mehr, sondern wie in diesem Beispiel dann auf einmal in Windows-1252.
            [...]
            Oder hast du eine Idee/ einen Vorschlag zur Lösung des Problems?

            Ja - die "Lösung" für total verkorkste Anfragen lautet: HTTP Statuscode 404.

            Irgendwann ist, bei aller Mühe und Liebe, dem Anfragenden nicht mehr zu helfen - und da ist mir dann auch egal, ob das nun mehr an ihm oder dem von ihm verwendeten Client/Browser liegt.

            MfG ChrisB

            --
            Light travels faster than sound - that's why most people appear bright until you hear them speak.
            1. Hi,

              vorweg vielen Dank für deine Antwort und vorallem auch dafür, dass du klipp & klar schreibst, was, bzw. wie du es machen würdest. Solche Aussagen helfen mir ganz erheblich bei meiner Entscheidungsfindung. Denn eines der häufigsten Probleme ist, dass es für viele Dinge eben mehr als eine Möglichkeit gibt, und man sich für irgendeine davon entscheiden muss.

              Der "Wahnwitz" daran ist, dass der Pfadteil nachwievor UTF-8 kodiert ist, aber der Query String dann nicht mehr, sondern wie in diesem Beispiel dann auf einmal in Windows-1252.
              [...]
              Oder hast du eine Idee/ einen Vorschlag zur Lösung des Problems?

              Ja - die "Lösung" für total verkorkste Anfragen lautet: HTTP Statuscode 404.

              Irgendwann ist, bei aller Mühe und Liebe, dem Anfragenden nicht mehr zu helfen - und da ist mir dann auch egal, ob das nun mehr an ihm oder dem von ihm verwendeten Client/Browser liegt.

              Ja, du sagst es. Zumal das außerhalb der "Reichweite" des Autors liegt, sondern in erster Linie wirklich vom OS und dem verwendeten Browser abhängt.
              Siehe z.B.: http://tyenki.com/thoughts/tag/character-encoding/

              Eventuell könnte man hier dann noch versuchen über den HTTP_USER_AGENT (sofern vorhanden) das "Erraten" der Kodierung etwas treffsicherer zu machen?

              In dem Zusammenhang würde mich jetzt auch noch interessieren, wie es sich bei anderen OS als Windows verhält, sprich also u.a. bei Linux und MacOS(X)?
              Ich gehe mal davon aus, dass auf diesen Systemen CP1252 nicht unbedingt vorhanden ist.

              Besten Dank nochmal.

              Gruß Gunther

      2. Hi!

        Alle Funktionen, die dir eine Zeichenkodierungserkennung versprechen, kochen auch nur mit Wasser und liefern nicht in jedem Fall ein fehlerfreies Ergebnis.
        Überhaupt scheint das ein "schwieriges" Kapitel zu sein. In den diversen User-Kommentaren zu einigen der PHP-Funktionen liest man immer das Wort "buggy" ...!

        Eine einigermaßen sinnvolle Funktion achtet nur darauf, dass keine bei UTF-8 nicht vorkommenden Bytefolgen enthalten sind. Wie gültige Bytefolgen aussehen, zeigt unter anderem die Wikipedia im Abschnitt UTF-8 - Kodierung.

        Es ist auch nicht möglich, einem Client die zu verwendende Kodierung vorzuschreiben. Das geht schon deshalb nicht, weil ein Client auch ohne den Server zu kennen, eine Anforderung absenden können muss. Weiterhin gibt es keine Regel, in welcher Kodierung er zu senden hat und auch keinen Header oder dergleichen, in dem der Client dem Server die verwendete Kodierung mitteilt.
        Ja, klasse. Je mehr ich mich mit dem Thema (zwangsweise) befasse, umso erstaunter bin ich, dass das Surfen im Web bisher so "unproblematisch" funktioniert hat.

        Die Vorreiter der Rechentechnik und des Internets haben keine Probleme mit komischen Zeichen, weil sie diese einfach nicht kennen. Sie haben seit je her nur 26 Buchstaben betrachtet und sich nicht für den Rest der Welt interessiert - und haben uns damit dieses ganze Dilemma eingebrockt. Da also vieles nur ASCII-tauglich erfunden wurde, tut man gut daran, an den kritischen Stellen nur ASCII zu verwenden. Kritische Stellen sind all die, an denen keine explizite Kodierung angegeben werden kann, also vor allem bei URLs und Dateinamen.

        Wenn du eine URL als Text veröffentlichst, in der ein Umlaut enthalten ist, so wird es vermutlich jemanden geben, der die URL kopiert und bei sich veröffentlicht. Auf deinen Seiten ist alles kein Problem, solange die Leute nur klicken. Der Browser wird (wenn er nicht verkonfiguriert ist) die Zeichenkodierung verwenden, in der deine Seite vorliegt. Wird sie aber in eine Seite mit anderer Kodierung eingefügt, wirst du sie in eben dieser zurückbekommen. Machen kannst du dagegen nichts, nur vermeiden, indem du dich auf ASCII beschränkst, oder musst hinterher rumraten, welche Kodierung es sein könnte.

        Hier fangen dann meine Probleme an. Denn welche Funktion muss ich denn nun bspw. verwenden: stripos() (für ISO-8859-x) oder mb_stripos() (für UTF-8)?

        Zunächst musst du wissen, welche Zeichenkodierung vorliegt. Das weißt du entweder, weil du volle Kontrolle über die Systeme hast - beispielsweise in Richtung DBMS - oder weil du auf eine bestimmte Kodierung vertraust oder sie zumindest auf syntaktische Fehlerfreiheit geprüft hast.

        Und es fängt ja schon viel früher an. So ist mir u.a. auch noch nicht klar, wann, wie und warum welcher Browser in der Adresszeile "Klartext" (auch mit Umlauten etc.) und wann URL kodierten Text (also mit %xx) anzeigt? Mein FF 3.5 zeigt bspw. bei einem Klick auf einen Link den "Klartext" in der Adresszeile (in der Statuszeile ebenso) an und bei manueller Eingabe den kodierten Text.

        Das ist auch einigermaßen unerheblich, weil du als Server nur den Request siehst, nicht was der Client anzeigt.

        Lasse ich mir dann bspw. per Script die $_SERVER Variablen anzeigen, dann sind $_SERVER[REQUEST_URI] und $_SERVER[QUERY_STRING] immer kodiert, während $_SERVER[PATH_INFO] im Klartext angezeigt wird.
        Beispiel:
        $_SERVER[REQUEST_URI]: /Umlaute:%20%C3%A4%C3%84%C3%B6%C3%96%C3%BC%C3%9C%C3%9F%20Preis:%20%E2%82%AC%2030,-
        $_SERVER[PATH_INFO]: /Umlaute: äÄöÖüÜß Preis: € 30,-

        Du musst hier zwischen kontexterechter Kodierung und Zeichenkodierung unterscheiden. Die %xx-Schreibweise ist dem URL-Kontext geschuldet. PathInfo ist bereits ein vom Server dekodierter Wert, während RequestUri als Original angezeigt wird, also so wie der Webserver ihn empfangen hat. Wenn du damit weiterarbeiten willst, empfiehlt es sich, erst einmal die "Transportsicherung" zu entfernen, also url_decode() darauf anzuwenden.

        Das Ergebnis ist eine Bytefolge. Mit bin2hex() kannst du dir die Bytewerte genau ansehen, doch aus der URL-Kodierung lassen sich die uns interessierenden Bytes ebenfalls herauslesen. %20 ist ein Leerzeichen, nicht weiter beachtenswert. %C3%B4 steht für das ä, also ein UTF-8-kodiertes. Sähest du stattdessen ein %E4, wäre es mit hoher Wahrscheinlichkeit ein ISO-8859-1-kodiertes. Und so weiter. Das €-Zeichen am Schluss wird übrigens in UTF-8 mit drei Bytes kodiert: %E2%82%AC.

        Für die UTF-8-"Erkennung" gibt es einige Funktionen in den Userkommentaren bei den üblichen verdächtigen Funktionen (suche bei utf8_decode, utf8_encode, mb_detect_encoding). Sie können aber lediglich die formale Richtigkeit von UTF-8-Sequenzen ermitteln, nicht aber, ob tatsächlich eine UTF-8-Kodierung vom Autor beabsichtig war oder ob die Bytesequenz nur zufällig gültiges UTF-8 ergibt.
        Ja, danke! Mit den ganzen Funktionen komme ich auch noch nicht wirklich zurecht. Solange ich die_nicht_verwende, funktioniert bisher alles (augenscheinlich) wie es soll. Sobald ich mit einer der Funktionen anfange "rumzudoktern", klappt nichts mehr!

        Versuch genauer hinzuschauen, notfalls auch indem du dir die Bytewerte mit bin2hex() ausgeben lässt. Was genau liegt vor? Was verspricht eine Funktion zu tun? Was genau ist das Ergebnis? Welches Ziel sollte erreicht werden und stimmt das mit dem tatsächlich vorliegenden Ergebnis überein?

        Dabei musst du auch beachten: Muss eine weitere Kodierung beachtet werden, beispielsweise die URL-Kodierung? Sehe ich Zeichen? Wenn ja, gemäß welcher Zeichenkodierung sind sie aktuell interpretiert worden? Was zeigt der Browser im Kontextmenü an oder was hat er unter Ansicht->(Zeichen)kodierung angehakt?

        Übrigens, den Request allein im Hinblick auf UTF-8-Fähigkeit zu beachten, ist oft noch nicht ausreichend. Auch das Speichern der Daten verlangt eine Betrachtung und damit die dafür verwendeten Medien (Dateien oder Datenbanken). Du kannst ja mal aufzählen, welche Stellen dir im Webumfeld einfallen, an denen eine Zeichenkodierung eine Rolle spielt und eingestellt werden kann. Wir™ schauen dann mal, ob du alle kennst. (Das Nachschlagen im hiesigen Archiv ist nicht nur nicht verboten, sondern ausdrücklich erwünscht :-)
        Ja wie jetzt? Gibt es denn noch mehr, als die, die du in deinem Artikel genannt hast? Dann wäre der ja unvollständig. ;-)

        Ja, der Request ist ja nur ein Teil des gesamten Verarbeitungsvorgangs. Zu beachten sind immer zwei Dinge: die Verarbeitung innerhalb eines Systems und die Kommunikation zwischen zwei Systemen.

        Mein Artikel ist unvollständig, wenn du so willst. Er betrachtet nur den Kontextwechsel an sich und welche Zeichen in welchen Situationen wie maskiert werden müssen. Er behandelt nicht das Thema Zeichenkodierung, also welche Bits und Bytes und welches System zum Speichern dieser Zeichen verwendet wurden, weil das ebenfalls ein komplexes Thema und einen weiteren Artikel wert ist. Angenommen es gäbe kein Zeichenkodierungsproblem, das Kontextproblem besteht weiterhin.

        Ein Aufruf von mb_internal_encoding() gibt mir ISO-8859-1 als interne Kodierung aus.
        Diese müsste ich, wenn ich durchgehend mit UTF-8 arbeiten will, dann entsprechend umstellen?
        Diese Frage konnte das PHP-Handbuch nicht klären?
        Nein, nicht wirklich.

        Schade, ich habe nämlich die mb-Fuktionen selbst noch nicht verwendet und wollte deshalb eine konkrete Antwort vermeiden. So ein stringverarbeitendes System benötigt aber stets Informationen, welche Kodierung vorliegt, denn raten ist ja schlecht bis unmöglich. Prinzipiell kann man das so lösen, dass man die Kodierung über einen weiteren Parameter bekanntgibt oder man verwendet generel nur eine Kodierung, die man zentral einstellt, was dann wohl mb_internal_encoding() macht.

        Außerdem müsste ich doch dann nach meinem bisherigen Verständnis sicherstellen, dass meine zu verarbeitenden Strings und Arrays auch wirklich alle definitiv UTF-8 kodiert sind, oder nicht? Ansonsten würde ich ja wiederum "falsche" Ergebnisse bekommen?

        Ja klar, so ein System ist per se dumm. Wenn du ihm sagst: "englisch", kannst du ihm nicht mit französisch kommen und Fehlerfreiheit erwarten. Du musst sicherstellen, dass englisch ankommt und gegebenenfalls französisch nach englisch übersetzen.

        Stellt sich für mich aber gleich die nächste Frage: Lohnt sich der "Aufwand/ Aufstand" (bei PHP < 6), [...]
        Anforderung ist im Prinzip "nur", dass aufgrund meiner "sprechenden" URLs eben auch Umlaute korrekt gehandhabt werden können sollen (tolles Satzkonstrukt). Dabei war ich wohl fälschlicherweise davon ausgegangen, bzw. dem Trugschluss erlegen, dass wenn ich eh alles andere UTF-8 kodiere, es dann auch "einfacher" wäre, scriptintern gleich mit UTF-8 kodierten Strings und Arrays zu arbeiten. Dem scheint aber wohl momentan zumindest noch nicht so zu sein.

        Das wird sich auch in absehbarer Zukunft nicht ändern, solange nicht UTF-8 mit Höchstquote im Internet Verwendung findet. Das Problem hängt ja nicht nur an PHP6 oder nicht, sprich: an der eigenen Fähigkeit UTF-8 verarbeiten zu können, sondern gerade bei den ULRs an den Zulieferern.

        Nicht-ASCII-Zeichen in URLs zu verwenden, die auch in anderen, nicht selbst kontrollierten Systemen abgelegt werden können, ist derzeit nicht wirklich empfehlenswert. Als einen Weg, wenn du trotzdem Nicht-ASCII-Zeichen in URLs haben möchtest, sehe ich nur, einkommende URLs einem UTF-8-Syntax-Check zu unterziehen und bei Nichtbestehen, vor dem Weiterverarbeiten von ISO-8859-1[*] nach UTF-8 umzukodieren.

        [*] Windows-1252 kann auch eine Option sein, besonders wenn das €-Zeichen eine Rolle spielt, denn das gibt es nicht in ISO-8859-1 wohl aber in Windows-1252. Im Allgemeinen verwenden Browser beim Vorkommen von den in Windows-1252 zusätzlich enthaltenen Zeichen tatsächlich Windows-1252 statt ISO-8859-1, auch wenn es als letzteres deklariert wird. (Siehe zu den Unterschieden den Wikipedia-Eintrag zu ISO-8859-1.)

        Lo!