2012-04-12 6 views
4

Ich möchte Text ausgeben, der aus einer Datenbank in UTF-8 in eine Datei in CP1252 (alias Latin1) kommt. Um das zu tun, verwende ich Text :: Iconv, das funktioniert , es sei denn die Zeichen in der zu konvertierenden Zeichenkette sind zerlegt. Ob das ein Fehler der iconv-Bibliothek ist oder nicht, ist eine Frage, die ich gestellt habe und für die die Antwort nicht offensichtlich ist. Da iconv auf zusammengesetzte Zeichen gut funktioniert, ist die Lösung für meine Saiten zu normalisieren erste, aber ich kann es nicht zu verwalten scheinen zu tun:Normalisierung von Unicode-Zeichenfolgen in Perl

use strict; 
use warnings; 
use Data::Hexdumper qw(hexdump); 
use Unicode::Normalize; 

my $v = "É"; # E=U+0045 followed by combining ´=U+0301. UTF-8: 0x45CC81 
print "'$v'\n"; 
print hexdump($v); 

my $n = NFC $v; # should be É=U+00C9. UTF-8: 0xC389 
print "'$n'\n"; 
print hexdump($n); 

Aber hier ist die Ausgabe erhalte ich:

'É' 
    0x0000 : 45 CC 81 00 00 00 00 00 00 00 00 00 00 00 00 00 : E............... 
'É' 
    0x0000 : 45 CC 81 00 00 00 00 00 00 00 00 00 00 00 00 00 : E............... 

Mit anderen Worten, die NFC-Funktion (convert to Normalization Form C) hat nichts bewirkt. Habe ich etwas vergessen? Ich benutze Perl 5.12.3 unter Mac OS X 10.7.3.

Dies ist nur der Anfang meiner Probleme mit der Textverarbeitung in Perl, die ich nicht erwartet hätte. Danke für jede Hilfe.

Edit: einige Kontext scheint nützlich. Natürlich kann meinem erfundenen Beispiel viel geholfen werden durch eine use utf8 Klausel. Mein aktuelles Problem ist natürlich nicht mit String-Literalen.

Zuerst erkenne ich aus den Antworten, dass ich viel über Perl lernen muss. Tatsächlich bin ich kein Perl-Programmierer, sondern ein Objective-C/Cocoa-Programmierer, bei dem diese Probleme gar nicht auftauchen.

Also begann ich zu lesen, und ich finde die Perl-Dokumentation ziemlich verwirrend, zum Beispiel, wenn es über native Codierung anders als UTF-8 spricht. Was es nicht sagt, ist, wie man das für die Mac OS X Plattform übersetzt, wo UTF-8 die native Kodierung ist.

In jedem Fall der Kontext ist mein Programm produziert Ausgabe in Textdateien, die verschiedene Formate (einschließlich CSV und Unimarc) und mehrere Codierungen (die vier häufigsten sind UTF-8, CP1252, MARC8 und ISO-5426) . Benutzerwahl.

Er erhält seine Eingabe von einer Datenbank (derzeit mySQL oder SQL Server), wo Daten normalerweise in UTF-8 (aber manchmal in CP1252) codiert sind.

+1

