2017-02-07 43 views
3

我的問題是我有一個數據文件包含UTF-8,其中大部分是有效的,必須保留,但其中一些有隨機「垃圾​​」 UTF-8,即在0xf0 - 0xff的範圍內。十六進制的壞數據的例子可以看出以下一個腳本去掉文件中的UTF-8字符範圍

f4 80 80 ab f4 80 80 b6 f4 80 80 
a5 f4 80 80 a6 f4 80 80 83 f4 80 80 b6 f4 80 81 
84 f4 80 81 98 f4 80 81 87 f4 80 81 8c f4 

我試圖寫一個Perl腳本,將搜索和替換的第一個字節的範圍是0xf0 - 0xff字符。在this website代碼頁被列爲私人使用。

我現有的嘗試要麼什麼都不做,或者只能夠刪除多字節字符的第一個字節,如perl -CSD -pi.orig -e 's/[\x{f4}-\x{ff}]/?/g'運行的perl v5.12.5

我沒有太大的一個Perl專家,也不是一個utf-8專家。我也願意在ruby/python/C++(98)中做這個,只要它在Linux機器上相對便攜。

下面是垃圾數據片段的鏈接。 http://pastebin.com/LR0StPHu

+0

現在我的問題是,我如何獲得演示數據到我的代碼? :D – simbabque

+0

@simbabque Pastbin for you http://pastebin.com/LR0StPHu –

回答

5

好吧,讓我們不要混淆一些東西。

第一個字節爲0xf0的UTF-8字符長度爲4個字節,這是編碼合法Unicode字符所需的最多的字符。由於超過94%的可能的Unicode範圍需要第四個字節,因此0xf0不映射到任何單個代碼頁,當然也不映射到專用區。

這樣的字符以外的Basic Multilingual Plane。但這不同於無效或私人使用;這隻意味着它們的代碼點大於U + FFFF(十進制值65,535)。

如果你想排除外界BMP所有的字符,你應該尋找匹配這個表達式的那些:

[\x{10000}-\x{10FFFF}] 

使用Perl的\x{ ... }插語法用十六進制代碼,包括字符點值。如果你真的在使用Perl,那麼爲了便於使用,你可能想把正則表達式放入一個變量中(使用quote-regex構造qr( ... ),因爲裸斜槓會立即嘗試在賦值時與$_匹配時間):

my $not_bmp = qr([\x{10000}-\x{10FFFF}]); 

但同樣,匹配的正則表達式刪除字符有效避免了可能的Unicode字符的94%,因此可以肯定這就是你想要的。

如果你真的只想消除私人使用字符 - 其中一些是裏面的 BMP - 只是特別排除這些範圍。使用Perl或Python或任何其他支持UTF-8的語言,您不必擔心字節;只需檢查代碼點。

由於Wikipedia會告訴你,這三個私人使用區域,在這些代碼點範圍:

  • U + E000..U + F8FF
  • U + F0000..U + FFFFF
  • U + 100000..U + 10FFFF

所以相應的Perl的正則表達式如下:

my $pua = qr([\x{e000}-\x{f8ff}\x{f0000}-\x{fffff}\x{100000}-\x{10ffff}]); 

許多其他語言具有相似的Unicode支持(與UTF-8字符匹配,包括通過代碼點在字符串中的字符等)。例如,這裏的Ruby,主要區別在於使用\u{...}代替\x{...}的插值:

not_bmp = %r([\u{10000}-\u{10FFFF}]) 
pua = %r([\u{e000}-\u{f8ff}\u{f0000}-\u{fffff}\u{100000}-\u{10ffff}]) 

的Python \u逃逸僅正好四個十六進制數字的工作,但如果你有Python3 - 或Python2在廣泛編譯模式 - 您可以使用大寫字母\U,其中只需8個字符(沒有通過{的可變長度支持...}像Perl和Ruby有):

not_bmp = re.compile(u'[\U00010000-\U0010ffff]') 
pua = re.compile(u'[\ue000-\uf8ff\U000f0000-\U000fffff\U00100000-\U0010ffff]') 
+0

我已經對BMP進行了一些研究,它似乎正是我關心的我們收到的數據。除此之外的任何數據,不正確的或其他的數據都是我們不希望存儲的數據。 –

+0

爲了給出一些上下文,我們將這些數據從文件發送到SQL Server時遇到了數據問題,因爲SQL服務器對所有Unicode數據都進行了UTF-16轉換,而且當我們的特定驅動程序出現一些致命錯誤時它不能正確地編碼開關。此數據來自UTF-16 Windows計算機 - > UTF-8文件 - > UTF-16 SQL Server - > UTf-16文件 - > UTF-8文件 - > UTF-16 SQL Server。這是一個該死的混亂。感謝您糾正我對UTF的不理解,這正是我所期待的。 –

+2

聽起來像一路上並不真正支持UTF-16,但只有UCS-2。無論如何,很高興我能幫上忙。 –

3

您需要使用字符而不是字節。

如果你的數據在你的代碼中,並且你使用use utf8編譯指示來告訴Perl你的程序的源代碼是用utf8編寫的。我們這樣做的例子,所以你可以複製/粘貼我的代碼。

您可以使用字符類[]中的\x{} escape sequence進行字符串替換。這些可以在範圍內以及單獨使用。

use utf8; 

my $foo = "asfd ☃ Բարեւ ສະບາຍດີ"; 
$foo =~ s/[\x{10002b}\x{100036}]//g; 
CORE::say $foo; 

這將輸出:

asfd ☃ Բարեւ ສະບາຍດີ 

(還有一個寬字符打印警告,但讓我們忽略了,那是因爲我的stdout不是正確打開)。

我替換的兩個字符\x{10002b}\x{100036}是示例數據中的前兩個字符。我在我的IDE中使用的字體顯示字符序號中沒有任何字形,因此我很容易知道這些字符是什麼。

my font shows character ordinals

這些字符是從Supplementary Private Use Area-B。 (Wikipedia

16 PUA-B U + 100000..U + 10FFFF補充專用區-B 65536 65,534未知

所以,我們也可以做一個範圍。

my $foo = "asfd ☃ Բարեւ ສະບາຍດີ"; 
$foo =~ s/[\x{100000}-\x{10ffff}]//g; 
CORE::say $foo; 

輸出:

asfd ☃ Բարեւ ສະບາຍດີ 

要獲取所有私人使用領域,則需要包括列出here三個範圍。

/[\x{E000}-\x{F8FF}\x{F0_000}-\x{FF_FFD}\x{100_000}-\x{10f_fff}]//g; 
1

這是你的時間浪費得仰望私人使用區域的十六進制範圍。簡單的說

S/\ p {Private_Use} // g^

perluniprops爲主機文件,讓所有的Unicode屬性。如果你只想要上述的BMP私人使用區域,你可以諮詢它(grep for Private)以找到如何匹配這些區域。