2012-11-03 26 views
7

Tôi đang làm việc trên một dự án liên quan đến dữ liệu bằng tiếng nước ngoài. Kịch bản Perl của tôi đã hoạt động tốt.Tại sao chương trình Perl của tôi không thành công với mã hóa Tie :: File và Unicode/UTF-8?

Sau đó tôi muốn sử dụng Tie :: Tệp, vì đây là một khái niệm gọn gàng (và tiết kiệm thời gian và mã hóa).

Có vẻ như Tie: Tệp bị lỗi theo Unicode/UTF-8 (trừ khi tôi thiếu gì đó).

Dưới đây là một chương trình trong đó mô tả các vấn đề: (Các dữ liệu là một sự pha trộn của tiếng Anh, tiếng Hy Lạp và tiếng Hebrew):

use strict; 
use warnings; 
use 5.014; 
use Win32::Console; 
use autodie; 
use warnings qw< FATAL utf8 >; 
use Carp; 
use Carp::Always; 
use utf8; 
use feature  qw< unicode_strings>; 
use charnames  qw< :full>; 
use Tie::File; 

my ($i); 
my ($FileName); 
my (@Tied); 
binmode STDOUT, ':unix:utf8'; 
binmode STDERR, ':unix:utf8'; 
binmode $DB::OUT, ':unix:utf8' if $DB::OUT; # for the debugger 
Win32::Console::OutputCP(65001);   # Set the console code page to UTF8 

$FileName = 'E:\\My Documents\\Technical\\Perl\\Eclipse workspace\\Work\\'. 
     'Tie File test res.txt'; 
tie @Tied, 'Tie::File', $FileName, recsep => "\x0D\x0A", discipline => ':encoding(utf8)' 
      or confess 'tie @Tied failed'; 
$i =0; 
while (<DATA>) { 
    chomp; 
    $Tied[$i] = $_; 
    ++$i; 
} # end while (<DATA>) 
$i =0; 
foreach (@Tied) { 
    say "$i $Tied[$i]"; 
    ++$i; 
} # end foreach (@Tied) 
untie $FileName; 
__DATA__ 
τι κάνετε; 
πάρτε το ή αφήστε το 
שלום חברים 
abc לא כןכן efg 
מתי ולאן This is it 
מעכשיו לעכשיו 
Σήμερα είναι Τρίτη 
Θέλω να φάω 
τι κάνετε; 
שורה מס' 5 

này tạo ra một thác lớn cảnh báo: đây là một số:

utf8 "\xCE" does not map to Unicode at F:/Win7programs/Dwimperl/perl/lib/Tie/File.pm line 917 
     Tie::File::_read_record('Tie::File=HASH(0x24cb72c)') called at F:/Win7programs/Dwimper 
l/perl/lib/Tie/File.pm line 175 
     Tie::File::_fetch('Tie::File=HASH(0x24cb72c)', 0) called at F:/Win7programs/Dwimperl/p 
erl/lib/Tie/File.pm line 210 
     Tie::File::STORE('Tie::File=HASH(0x24cb72c)', 0, 'τι κάνετε;') called at tie file test 
.pl line 31 
utf8 "\xCF" does not map to Unicode at F:/Win7programs/Dwimperl/perl/lib/Tie/File.pm line 917 
     Tie::File::_read_record('Tie::File=HASH(0x24cb72c)') called at F:/Win7programs/Dwimper 
l/perl/lib/Tie/File.pm line 175 
     Tie::File::_fetch('Tie::File=HASH(0x24cb72c)', 0) called at F:/Win7programs/Dwimperl/p 
erl/lib/Tie/File.pm line 210 
     Tie::File::STORE('Tie::File=HASH(0x24cb72c)', 0, 'τι κάνετε;') called at tie file test 
.pl line 31 
utf8 "\xD7" does not map to Unicode at F:/Win7programs/Dwimperl/perl/lib/Tie/File.pm line 917 
     Tie::File::_read_record('Tie::File=HASH(0x24cb72c)') called at F:/Win7programs/Dwimper 
l/perl/lib/Tie/File.pm line 175 
     Tie::File::_fetch('Tie::File=HASH(0x24cb72c)', 0) called at F:/Win7programs/Dwimperl/p 
erl/lib/Tie/File.pm line 210 
     Tie::File::STORE('Tie::File=HASH(0x24cb72c)', 0, 'τι κάνετε;') called at tie file test 
.pl line 31 
utf8 "\xD7" does not map to Unicode at F:/Win7programs/Dwimperl/perl/lib/Tie/File.pm line 917 
     Tie::File::_read_record('Tie::File=HASH(0x24cb72c)') called at F:/Win7programs/Dwimper 
l/perl/lib/Tie/File.pm line 175 
     Tie::File::_fetch('Tie::File=HASH(0x24cb72c)', 0) called at F:/Win7programs/Dwimperl/p 
erl/lib/Tie/File.pm line 210 
     Tie::File::STORE('Tie::File=HASH(0x24cb72c)', 0, 'τι κάνετε;') called at tie file test 
.pl line 31 

Sau đó, nó in này trên STDOUT:

0 τι κάνετε; 
1 πάρτε το ή αφήστε το 
2 שלום חברים 
3 abc לא כןכן efg 
4 מתי ולאן This is it 
5 מעכשיו לעכשיו 
6 Σήμερα είναι Τρίτη 
7 Θέλω να φάω 
8 τι κάνετε; 
9 שורה מס' 5 
10 
11 
12 
13 
14 \xA4\xΘέλω\xA8\x 

