2011-10-21 26 views
0

我有一個合理大小的平面文件數據庫,主要以8859格式保存,通過Web表單(使用Perl腳本)收集。直到最近,我在共同1252個字符(彎引號,頓號等)一套簡單的正則表達式的進行談判:在Perl中以utf8模式打開的ASCII文本文件中過濾微軟1252個字符

$line=~s/\x91/\&\#8216\;/g; # smart apostrophe left 
$line=~s/\x92/\&\#8217\;/g; # smart apostrophe right 

...等

但是自從我決定我該走了Unicode,並且已經將所有腳本轉換爲讀入並輸出utf8(這對所有新材料都適用),這些(現有的)1252字符的正則表達式不再有效,我的Perl html輸出字面上輸出了4個字符:'\ x92'和'\ x93'等(至少這是它在utf8模式下如何在瀏覽器上顯示,下載(ftp不是http),並在文本編輯器(文本板)中打開它不同,單個未定義字符仍然存在,輸出文件在Firefox默認(沒有內容類型標題)8859模式呈現正確的字符)。

在腳本的開始新的UTF8編譯指示是:

使用CGI QW(-utf8); 使用open IO =>':utf8';

據我所知,這是由於utf8模式使字符雙字節而不是單字節,並適用於0x80到0xff範圍內的字符,閱讀了有關這個wikibooks的文章,但我不聰明至於如何過濾它們。理想情況下,我知道我應該以utf8模式重新保存所有文檔(因爲平面文件數據庫現在包含8859和utf8的混合),但是如果我要這樣做,首先我需要某種過濾器。

對於內部的2字節存儲空間我可能是錯誤的,因爲它似乎意味着Perl根據各種情況處理的東西非常不同。

如果有人能給我提供正則表達式的解決方案,我將非常感激。或者其他一些方法。我曾經多次嘗試過,並且在黑客攻擊方面一直在嘲笑我的頭髮數週。只有大約6 1252個字符通常需要替換,並且使用過濾器方法,我可以在utf8中重新保存整個flippin並忘記曾經存在過1252個字符...

+0

哦......我不能簡單的復位在8859和過濾器打開文件以來,DB現在包含UTF8和8859.哎呦。 – Beeblbrox

回答

1

Ikegami already mentionedEncoding::FixLatin模塊。

另一種方式來做到這一點,如果你知道,每個字符串將要麼 UTF-8或CP1252,而不是兩者的混合,是它讀成一個二進制字符串做:

unless (utf8::decode($string)) { 
    require Encode; 
    $string = Encode::decode(cp1252 => $string); 
} 

與Encoding :: FixLatin相比,這有兩個小優勢:將CP1252文本誤解爲UTF-8(因爲整個字符串必須是有效的UTF-8)的機率較低,以及用某些其他後備編碼替換CP1252的可能性。相應的缺點是,由於某些其他原因,這些代碼可能會回退到CP1252的字符串上,這些字符串不是完全有效的UTF-8,例如因爲它們在多字節字符中間被截斷。

+0

這真是太棒了我認爲這可能是我需要的解決方案 - 它從來沒有發生過我一行一行地解碼,而不是整個文件作爲一個或另一個。因此,這將只留下有效的utf8字符串,並允許我使用正則表達式使用正則表達式來處理包含非UTF字符的字符串? – Beeblbrox

+0

...我不知道有關Fix Fix模塊,它似乎正在做我正在尋找的東西,再次感謝 – Beeblbrox

+0

這兩種解決方案(除了字符集錯誤識別的小機會)都將所有輸入字符串轉換爲Perl Unicode字符串(可能在內部表示爲UTF-8,但你真的不應該在意),而不管它們是用UTF-8編碼還是用CP1252編碼。所以你不需要在上面做任何額外的「正則表達式」。 (即使你這樣做,它也可能不會造成任何傷害,因爲這些正則表達式永遠不會匹配有效的可打印Unicode字符串。) –

0

您是否重新編碼了數據文件?如果不是,將它們打開爲UTF-8將不起作用。你可以簡單地打開它們作爲

open $filehandle, '<:encoding(cp1252)', $filename or die ...; 

和一切(tm)應該工作。

如果您進行了重新編碼,某些東西似乎出了問題,您需要分析它是什麼並進行修復。我建議使用hexdump來找出文件中的實際內容。文本控制檯和編輯有時對你說謊,hexdump永遠不會說謊。

+0

Hexdump在1252個字符如預期的地方顯示91,92,93。爲什麼在這種情況下,正則表達式匹配/ \ x91// \ x92 /等失敗。注意到我的文本編輯器會報告新創建的文件,其中1252個字符爲ANSI,而沒有utf8的文件 - 我期望Perl utf8 IO層將所有文件強制爲utf8。 我無法重新編碼,直到我找到一種方法來過濾舊版8859文件中的1252個字符,因爲它們都與新的utf8文件混合在一起 – Beeblbrox

1

您還可以使用Encode.pmfallback的支持。

use Encode qw[decode]; 

my $octets = "\x91 Foo \xE2\x98\xBA \x92"; 
my $string = decode('UTF-8', $octets, sub { 
    my ($ordinal) = @_; 
    return decode('Windows-1252', pack 'C', $ordinal); 
}); 

printf "<%s>\n", 
    join ' ', map { sprintf 'U+%.4X', ord $_ } split //, $string; 

輸出:

<U+2018 U+0020 U+0046 U+006F U+006F U+0020 U+263A U+0020 U+2019> 
相關問題