Warum die downvote? Die Frage ist klar formuliert und hat Beispielcode. Offensichtlich hat das OP mit Unicode zu kämpfen - so wie es viele Leute in vielen Sprachen tun. Für Anfänger sehen Sie [perlunitut] (http://perldoc.perl.org/perlunitut.html) und [perlunifaq] (http://perldoc.perl.org/perlunifaq.html). – Lumi

+0

CP1252 ist _nicht_ das gleiche wie Latin-1. Latin-1 ist ISO-8859-1. Einzelheiten zu den Unterschieden finden Sie unter http://en.wikipedia.org/wiki/Windows-1252. –

+0

Sie haben Recht, dass ISO-8859-1 und CP1252 nicht genau identisch sind. Ob Latin1 ISO-8859-1 oder CP1252 bezeichnet, bin ich nicht so sicher. Ich habe beides gesehen. Es ist ein Red Herring hier aber –

Antwort

2

Ohoho, in meiner ursprünglichen Nachricht unten habe ich das Wesentliche über zerlegte Charaktere verpasst. Gerade versucht die folgenden für Ihr flippiges Brief É:

perl -C3 -lwe '$_ = qq(\x45\x{0301}); print' 

funktioniert gut für 5.10.1 auf Cygwin.

Ich könnte etwas vermissen ... aber es scheint, dass Sie hier einen sehr niedrigen Ansatz zur Textverarbeitung nehmen.

Zuerst sagen Sie, dass Sie die Daten aus einer Datenbank erhalten, wo die Codierung UTF-8 ist. Das ist gut. Wenn der Treiber die Codierung nicht automatisch erkennt, sollten Sie darüber nachdenken. Sie sagen nicht, welche Datenbank Sie verwenden, aber Sie werden wahrscheinlich etwas finden, indem Sie die DBI manual und vielleicht auch den Treiber (DBD::*), den Sie für "utf" oder "encoding" verwenden.

Dann, bei einer geeigneten Codierung Einstellung für die Datenbank-Verbindung, sollte Ihr Text in Perl als, wie, Text ankommen. Nur Text, keine Kodierung. Wie zum Beispiel in Java. Ja, es gibt eine interne Codierung für die Zeichenfolge, aber Sie sollten sich nicht darum kümmern, was es ist.

Wenn dann in die Datei zu schreiben, verwenden Sie einfach den folgenden Code:

open my $fh, '>:encoding(CP1252)', $filename or die "open $filename: $!"; 
print $fh $text_from_db; 
close $fh; 

Und das sollte alles, was Sie tun müssen, sein.

Gibt es einen bestimmten Grund, warum Sie Text::Iconv verwenden? Ich denke, Sie sollten mit der Encode module bekommen. Aber für den in Ihrer Frage beschriebenen Job brauchen Sie das nicht einmal.

Sie verwenden Perl 5.12.3, also sollte die Unicode-Verarbeitung für alle, aber seltsame Grenzfälle funktionieren. Die Probleme bestehen hauptsächlich mit Perls, die vor einigen Jahren entstanden sind. Die 5.12 und 5.10 Serie sollte in Ordnung sein, denke ich. Habe die Details nicht griffbereit, aber ich musste einmal Unicode-Arbeit mit einem alten 5.6.1 machen, dessen Unicode-Unterstützung experimentell war, und es war schrecklich.

+0

Ich merke, ich muss hier viel über Perl lernen. Meine bisherigen Experimente sind jedoch nicht ermutigend. Ein einfaches Beispiel: Eine der Kodierungen, die ich in eine Datei ausgeben muss, ist ISO 5426. Allerdings öffnet meine Ausgabedatei mit dem Dateimodus '':: encoding (ISO5426) '' mit Fehler 'Kann Kodierung" ISO5426 "nicht finden'. Ich bin nicht überrascht, dass Perl von dieser Kodierung nichts weiß. Aber was soll ich jetzt tun, um mit dieser Codierung umzugehen? –

+0

Dito für Encode: weiß es nicht über ISO 5426 –

+0

Die Datenbank, mit der ich spreche, ist Microsoft SQL Server über den Sybase-Treiber. Ich habe nicht herausgefunden, wie man es über die Kodierung "erzählt". –

3

Was Ihnen fehlt, ist, dass $v auf die ‚E‘ Zeichen gesetzt und die utf-8-Codierung der Kombination Akut diakritische, nicht die Kombination von akuten diakritischen selbst. Um dies zu beheben, werden Sie use utf8 so etwas wie

1) tun wollen - verursacht Perl automatisch utf-8 decode Ihren Quellcode

2) ausdrücklich $v

my $v = chr(0x45) . chr(0xCC) . chr(0x81); 
use Encode; 
$v = Encode::decode('utf-8', $v); # now $v is 0x45 0x301 

3) dekodieren verwenden chr ausdrücklich $v zu setzen, was Sie meinen

my $v = chr(0x45) . chr(0x301); 

ich nicht wirklich empfehlen würde Nummer (2), aber ich schließe ein, um zu veranschaulichen, was mit Ihrem Skript passiert, wenn Sie nicht use utf8 tun.

2

Sie haben Perl nicht gesagt, dass Ihre Datei UTF-8 ist.

Sie haben Perl nicht mitgeteilt, wie Sie Ihre Ausgabe codieren sollen.

use strict; 
use warnings; 

use utf8;        # UTF-8 source. 
use open ':std', ':encoding(UTF-8)'; # UTF-8 output. Don't forget to chcp 65001.. 

use Data::Dumper  qw(Dumper); 
use Unicode::Normalize qw(NFC); 

local $Data::Dumper::Useqq = 1;  
local $Data::Dumper::Terse = 1; 
local $Data::Dumper::Indent = 0; 

my $v = "\x{0045}\x{0301}"; 
print "'$v'\n"; 
print Dumper($v), "\n"; 

my $n = NFC $v; 
print "'$n'\n"; 
print Dumper($n), "\n"; 

(Ich hatte Probleme Hexdumper Laden.)