2010-02-25 1 views
7

Ich habe ein seltsames Perl-Verhalten festgestellt: Die Verwendung einer Posix-Zeichenklasse in einem Regexp ändert die Sortierreihenfolge für die resultierenden Strings vollständig.Warum verwendet eine POSIX-Zeichenklasse in meinem Regex-Muster unerwartete Ergebnisse?

Hier ist mein Testprogramm:

sub namecmp($a,$b) { 
    $a=~/([:alpha:]*)/; 
    # $a=~/([a-z]*)/; 
    $aword= $1; 

    $b=~/([:alpha:]*)/; 
    # $b=~/([a-z]*)/; 
    $bword= $1; 
    return $aword cmp $bword; 
}; 

$_= <>; 
@names= sort namecmp split; 
print join(" ", @names), "\n"; 

Wenn Sie die kommentierten-out regexp die Verwendung von [a-z] zu ändern, erhalten Sie die normale, lexikographische Sortierreihenfolge. Doch die Posix [: alpha:] Zeichenklasse liefert einige seltsame-ass Sortierreihenfolge wie folgt:

$test_normal 
aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cbb 
aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cbb 

$test_posix 
aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cbb 
baa bab bac bba bbb bbc bca bcb bcc caa cbb aba abb abc aca acb acc aab aac aaa 

Meine beste Vermutung ist, dass die Posix Zeichenklasse eine Art von locale Sachen aktivierend habe ich noch nie gehört von und fragte nicht nach. Ich vermute die logische Reaktion auf "Doktor, Doktor, es tut weh, wenn ich das mache diese!" ist, "gut, tue nicht das, dann!".

Aber kann mir jemand sagen, was hier passiert und warum? Ich benutze Perl 5.10, aber ich glaube, es funktioniert auch unter Perl 5.8.

+7

Es gibt einige Programmierer, die, wenn sie mit Ausgang konfrontiert sie nicht erwarteten, dessen erster Instinkt ist * zu fragen * Was mache ich falsch? ** und finde es heraus. Dann gibt es diejenigen, deren erster Instinkt es ist zu fragen: Was macht der Compiler/Interpreter falsch? In der zweiten Kategorie fällt es schwerer, guten Code zu schreiben. –

+4

Der häufigste Grund, warum Menschen unerwartetes Verhalten bekommen, ist, dass sie das Falsche erwarten. –

Antwort

13

Die Zeichenklasse [:alpha:] steht für Alpha-Zeichen in regulären Ausdrücken von Perl, aber die eckigen Klammern bedeuten nicht, was sie normalerweise in regulären Ausdrücken tun. Sie müssen also:

$a=~/([[:alpha:]]*)/; 

Das in perlre erwähnt wird:

Die POSIX-Zeichenklasse Syntax

[:class:] 

ist ebenfalls verfügbar. Beachten Sie, dass die Klammern [ und ] Literal sind; Sie müssen immer innerhalb eines Zeichenklassenausdrucks verwendet werden.

# this is correct: 
$string =~ /[[:alpha:]]/; 

# this is not, and will generate a warning: 
$string =~ /[:alpha:]/; 
6

Da Perl nicht POSIX Zeichenklassen in dieser Form unterstützen. (Verwenden Sie [[:alpha:]]. Siehe @Greg's answer)

So

[:alpha:] 

wird als ein Zeichenklasse interpretiert bestehend aus den Zeichen "a", "h", "l", "p" und ":".

Nun, für Strings, die nichts tun, enthalten [ahlp:]am Anfang (wegen der *), z.B. "baa" gibt die Übereinstimmung eine leere Zeichenfolge zurück. Eine leere Saite ist natürlich kleiner als jede andere Saite, daher werden sie am Anfang arrangiert.

8

Was Sie schreiben, ist Perl nicht durch irgendeine Vorstellungskraft. Sie können damit durchkommen, weil Sie warnings ausgeschaltet haben.Wenn Sie Warnungen verwendet hätte, hätte perl gesagt,

POSIX syntax [: :] belongs inside character classes in regex; marked by <-- HERE in m/([:alpha:] <-- HERE *)/ at j.pl line 4.

POSIX syntax [: :] belongs inside character classes in regex; marked by <-- HERE in m/([:alpha:] <-- HERE *)/ at j.pl line 8.

dass Stellen Sie sich vor! Jetzt

, perl hätte auch gesagt, Sie:

Illegal character in prototype for main::namecmp : $a,$b at j.pl line 3.

da, Perl ist nicht C. Perl nicht Funktionsprototypen der Art haben Sie scheinen zu nutzen zu versuchen.

Eine bessere Möglichkeit, die genau die gleiche Funktionalität, in Perl diese Zeit mit dem Schreiben ist:

use warnings; use strict; 

sub namecmp { 
    my ($aword) = $a =~ /([[:alpha:]]*)/; 
    my ($bword) = $b =~ /([[:alpha:]]*)/; 
    return $aword cmp $bword; 
} 

print join(' ', sort namecmp split ' ', scalar <>), "\n"; 
+0

Meh, es hat meistens funktioniert. Beim Lesen der Dokumente ist es überraschend, dass mein "Prototyp" überhaupt funktioniert hat. Allerdings muss ich Ihre ursprüngliche Behauptung in Frage stellen: Was ich geschrieben habe, war * perl, per definitionem, weil es akzeptiert wurde und ohne Beanstandung lief. – comingstorm