2017-03-03 71 views
0

當我嘗試在正則表達式測試中使用下面的簡單替換時,它工作正常。但是,當我在Perl代碼中使用它,在ù不被取代:爲什麼特殊字符「ù」在替換中被替換?

#!/usr/bin/perl 

use 5.010; 
use strict; 
use warnings; 

use File::Slurp; 

my $file = 'test.html'; 
my $str = read_file($file); 

$str =~ s/ù/u/g; 

write_file("out/$file", $str); 

這裏是我想換一個示例文本:

ùmbrella ùnder ùùùùù ùtters 

umbrella under uuuuu utters 

任何幫助並建議高度讚賞。

+0

@siam的感謝!我不得不添加「使用utf8;」頂部還有 – Xavia

+2

@siam:你在想Python!這只是Perl中的一個評論。 – Borodin

+4

[檔案::嘟嘟地喝壞了,錯了。(http://blogs.perl.org/users/leon_timmermans/2015/08/fileslurp-is-broken-and-wrong.html) – ThisSuitIsBlackNot

回答

7

如果您的腳本和輸入文件使用相同的編碼進行編碼,則您的代碼將按原樣運行。

$ cat>test.html 
ùmbrella ùnder ùùùùù ùtters 

$ perl a.pl 

$ cat out/test.html 
umbrella under uuuuu utters 

你的程序是越野車,雖然。假設我們正在談論UTF-8。Perl的實際看到

$str =~ s/\xC3\xB9/u/g; 

雖然這並沒有那麼糟糕,想象一下,如果你有

$str =~ s/[ùú]/u/g; 

的Perl會認爲這是

$str =~ s/[\xC3\xB9\xC3\xBA]/u/g; 

這會變成ùC3 B9)爲uuéC3 A9)插入u<garbage>

對於Perl來識別程序中的任何非ASCII字符,您必須確保程序文件使用UTF-8進行編碼,並且您需要在文件頂部添加use utf8;。隨着use utf8;,Perl中看到

$str =~ s/[ùú]/u/g; 

或者說

$str =~ s/[\xF9\xFA]/u/g; # F9 and FA are the Unicode Code Points for ù and ú 

然而,增加use utf8;只是解決方案的一半。我們改變了Perl看到正則表達式的方式,但我們沒有改變$str,所以它們不可能再匹配。我們比較ùC3 B9)與ùF9)Unicode代碼點編碼

始終解碼你的投入。始終對輸出進行編碼。

我們已經解碼的一個輸入(程序本身)。現在我們需要對文件的內容做同樣的事情。

同樣,我們需要編碼輸出。這不僅包括文件的內容,還包括輸出到STDERR的警告。

大部分由

use open ':std', ':encoding(UTF-8)'; 

做它增加了一個編碼層STDIN,STDOUT和STDERR,並設置編譯的詞法範圍內打開文件的默認編碼層。

#!/usr/bin/perl 

use utf8; 
use open ':std', ':encoding(UTF-8)'; 

use strict; 
use warnings; 

my $in_qfn = 'test.html'; 
my $out_qfn = 'out/test.html'; 

# :encoding(UTF-8) is added by "use open". 
open(my $in_fh, '<', $in_qfn) or die("Can't open \"$in_qfn\": $!\n"); 
open(my $out_fh, '>', $out_qfn) or die("Can't create \"$out_qfn\": $!\n"); 

while (<$in_fh>) { 
    s/[ùú]/u/g; 
    print($out_fh $_); 
} 

如果使用文件::嘟嘟地喝,你需要告訴它的文件進行解碼(或自己對其進行解碼),因爲它open不在use open的範圍。

#!/usr/bin/perl 

use utf8; 
use open ':std', ':encoding(UTF-8)'; 

use strict; 
use warnings; 

use File::Slurp qw(read_file write_file); 

my $in_qfn = 'test.html'; 
my $out_qfn = 'out/test.html'; 

my $file = read_file($in_qfn, binmode => ':encoding(UTF-8)'); 

$file =~ s/[ùú]/u/g; 

write_file($out_qfn, { binmode => ':encoding(UTF-8)' }, $file); 
0

解決方案:

#!/usr/bin/perl 

use 5.010; 
use strict; 
use utf8; # <-- Added this 
use warnings; 
use File::Slurp; 
my $file = test.html; my $str; 

$str = read_file($file); 
$str =~ s/ù/u/g; 

write_file("out/$file",$str); 
+1

的'使用utf8'編譯告訴你_source代碼_文件一起保存UTF8編碼的Perl。 – simbabque

+1

換句話說,您仍然需要告訴Perl將輸出編碼爲UTF-8。 – ThisSuitIsBlackNot

+1

修改的程序保持輸入文件不變。雖然添加'use utf8;'是正確的,但它只是解決方案的一部分。看到我的答案更多。 – ikegami

0

我懷疑有幾個問題在這裏。首先,你使用的是File :: Slurp,而你沒有告訴它數據是UTF編碼的。這意味着您的雙字節「ù」字符將被解釋爲兩個單字節字符。其次,你的代碼中有一個字面的「ù」,但你不會告訴Perl把你的源代碼解釋爲UTF8,所以你可能會有單字節的ISO-8859表示。

輸入字符串中的兩個單字節字符與源代碼中的單字節字符不匹配,因此替換不起作用。

您需要a)告訴Perl您的源代碼是UTF8並且b)正確處理輸入和輸出編碼的解碼。我建議扔掉File :: Slurp並自己做。

我也建議不要sl files文件,但只要有可能就一次處理它們。

#!/usr/bin/perl 

use 5.010; 
use strict; 
use warnings; 
use utf8; 

my $file = 'test.html'; 
open my $in_fh, '<:utf8', $file or die $!; 
open my $out_fh, '>:utf8', "out/$file" or die $!; 

while (<$in_fh>) { 
    s/ù/u/g; 

    print $out_fh $_; 
} 

更新:這裏是一個非常簡單的子程序,我用它來獲取有關字符串的信息。

sub string_chars { 
    say join ':', map { ord } split //, $_[0]; 
} 

如果您添加到您的代碼,並把它傳遞「U」 - 你的輸出「​​249」(這是在ISO-8859-1「U」碼點)。如果你傳遞你的$str值,您可以:

'195:185:109:98:114:101:108:108:97:32:195:185:110:100:101:114:32:195:185:195:185:195:185:195:185:195:185:32:195:185:116:116:101:114:115:10' 

重複的「195:185」是UTF8「U」的兩個字節表示。

+1

這裏有一個更簡單的例子:'sub string_chars {說sprintf「%vX」,$ _ [0]}'(獎金,數字以十六進制表示!) – ikegami