2013-02-18 4 views
7

Von meinen vorherigen Fragen Why under locale-pragma word characters do not match? und How to change nested quotes Ich habe gelernt, dass im Umgang mit UTF-8-Daten können Sie \w nicht als Word-Zeichen vertrauen und Sie müssen die Unicode-Zeicheneigenschaft \p{Word} verwenden. Jetzt bin ich in einer Situation, in der ich fand, dass Null-Wortgrenze \b auch nicht mit UTF-8 funktioniert (mit aktiviertem Gebietsschema), aber ich fand keine Entsprechung in Unicodezeicheneigenschaften. Ich dachte, ich könnte es selbst bauen wie: (?<=\P{Word})(\p{Word}+)(?=\P{Word}), sollte es \b(\w+)\b entsprechen.Wie Emulation der Wortgrenze bei Verwendung von Unicode-Zeicheneigenschaften?

Im Testskript unten habe ich zwei Arrays, um zwei verschiedene Regexe zu testen. Die erste basiert auf \b funktioniert gut, wenn das Gebietsschema nicht aktiviert ist. Um es auch mit Locales arbeiten zu lassen, schrieb ich eine andere Version mit Emulation der Grenze (?=\P{Word}), aber es funktioniert nicht so, wie ich es erwartet hatte (ich zeige auch erwartete Ergebnisse im Skript).

Sehen Sie, was falsch ist und wie man emulierte Regex-Arbeit als erstes mit ASCII (oder ohne Gebietsschema) bekommt?

#!/usr/bin/perl 

use 5.010; 
use utf8::all; 
use locale; # et_EE.UTF-8 in my case 
$| = 1; 

my @test_boundary = ( # EXPECTED RESULT: 
    '"abc def"',   # '«abc def»' 
    '"abc "d e f" ghi"', # '«abc «d e f» ghi»' 
    '"abc "d e f""',  # '«abc «d e f»»' 
    '"abc "d e f"',  # '«abc "d e f»' 
    '"abc "d" "e" f"', # '«abc «d» «e» f»' 
    # below won't work with \b when locale enabled 
    '"100 Естонiï"',  # '«100 Естонiï»' 
    '"äöõ "ä õ ü" ï"', # '«äöõ «ä õ ü» ï»' 
    '"äöõ "ä õ ü""',  # '«äöõ «ä õ ü»»' 
    '"äöõ "ä õ ü"',  # '«äöõ «ä õ ü»' 
    '"äöõ "ä" "õ" ï"', # '«äöõ «ä» «õ» ï»' 
); 

my @test_emulate = ( # EXPECTED RESULT: 
    '"100 Естонiï"',  # '«100 Естонiï»' 
    '"äöõ "ä õ ü" ï"', # '«äöõ «ä õ ü» ï»' 
    '"äöõ "ä õ ü""',  # '«äöõ «ä õ ü»»' 
    '"äöõ "ä õ ü"',  # '«äöõ "ä õ ü»' 
    '"äöõ "ä" "õ" ï"', # '«äöõ «ä» «õ» ï»' 
); 

say "BOUNDARY"; 
for my $sentence (@test_boundary) { 
    my $quote_count = ($sentence =~ tr/"/"/); 

    for (my $i = 0 ; $i <= $quote_count ; $i += 2) { 
    $sentence =~ s/ 
     "(       # first qoute, start capture 
     [\p{Word}\.]+?   # suva word-char 
     .*?\b[\.,?!»]*?   # any char followed boundary + opt. punctuation 
    )"       # stop capture, ending quote 
     /«$1»/xg;     # change to fancy 
    } 
    say $sentence; 
} 

say "EMULATE"; 
for my $sentence (@test_emulate) { 
    my $quote_count = ($sentence =~ tr/"/"/); 

    for (my $i = 0 ; $i <= $quote_count ; $i += 2) { 
    $sentence =~ s/ 
     "(      # first qoute, start capture 
     [\p{Word}\.]+?    # at least one word-char or point 
     .*?(?=\P{Word})   # any char followed boundary 
     [\.,?!»]*?     # optional punctuation 
    )"       # stop capture, ending quote 
     /«$1»/gx;     # change to fancy 
    } 
    say $sentence; 
} 
+4

Zuerst Sie sind falsch: '\ w' und' \ p {word} 'sind per Definition identisch. Aber zweitens, bitte, bitte, bitte *** benutze nicht das 'use locale'-Pragma. Es ist kaputt, unzuverlässig, unberechenbar und ein königlicher Schmerz im Hintern - wie Sie anscheinend entdeckt haben. Sie sollten das Modul "Unicode :: Collate :: Locale" verwenden. Du solltest wahrscheinlich auch nicht 'utf8: all' benutzen, sondern die spezifischen Dinge tun, die du willst. – tchrist

+0

@tchrist: '\ w' und' \ p {Wort} 'können identisch definiert werden, aber sie verhalten sich unter' use locale' anders. Natürlich werde ich das Gebietsschema nicht verwenden, wenn ich jetzt anders unterwegs bin. 'utf8 :: all' verwendet meine Bedürfnisse ziemlich gut und es ist eine saubere Art, meine Absichten zu zeigen. Wenn in utf8 :: all etwas fehlt, könnten Sie es vielleicht dem Autor zeigen? –

+0

