我繼續在編輯中暗示的解決方案。
我在Unicode空間中找不到有效範圍的易用列表;即使是官方的Unicode字符數據庫也會比我想要處理的更多的解析。因此,我寫了一個快速腳本來遍歷範圍[0x0,0x10FFFF]中的每個數字,然後使用Encoding.UTF32.GetString(BitConverter.GetBytes(code))
將其轉換爲string
,然後嘗試.Normalize()
結果。如果引發異常,那麼該值不是有效的代碼點。
從這些結果,我創建了以下功能:
bool IsValidCodePoint(UInt32 point)
{
return (point >= 0x0 && point <= 0xfdcf)
|| (point >= 0xfdf0 && point <= 0xfffd)
|| (point >= 0x10000 && point <= 0x1fffd)
|| (point >= 0x20000 && point <= 0x2fffd)
|| (point >= 0x30000 && point <= 0x3fffd)
|| (point >= 0x40000 && point <= 0x4fffd)
|| (point >= 0x50000 && point <= 0x5fffd)
|| (point >= 0x60000 && point <= 0x6fffd)
|| (point >= 0x70000 && point <= 0x7fffd)
|| (point >= 0x80000 && point <= 0x8fffd)
|| (point >= 0x90000 && point <= 0x9fffd)
|| (point >= 0xa0000 && point <= 0xafffd)
|| (point >= 0xb0000 && point <= 0xbfffd)
|| (point >= 0xc0000 && point <= 0xcfffd)
|| (point >= 0xd0000 && point <= 0xdfffd)
|| (point >= 0xe0000 && point <= 0xefffd)
|| (point >= 0xf0000 && point <= 0xffffd)
|| (point >= 0x100000 && point <= 0x10fffd);
}
注意,這個功能不是通用的清理一定很大,這取決於你的需求。它不排除未指定或保留的代碼點,只是那些被明確指定爲「非字符」的編碼點(編輯:和Normalize()似乎阻塞的其他一些代碼點,例如0xfffff)。但是,這些似乎是導致IsNormalized()
和Normalize()
引發異常的唯一代碼點,所以對我的目的來說很好。
之後,它只是將字符串轉換爲UTF-32並對其進行梳理。由於Encoding.GetBytes()
返回一個字節數組和IsValidCodePoint()
需要一個UInt32的,我用了一個不安全的塊和一些鑄造縮小差距:
unsafe string ReplaceInvalidCodePoints(string aString, char replacement)
{
if (char.IsHighSurrogate(replacement) || char.IsLowSurrogate(replacement))
throw new ArgumentException("Replacement cannot be a surrogate", "replacement");
byte[] utf32String = Encoding.UTF32.GetBytes(aString);
fixed (byte* d = utf32String)
fixed (byte* s = Encoding.UTF32.GetBytes(new[] { replacement }))
{
var data = (UInt32*)d;
var substitute = *(UInt32*)s;
for(var p = data; p < data + ((utf32String.Length)/sizeof(UInt32)); p++)
{
if (!(IsValidCodePoint(*p))) *p = substitute;
}
}
return Encoding.UTF32.GetString(utf32String);
}
表現還是不錯的,相比較而言 - 幾個數量級比張貼在樣品更快題。將數據留在UTF-16中可能會更快,更有效率,但是代價很高,代價很大。當然有replacement
是char
意味着替換字符必須在BMP上。
編輯:這裏是一個更簡潔的版本IsValidCodePoint(的):
private static bool IsValidCodePoint(UInt32 point)
{
return point < 0xfdd0
|| (point >= 0xfdf0
&& ((point & 0xffff) != 0xffff)
&& ((point & 0xfffe) != 0xfffe)
&& point <= 0x10ffff
);
}
需要注意的是,因爲代理對,就不可能簡單地看在一個任意的'DWORD'處,並判斷它是否是一個有效的代碼點。 – 2012-01-09 17:24:12
UTF-32不使用代理對。 – 2012-01-09 17:54:32
你如何收到這些不良數據?如果您正在使用'Encoding'類讀取它們,則默認情況下應刪除這些字符。 – porges 2012-01-11 11:38:52