niehztog: RegExp: Range von UTF-8 Zeichen definieren

Hallo liebe Community,

noch immer baue ich an einem etwas kniffligem Regulären Ausdruck (PHP4, preg_match_all). Da die PHP Seiten UTF8 Header senden und es auch wichtig ist, dass der reguläre Ausdruck UTF8 Buchstaben erkennt, folgende Frage:

Ich muss irgendwie einen Zeichenbereich ähnlich wie [a-zA-Z0-9_] für sämtliche in UTF8 gültigen Buchstaben bzw. Nicht-Satzzeichen definieren. Das standartmäßige \w reicht in diesem Fall nicht, da z.B. keine Asiatischen/Kyrillischen usw. Buchstaben mit drin sind.

Ich bin dann auf diesen äußerst Hilfreichen Ausdruck gestoßen, der in PHP aber leider wegen der fehlenden Implementierung von \u nicht funktioniert: \u00A1-\uFFFF

Ich habe dann probiert den Ausdruck mit \x nachzubilden, leider erfolglos:
\xc2\xa1-\xef\xbf\xbf (matchte nur auf herkömmliche Zeichen von a-z und nicht auf Asiatische Sonderzeichen etc)

Wie kann ich da vorgehen? Oder bin ich da tatsächlich an eine Grenze der PHP4 Implementierung gestoßen?