Sie wissen nicht, was "utf8: all" tut oder nicht, was genau das Problem ist - ein Problem, das kann ich nicht durch Hinzufügen von Dingen beheben. Welche Ebene von "utf8" Warnungen erhalten Sie? Keine oder Warnung oder tödlich? Was ist mit den drei Subtypen, dem nonchar und den Surrogaten und der non_unicode? Diese Dinge sollten im Code explizit sein, damit die Leute sehen können, was sie sind. Dann gibt es das Problem des Renderns in NFD bei Eingabe und NFC bei Ausgabe; macht es das? – tchrist

Antwort

7

Da die Zeichen nach der Position des \b entweder einige Interpunktion oder " (um sicher zu sein, doppelt so überprüfen Sie bitte, dass \p{Word} keine von ihnen übereinstimmt), fällt es in dem Fall \b\W. Deshalb können wir \b mit emulieren:

(?<=\p{Word}) 

Ich bin nicht vertraut mit Perl, sondern aus what I tested here, scheint es, dass \w (und \b) auch gut funktioniert, wenn die Codierung auf UTF-8 gesetzt ist.

$sentence =~ s/ 
    "(
    [\w\.]+? 
    .*?\b[\.,?!»]*? 
)" 
    /«$1»/xg; 

Wenn Sie Perl 5.14 nach oben und oben können Sie den Zeichensatz-Set mit u Flag auf Unicode.


Sie können diese allgemeine Strategie verwenden, um eine Grenze zu erstellen, die einer Zeichenklasse entspricht. (So ​​wie \b Wortgrenze Definition auf der Definition von \w basiert).

Lassen Sie C die Zeichenklasse sein. Wir möchten, dass eine Grenze definieren, die auf der Zeichenklasse C basiert

Die unten Bau Grenze vor emulieren, wenn Sie das aktuelle Zeichen gehört zu C Zeichenklasse (entspricht (\b\w)) wissen:

(?<!C)C 

oder hinter (entspricht \w\b):

C(?!C) 

Warum negativ Umsehen? Da positive Umrundung (mit der komplementären Zeichenklasse) wird auch behaupten, dass es ein Zeichen vor/hinter haben muss (bestätigen Sie Breite vor/hinter mindestens 1). Negatives Umsehen ermöglicht den Anfang/das Ende der Zeichenfolge, ohne eine umständliche Regex zu schreiben.


Für \B\w Emulation:

(?<=C)C 

und ähnlich \w\B:

C(?=C) 

\B ist das genaue Gegenteil von \b, daher können wir drehen gerade die positive/negative Umsehen um den Effekt zu emulieren. Es macht auch Sinn - eine Nicht-Grenze kann nur gebildet werden, wenn es mehr Zeichen vor/hinter gibt.


Andere Emulationen (lassen c das Komplement Zeichenklasse von C sein):

  • \b\W: (?<=C)c
  • \W\b: c(?=C)
  • \B\W: (?<!C)c
  • \W\B: c(?!C)

Für die Emulation einer Standalone-Grenze (entspricht \b):

(?:(?<!C)(?=C)|(?<=C)(?!C)) 

und Standalone-nicht-Grenze (entspricht \B):

(?:(?<!C)(?!C)|(?<=C)(?=C)) 
+0

Das Ändern von '\ b' in' (?! \ P {Word}) 'hat die Ergebnisse nicht verändert. Mit Testfall ''" äöõ "ä õ ü" "werde ich statt" äöõ "ä äü' noch' äöõ' gefangen, wie bei meinem positiven Lookaround. Kannst du zeigen, was schief läuft? –

+0

@wk: Ich bin mir nicht sicher, was Sie versuchen (Bracket-Abgleich?). Das Problem ist nicht über Wortgrenze (und seine Emulation), sondern mit der Regex, die Sie gerade haben. – nhahtdh

+0

Mein Ziel ist es, Paare von Anführungszeichen zu ändern "äöõ" 'in fancy quotes' «äöõ». Bei verschachtelten Anführungszeichen sollte es nicht passende Paare ersetzen, sondern 1. und 3. Zitat, dann 2. und 4. Mein erster Regex funktioniert genauso wie ich es erwartet habe, wenn ich das Gebietsschema nicht aktiviere Ich brauche auch Locale. Also, in der zweiten Regex nur die Änderung, die ich gemacht habe, ändert '\ b' in' (? = \ P {Word}) 'und nach deinem Vorschlag in negative Lookahead' (?! \ p {Word}) ' Diese Lookaheads funktionieren nicht so wie '\ b' und ich verstehe nicht wieso? –

5

sollten Sie negativ lookarounds werden:

(?<!\p{Word})(\p{Word}+)(?!\p{Word}) 

Die positiven lookarounds am Anfang oder Ende des Strings scheitern, weil sie ein nicht-Wort-Zeichen vorhanden sein müssen. Die negativen Blickwinkel funktionieren in beiden Fällen.

+0

Ist das nicht so wie '\ b (\ w +) \ b' zu schreiben? – tchrist

+0

Er vermasselt Dinge mit dem ekligen/kaputten 'use locale'; Sehen Sie [diese Antwort] (http://stackoverflow.com/a/15036072/471272) für die korrekte Ausführung von Locale-Dateien in Perl. Auf diese Weise können Sie auch normale Regex-Dinge verwenden. – tchrist

Verwandte Themen