2016-10-23 1 views
11

Ich möchte diakritische Zeichen in einigen Zeichenfolgen entfernen. tr/// sollte den Job erledigen, scheitert aber (siehe unten). Ich dachte, ich hätte ein Kodierungs-/Dekodierungsproblem, aber ich habe festgestellt, dass s/// so funktioniert, wie ich es erwarte. Könnte jemand erklären warum? HierPerl: tr /// tut nicht, was ich erwarte, während s /// ist

ist ein Beispiel für Ergebnisse, die ich erhalten:

my $str1 = 'èîü'; 
my $str2 = $str1; 
$str1 =~ tr/î/i/; 
print "$str1\n"; # => i�iii� 
$str2 =~ s/î/i/; 
print "$str2\n"; # => èiü 

Beachten Sie, dass tr/// auch die erste und dritte Zeichen der Zeichenfolge geändert, nicht nur die mittleren.

Edit: Ich benutze Ubuntu 16.04 mit Mate Desktop-Umgebung.

Antwort

18

Wenn Sie nicht use utf8; haben, aber Sie den Code mit einem utf8 Texteditor anzeigen, sehen Sie es nicht so, wie Perl es sieht. Sie denken, Sie haben ein einzelnes Zeichen in der linken Hälfte Ihrer s/// und tr///, aber weil es mehrere Bytes ist, sieht Perl es als mehrere Zeichen.

Was denken Sie Perl sieht:

my $str1 = "\xE8\xEE\xFC"; 
my $str2 = $str1; 
$str1 =~ tr/\xEE/i/; 
print "$str1\n"; 
$str2 =~ s/\xEE/i/; 
print "$str2\n"; 

Welche Perl sieht tatsächlich:

my $str1 = "\xC3\xA8\xC3\xAE\xC3\xBC"; 
my $str2 = $str1; 
$str1 =~ tr/\xC3\xAE/i/; 
print "$str1\n"; 
$str2 =~ s/\xC3\xAE/i/; 
print "$str2\n"; 

Mit s///, da keiner der Charaktere regexp Operatoren sind, sind Sie nur eine Teil-Suche. Sie suchen nach einer Zeichenkette mit mehreren Zeichen. Und Sie finden es, weil das gleiche, was in Ihrem s/// passiert, auch in Ihren String-Literalen passiert: die Zeichen, die Sie denken, sind wirklich nicht, aber die Multi-Zeichen-Sequenz ist.

In tr/// auf der anderen Seite werden mehrere Zeichen nicht als eine Sequenz behandelt, sie werden als eine Menge behandelt. Jedes Zeichen (Byte) wird separat behandelt, wenn es gefunden wird. Und das bringt nicht die gewünschten Ergebnisse, weil das Ändern der einzelnen Bytes einer utf8-Zeichenfolge nie das ist, was Sie wollen.

Die Tatsache, dass Sie einfache ASCII-orientierte Teilstringsuche ausführen können, die nichts über utf8 wissen und das richtige Ergebnis für eine utf8-Zeichenfolge erhalten, wird im Gegensatz zu anderen Codierungen wie ucs2 als ein gutes Abwärtskompatibilitätsmerkmal von utf8 betrachtet/utf16 oder ucs4.


Die Lösung ist, die Quelle zu sagen, Perl ist durch Hinzufügen von use utf8; UTF-8 codiert werden. Sie müssen auch Ihre Ausgaben codieren, damit sie mit dem übereinstimmen, was Ihr Terminal erwartet.

use utf8;        # The source is encoded using UTF-8. 
use open ':std', ':encoding(UTF-8)'; # The terminal provides/expects UTF-8. 
my $str1 = 'èîü'; 
my $str2 = $str1; 
$str1 =~ tr/î/i/; 
print "$str1\n"; 
$str2 =~ s/î/i/; 
print "$str2\n"; 
3

Dies funktioniert wie für mich erwartet:

use v5.10; 
use utf8; 
use open qw/:std :utf8/; 

my $str1 = 'èîü'; 
my $str2 = $str1; 
$str1 =~ tr/î/i/; 
say $str1; # èiü 
$str2 =~ s/î/i/; 
say $str2; # èiü 

Die use utf8 Pragma ermöglicht UTF-8 für Literale im Quellcode, die use open Pragma schaltet STDOUT auf UTF-8.

+0

Es funktioniert auch für mich, danke. Irgendeine Idee, warum 'tr' scheint, diese Pragmas zu brauchen, während's' nicht? – Georg

+4

Ich wollte nur etwas über die Semantik von Zeichenketten und Bytestrings sagen, aber siehe @ Wumpus 'Antwort, ich denke, es erklärt das Problem viel besser. – zoul

+0

@ zoul, ich bin froh, dass du es nicht getan hast; Dies hat nichts mit den beiden internen Speicherformaten zu tun. – ikegami

Verwandte Themen