Alexander (HH): MySQL : mit regulären Ausdrücken Wörter für Buchstaben suchen

Beitrag lesen

Moin Moin!

»» Das hilft bei UTF-8 (genauer bei jeder Multi-Byte-Codierung) nicht. REGEXP ist derzeit (MySQL 5.x) nicht multibyte-safe und arbeitet byteweise, nicht zeichenweise.

Ich verstehe das Problem noch nicht, was aber sicherlich an meinen mangelnden technischen Fähigkeiten liegt.

Mit UTF-8 gilt die aus ASCII-Zeiten bekannte Regel 1 Byte = 1 Zeichen nicht mehr. Zeichen außerhalb des ASCII-Zeichensatzes (ab Zeichen 128) belegen zwei bis vier Bytes. Siehe http://de.wikipedia.org/wiki/UTF-8.

Jeder Code, der mit der Annahme 1 Byte = 1 Zeichen arbeitet, funktioniert mit UTF-8 nicht mehr, sobald auch nur ein Zeichen oberhalb Zeichen 127 vorkommt.

Das Wort "Bär" wird in ISO-8859-1 durch die drei Bytes 0x42, 0xE4, 0x72 dargestellt. In UTF-8 wird es durch vier Bytes 0x42, 0xC3, 0xA4, 0x72 dargestellt. Ein UTF-8-fähiges System (z.B. Perl 5.8 und neuer) kann beide Darstellungen in eine interne Darstellung überführen, die auf Zeichen (und nicht auf Bytes) basiert. Die length()-Funktion liefert dann auch für beide Bytefolgen 3 (Zeichen). Ein System, das von UTF-8 keine Ahnung hat, ermittelt für die UTF-8-Darstellung eine Länge von 4 (Bytes). Ein UTF-8-fähiges System vergleicht die beiden Darstellungen anhand der internen, Zeichen-basierenden Darstellung und hält sie für identisch. Ein System, das UTF-8 nicht berücksichtigt, sieht zwei unterschiedliche und sogar unterschiedlich lange Bytefolgen, die demnach absolut nicht identisch sind.

Das Problem mit den Character Classes (z.B. [abfgx]) ist nun, das MySQL hier byteweise arbeitet und nicht Zeichenweise. [Bär] als Character Class sucht nach den VIER(!) BYTES(!) 0x42, 0xC3, 0xA4, 0x72. Und aufgrund der UTF-8-Codierung kommt 0xC3 in UTF-8-Strings relativ häufig vor.

Das Problem mit dem Punkt als Platzhalter ist ähnlich. Obwohl du eigentlich ein beliebiges ZEICHEN meinst, vergleicht MySQL hier BYTES. Das Pattern /B.r/ paßt zwar auf "Bar", aber nicht auf "Bär" in UTF-8-Darstellung, weil das "ä" aus zwei Bytes zusammengesetzt ist. /B..r/ bzw. /B.{2}r/ würde zwar auf "Bär" in UTF-8-Darstellung passen, aber eben auch auf "Bahr" und nicht mehr auf "Bar".

MySQL ist an dieser Stelle schicht und ergreifend kaputt, weil die RE-Engine mit Bytes operiert, wo sie mit Zeichen arbeiten sollte.

Ein sehr kruder Workaround wäre, sowohl den Wert als auch die Regular Expression explizit in ISO-8859-x umzuwandeln, bevor der Vergleich stattfindet. Das scheitert allerdings, sobald Du Zeichen außerhalb ISO-8859-x benutzt. Sauberer wäre ein vollständiger Wechsel auf ISO-8859-x. Und noch besser wäre es, MySQL zu reparieren oder durch ein UTF-8-taugliches RDBMS zu ersetzen.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".