15 
16 
17 
18 

19 

Lưu ý rằng 10 dòng đầu tiên là OK, nhưng dòng 10 đến 19 đến từ hư không !? Ngoài ra, đầu ra của tệp được đính kèm chứa dữ liệu bị hỏng:

τι κάνϏN͏Ŏՠτήστε של חברءbc לؗܗࠗܗߠeמתולאן This is מעיו לעכ؎Ďώݎ֏ναι ΤρΘέώގѠφϏŎ٠κτε;שרה מס' 



\xA4\xΘέλω\xA8\x 

Điều gì đó rất sai ở đây. Hoặc là tôi thiếu một cái gì đó, hoặc Tie: File không thể đối phó với Unicode/UTF-8? Tôi đang chạy Strawberry Perl 5.14 trên hệ thống Windows 7.

Nhiều TIA - Helen

Lưu ý: đăng trên http://perlmonks.org/?node_id=1002104, quá

+0

Sự cố (có khả năng nhất) có thể là dữ liệu của bạn không được mã hóa đúng để bắt đầu. Đó là những gì các cảnh báo đang nói cho bạn anyway. – Mat

+0

@Mat: dữ liệu được mã hóa chính xác. Giống như tôi đã nói ở trên, mà không cần Tie :: File tất cả mọi thứ đang làm việc tốt. Cũng lưu ý rằng bản in trên STDOUT là tốt (đối với 9 dòng đầu tiên) –

+0

Trình soạn thảo nào bạn đang sử dụng và bạn có chắc là nó đang lưu tệp nguồn dưới dạng UTF-8 không? (Và bạn không cần phải xác định 'tính năng sử dụng qw ;' vì tính năng này được kích hoạt bằng 'sử dụng v5.14;'.) – titanofold

Trả lời

3

Các gợi ý tôi sẽ làm phụ thuộc rất nhiều vào các vấn đề thực tế mà bạn đang cố gắng để giải quyết. Nhìn vào câu hỏi này trong cách ly, tôi sẽ không có quá nhiều mã hóa/giải mã 'ma thuật' và chỉ đơn giản là sử dụng byte thô (vì tập lệnh không cần biết bất kỳ điều gì về các ký tự cho tác vụ này). Phần dưới đây tạo ra kết quả mong đợi cho đầu vào và đầu ra mà bạn mô tả.

use v5.014; 
use warnings; 
use autodie; 

use Carp::Always; 
use Tie::File; 

my $file_in = 'test_in.txt'; 
my $file_out = 'test_tie.txt'; 

unlink $file_out; 

tie my @tied, 'Tie::File', $file_out, recsep => "\x0D\x0A" or die 'tie failed'; 

open my $fh, '<', $file_in; 
while (my $line = <$fh>) { 
    chomp $line; 
    push @tied, $line; 
} 
close $fh; 

my $i = 0; 
say $i++ . ' ' . $_ foreach @tied; 

untie @tied; 

Tuy nhiên, có thể bạn muốn làm một số xử lý trên mà văn bản ở giữa. Trong trường hợp đó bạn muốn ký tự được giải mã. Như tôi đã nhìn thấy nó có hai tùy chọn:

  1. Mã hóa thủ công trước khi bàn giao tắt đến các mảng gắn
  2. Hình ra gì vấn đề là với Tie :: Tập tin

Số 2 có lẽ là phi tầm thường - một cách nhanh chóng quét của Tie :: File nguồn và có vẻ như nó giả định nó sẽ luôn luôn được cho byte. Phần duy nhất mà bạn có thể ảnh hưởng dường như là binmode tại https://metacpan.org/source/TODDR/Tie-File-0.98/lib/Tie/File.pm#L111 - mà bạn đang thực hiện.

Tie :: Tập tin hiện rất nhiều seek cuộc gọi, perldoc có này để nói về tìm kiếm (http://perldoc.perl.org/functions/seek.html):

Lưu ý theo byte: ngay cả khi filehandle đã được thiết lập để hoạt động trên nhân vật (ví dụ bằng cách sử dụng lớp mở: encoding (utf8)), tell() sẽ trả về các offset byte, chứ không phải bù đắp ký tự (vì việc thực hiện sẽ trả về tìm kiếm() và tell() khá chậm).

Vì vậy, có vẻ như Tie :: Tệp đang sử dụng độ dài ký tự để xác định bù trừ byte của bản ghi. Do đó nó có thể kết thúc ở giữa một chuỗi ký tự UTF-8. Điều này có vẻ là nguyên nhân gây ra lỗi của bạn.

Nói chung, tôi tránh xa binmode khi dựa vào mô-đun bên ngoài để đọc/ghi vào bộ xử lý tệp - trong trường hợp này tôi sẽ có một cuộc gọi phụ đơn giản Encode::encode('UTF-8', ...) trên dữ liệu trước khi đẩy lên @tied.

Ngoại lệ là nơi tài liệu của mô-đun nêu rõ hành vi của dữ liệu được giải mã hoặc nếu nguồn đủ đơn giản để tôi xác minh hành vi.

+1

cảm ơn bạn, điều này rất hữu ích, tôi quyết định đánh dấu nó được chấp nhận. Mặc dù, tôi đã kết thúc bằng cách sử dụng tie với DB_file và DBM_filter, như đề xuất của remiah ở đây: http://perlmonks.org/?node_id=1002394. điều đó thực hiện công việc. –