Gruss
Niehztog

  1. @@niehztog:

    […] \u00A1-\uFFFF

    Ich habe dann probiert den Ausdruck mit \x nachzubilden, leider erfolglos:
    \xc2\xa1-\xef\xbf\xbf

    Wie kommst du auf die Idee, '\xc2\xa1' und '\xef\xbf\xbf' würden '\u00A1' bzw. '\uFFFF' entsprechen?

    Live long and prosper,
    Gunnar

    --
    Erwebsregel 208: Manchmal ist das einzige, was gefährlicher als eine Frage ist, eine Antwort.
    1. Wie kommst du auf die Idee, '\xc2\xa1' und '\xef\xbf\xbf' würden '\u00A1' bzw. '\uFFFF' entsprechen?

      Habs hier nachgeschaut: http://www.utf8-zeichentabelle.de -> [für Perl-String-Literals]

      Es war einen Versuch wert. Jedenfalls klappt es auch nicht das \u einfach durch ein \x zu ersetzen, was ja eigentlich zu erwarten wäre? (Modifier u ist gesetzt)

  2. Moin!

    Ich muss irgendwie einen Zeichenbereich ähnlich wie [a-zA-Z0-9_] für sämtliche in UTF8 gültigen Buchstaben bzw. Nicht-Satzzeichen definieren. [...] nicht funktioniert: \u00A1-\uFFFF

    Du hast dir da eine ziemlich aufwendige Aufgabe vorgenommen.

    Falsch ist natürlich, pauschal den gesamten Zeichenbereich von U+00A1 bis U+FFFF zu verwenden - damit erlaubst du viele Bereiche, die undefiniert sind (beispielsweise dürfen die Codepoints U+D800 bis U+DFFF nicht auftreten, weil sie in UTF-16 als Codierung für Zeichen außerhalb der Basic Multilingual Plane BMP  (U+0000 bis U+FFFF) verwendet werden.

    Außerdem stecken auch innerhalb der BMP verteilt viele "nette" Zeichen, die zwar hübsch anzuschauen sind, aber keinerlei "Buchstabe" darstellen, sondern in irgendeiner Art und Weise als Sonderzeichen zu betrachten wären. Diese Zeichen mittels RegEx herauszufiltern dürfte eine höchst aufwendige Arbeit werden.

    Wie kann ich da vorgehen? Oder bin ich da tatsächlich an eine Grenze der PHP4 Implementierung gestoßen?

    Du bist sowieso an eine Grenze gestoßen. Schon allein wegen PHP 4. PHP 4 ist offiziell seit dem 8.8.2008 tot - die Beerdigung wurde mit der definitiv letzten Veröffentlichung diverser Patches gefeiert, ab jetzt gibt es nur noch PHP 5. Stelle alle deine Skripte schnellstmöglich auf PHP 5 um, bevor dich dein Provider dazu zwingt. Wenn dein Provider dir heute kein PHP5-Hosting anbieten kann, wechsle den Provider - er ist schlecht informiert und wird Sicherheitslücken in PHP 4 nicht schließen können.

    Zweitens: Ja, PHP bietet derzeit nur eine sehr schlechte Berücksichtigung von Unicode allgemein (UTF-8 ist ja nur eine Codierungsform). Unicode selbst definiert zu jedem Zeichen weit mehr als nur den Codepoint, man kann der Zeichendatentabelle auch entnehmen, welchen Typs das Zeichen entspricht - also beispielsweise "Buchstabe" oder "Satzzeichen". Diese Info würde mutmaßlich auch einem unicodefähigen Regex-Interpreter zur Verfügung stehen, um "\w" auswerten zu können.

    Damit PHP mit Multibyte-Encodings umgehen kann, ist die Multibyte-Erweiterung notwendig. Damit kannst du dann UTF-8 in Strings direkt nutzen, beispielsweise mb_strlen() statt strlen() - und auch mb_ereg() existiert.

    Auf der anderen Seite frage ich mich allerdings, ob dein Vorhaben grundsätzlich sinnvoll ist. Wenn du sowieso mehrere zehntausend Schriftzeichen erlauben willst, warum willst du dann einige tausend Nicht-Schriftzeichen ausschließen? Wem ist damit geholfen? Welchen Sinn hat das?

    - Sven Rautenberg

    --
    "Love your nation - respect the others."
    1. Auf der anderen Seite frage ich mich allerdings, ob dein Vorhaben grundsätzlich sinnvoll ist. Wenn du sowieso mehrere zehntausend Schriftzeichen erlauben willst, warum willst du dann einige tausend Nicht-Schriftzeichen ausschließen? Wem ist damit geholfen? Welchen Sinn hat das?

      Es geht darum, dass mein momentaner Ausdruck:
      [\w.:/]{2,}.(?:de|com|org|net){1}
      leider keine IDN-Domains aus dem Asiatischen oder Orientalischen Raum erfasst, was mich etwas wundert.
      Leider bin ich an PHP4 gebunden, da es kein privates Projekt ist.

      1. Moin!

        Auf der anderen Seite frage ich mich allerdings, ob dein Vorhaben grundsätzlich sinnvoll ist. Wenn du sowieso mehrere zehntausend Schriftzeichen erlauben willst, warum willst du dann einige tausend Nicht-Schriftzeichen ausschließen? Wem ist damit geholfen? Welchen Sinn hat das?

        Es geht darum, dass mein momentaner Ausdruck:
        [\w.:/]{2,}.(?:de|com|org|net){1}
        leider keine IDN-Domains aus dem Asiatischen oder Orientalischen Raum erfasst, was mich etwas wundert.

        IDN-Domains zu prüfen ist ungefähr so schwer, wie normale Domains zu prüfen. Ohne DNS-Request komplett unmöglich.

        Da aber der DNS-Request ohnehin erfolgen muß, implementierst du am besten den Punycode-Algorithmus, der dir aus beliebigem Unicode-Zeugs eine schöne, ASCII-kompatible IDN-Domain macht. Die kannst du dann bei Bedarf immer noch durch den Regex schicken.

        Allerdings hilft dir weder der Check mit oder ohne Punycode wirklich weiter, da du - zumindest ohne DNS-Request - nicht weißt, ob die verwendeten Unicode-Zeichen wirklich in einem Domainnamen erlaubt sind.

        Die DENIC beispielsweise erlaubt zwar "Umlautdomains" (und die Umlaute sind natürlich Unicode, also sind IDN-Domainnamen notwendig), aber beispielsweise nichts chinesisches im Namen (was auch nur Unicode wäre).

        Es dürfte vermutlich auch bei anderen Top-Level-Domains so sein, dass nicht pauschal der gesamte Unicode-Bereich erlaubt ist, sondern nur ausgewählte Zeichen. Welche Zeichen erlaubt sind, weiß der Verwalter der jeweiligen TLD. Nur mit einem nackten Regex wirst du mit großer Wahrscheinlichkeit scheitern - oder die Regex-Prüfung ist sinnlos.

        Leider bin ich an PHP4 gebunden, da es kein privates Projekt ist.

        Auch kommerzielle bestehende Seiten haben ab jetzt das Problem, dass sie auf PHP 5 umgestellt werden müssen, egal wieviel oder wie wenig Spaß das dem Geldgeber macht.

        Das ist im Idealfall die Installation von PHP 5 und der Applikation auf einem Testserver, um festzustellen, dass alles trotzdem noch funktioniert. Nur gemacht werden muß es halt. "Du wurdest gewarnt." Die Story ist ja auch nicht neu, sondern war bereits seit mindestens zwei Jahren absehbar.

        - Sven Rautenberg

        --
        "Love your nation - respect the others."
        1. Moin!

          Einen wunderschönen Guten Morgen zurück! :-)

          Vielen Dank für deine sehr ausführliche Antwort (die ich mindestens zweimal lesen musste, um die ganzen Infos aufzunehmen).

          In meinem Anwendungsfall ist das mit dem DNS-Request nicht ganz so wichtig (so genau nimmt das niemand), trotzdem werde ich mich mal nach dem Punycode erkundigen.

          Schöne Grüße aus Köln
          Niehztog

  3. Hi,

    Ich muss irgendwie einen Zeichenbereich ähnlich wie [a-zA-Z0-9_] für sämtliche in UTF8 gültigen Buchstaben bzw. Nicht-Satzzeichen definieren.

    Unterstützen die preg-Funktionen die Unicode-Properties von Perl?
    \p{L} oder \p{Letter} für Buchstaben, \P{L} bzw. \P{Letter} für Nicht-Buchstaben,
    \p{P} oder \p{Punctuation} für Satzzeichen, \P{P} für Nicht-Satzzeichen usw.

    cu,
    Andreas

    --
    Warum nennt sich Andreas hier MudGuard?
    O o ostern ...
    Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
  4. @@niehztog:

    UTF8 Buchstaben

    Da scheint mir noch ein Verständnisproblem vorzuliegen: Es gibt keine „UTF-8-Zeichen“. Du meinst „Unicode-Zeichen“. Unicode ist ein Zeichensatz; UTF-8 eine Zeichencodierung. [QA-WHAT -IS-ENCODING, Jendryschik]

    Aus diesem Verständnisproblem resultierte wohl auch dein Versuch, die Bytes, die in UTF-8 für ein Zeichen stehen, zu betrachten anstatt die Unicode-Codepoints. Reguläre Ausdrücke arbeiten auf Zeichen.

    Live long and prosper,
    Gunnar

    --
    Erwebsregel 208: Manchmal ist das einzige, was gefährlicher als eine Frage ist, eine Antwort.
  5. (Hallo|Hi(ho)|Tag) niehztog,

    Ich muss irgendwie einen Zeichenbereich ähnlich wie [a-zA-Z0-9_] für sämtliche in UTF8 gültigen Buchstaben bzw. Nicht-Satzzeichen definieren. Das standartmäßige \w reicht in diesem Fall nicht, da z.B. keine Asiatischen/Kyrillischen usw. Buchstaben mit drin sind.

    Ich bin dann auf diesen äußerst Hilfreichen Ausdruck gestoßen, der in PHP aber leider wegen der fehlenden Implementierung von \u nicht funktioniert: \u00A1-\uFFFF

    In PCRE hättest du das so formulieren müssen '/\x{0a1}-\x{fff}/u', damit es funktioniert. Und der Modifikator /u (Klein-u) signalisiert dir, dass das auch nur im UTF-8-Modus funktioniert. Der entsprechende Hinweis ist tief im PHP-Handbuch vergraben (suche nach "\x{").

    Wie kann ich da vorgehen? Oder bin ich da tatsächlich an eine Grenze der PHP4 Implementierung gestoßen?

    Nein, zumindest nicht, wenn es sich um PHP-Versionen handelt, die größer oder gleich 4.3 sind.

    MffG
    EisFuX