2016-04-21 3 views
2

Was genau ist die beabsichtigte Semantik für Zeichenbereiche in regulären Ausdrücken, wenn einer oder beide Endpunkte des Bereichs außerhalb des BMP liegen? Ich habe beobachtet, dass die folgende Eingabe in Python 2.7 und 3.5 anders verhält:Python-Semantik für Unicode-Bereiche mit astralen Ebenen

import re 
bool(re.match(u"[\u1000-\U00021111]", "\u1234")) 

In meinem 2.7 ich False, in 3.5 ich True. Letzteres macht für mich Sinn. Ersteres ist vielleicht auf \U00021111 zurückzuführen, das durch ein Ersatzpaar \ud844\udd11 dargestellt wird, aber selbst dann verstehe ich es nicht, da \u1234 gerade in Ordnung sein sollte.

  • Ist das irgendwo angegeben?
  • Ist das beabsichtigte Verhalten?
  • Hängt dies nur von der Python-Version ab, oder auch von Kompilierzeit-Flags bezüglich UTF-16 vs. UTF-32?
  • Gibt es eine Möglichkeit, konsistentes Verhalten ohne Fallunterscheidungen zu erhalten?
  • Wenn Fallunterscheidungen unvermeidlich sind, was genau sind die Bedingungen?

Antwort

3

Verwenden Sie einfach den u Präfix mit dem Eingabestring Python zu sagen, es ist ein Unicode-String:

>>> bool(re.match(u"[\u1000-\U00021111]", u"\u1234")) # <= See u"\u1234" 
True 

In Python 2.7, müssen Sie die Zeichenfolge in Unicode jedes Mal, wenn sie verarbeiten entschlüsseln. In Python 3 sind alle Zeichenfolgen standardmäßig Unicode und in der docs angegeben.

+1

Danke! Ich fühle mich dumm, dass ich das nicht bemerkt habe. Das zugrunde liegende Problem bleibt jedoch bestehen: 'bool (re.match (u [[u1000- \ U00021111]", u "\ ueeee")) "liefert immer noch andere Ergebnisse.Ich weiß, es ist ein schlechter Stil, eine Frage zu bearbeiten, sobald sie beantwortet wurde, so dass die Antwort nicht mehr zutrifft. Würdest du mit mir einverstanden sein oder sollte ich eine neue Frage stellen? – MvG

+0

Überprüfen Sie [diese Python 2.7 IDEONE-Demo] (https://ideone.com/p9Jsgv). 'print (bool (re.match (u" [u1000- \ U00021111] ", u" \ ueeee ")))' druckt 'True'. Ihr Python 2.7 sollte so eingerichtet sein, dass Unicode-Eingaben möglich sind (siehe '# - * - Codierung: utf-8 - * -' Pragma). –

+2

@ WiktorStribiżew '#coding: utf8' tut in diesem Fall nichts. Es deklariert die Codierung der Quelldatei selbst, und in dieser Quelle gibt es keine Nicht-ASCII-Zeichen. –

2

Hier ist, was ich bisher herausgefunden habe.

PEP 261 die für Python 2.2 akzeptiert wurde eingeführt ein Kompilierzeit-Flag zum Erstellen von Unicode-Unterstützung entweder mit einer schmalen UTF-16-Darstellung oder eine breite UTF-32-Darstellung von Zeichen. Überprüfen Sie hex(sys.maxunicode) oder len(u'\U00'), um diese zur Laufzeit zu unterscheiden: schmale Builds melden maximal 0xffff und eine Länge von 2, breite bildet ein Maximum von 0x10ffff und eine Länge von 1. für Python 3.3 verbirgt die Implementierungsdetails einer Unicode-Zeichenkette, wodurch alle Zeichenketten wie UTF-32 erscheinen (ohne wirklich viel Platz zu verschwenden, wenn es nicht notwendig ist). So werden schmale Builds vor 3.3 Codepunkte auf Astralebenen in Surrogatpaare zerlegen und die einzelnen Surrogate unabhängig sowohl für die Konstruktion des regulären Ausdrucks als auch für die zu vergleichende Zeichenkette behandeln. Zumindest konnte ich keinen Hinweis auf das Gegenteil finden.

Wie Wiktor darauf hinwies, war mein Beispiel einfach dumm, da ich das u Präfix für das zweite String-Literal vergessen hatte. Daher wird Python 2 dies nicht als Escape-Sequenz sondern als Byte-String analysieren. Dies erklärt, warum es so aussah, als ob der Codepunkt selbst nach der Berücksichtigung von Ersatzpaaren nicht in diesem Bereich enthalten wäre.

Wie für das beabsichtigte Verhalten: Seit Python 3.3 sollte die Unterscheidung nach Build-Typ obsolet werden. Das Behandeln jedes Codepunkts als Einheit, unabhängig von der Ebene, sollte für Python 3 der richtige Weg sein. Die Abwärtskompatibilität bei Narrow-Builds stellt jedoch ein Konfliktziel für ältere Versionen dar.

+0

Python 3.3 verwendet flexible Unicode-Darstellung (ich glaube nicht, dass es die Implementierung mehr verbirgt als beispielsweise ein breiter Python-Build - es ist das Gegenteil: die Abstraktion leckt für Nicht-BMP-Zeichen in engen Python-Builds). Es ist nicht offensichtlich, warum das Regex-Modul keine Ersatzpaare verarbeiten kann. – jfs

+0

@ J.F.Sebastian: Ich stimme zu, dass es keinen Grund gibt, dass Ersatzpaare * vom Re-Modul nicht behandelt werden können, aber meine Beobachtung zeigt an, dass * nicht behandelt wird. Ich nehme an, dass dies zuerst aus der Einfachheit und später aus der Rückwärtskompatibilität heraus war. Viele andere UTF-16-basierte Sprachen machen dasselbe: Java, JavaScript, ... Ich bin mir nicht sicher, ob ich Ihre Argumentation über die abstrahierte Abstraktion verstehe. Aber ich denke, dass es auf der Python-Ebene keinen Unterschied zwischen 3,3+ und einem breiten <3,3 geben sollte, obwohl es Unterschiede auf der Ebene des Erweiterungsmoduls geben wird. Ist es das, was du meintest? – MvG

+2

Wir sind uns einig, aber es gibt einen winzigen kleinen Unterschied: Sie sagen: "Python 3.3 ** verbirgt ** die Implementierungsdetails" *, ich sage, dass ein schmaler Python-Build ** seine (mangelhafte) Implementierung aufdeckt. Das Ergebnis ist das gleiche, der Unterschied ist, dass Sie den Akzent/Stress setzen. "Abstraktionslecks" bedeutet, dass ein schmaler Python-Build nicht die Vorstellung respektiert, dass eine Unicode-Zeichenfolge eine Sequenz von Unicode-Codepunkten in Python ist (ein einzelner Unicode-Codepoint kann als zwei Codepunkte (Ersatzpaar) dargestellt werden). – jfs

Verwandte Themen