2015-08-09 42 views
2

给定一个UTF-8字符串(&str),我想找出任何未规范化的字符范围(例如a\u{300}而不是\u{e0})。如何检测非标准化的unicode字符?

我该怎么做?

编辑:感谢DK纠正我错误的UTF-8序列。组合字符a,而不是之前。

回答

1

编辑:我刚刚意识到,我得到的结果原因是你的榜样字符串是向后。合并代码点应该排在第二位,而不是第一位。我已经相应地更新了答案。那么,这取决于“规范化”的定义。

例如:

/*! 
Add this to a `Cargo.toml` manifest: 

```cargo 
[dependencies] 
unicode-normalization = "0.1.1" 
``` 
*/ 
extern crate unicode_normalization; 

fn main() { 
    for test_str in vec!["a\u{300}", "\u{e0}"] { 
     is_nfd(test_str); 
     is_nfkd(test_str); 
     is_nfc(test_str); 
     is_nfkc(test_str); 
    } 
} 

macro_rules! norm_test { 
    ($fn_name:ident, $norm_name:ident) => { 
     fn $fn_name(s: &str) { 
      use unicode_normalization::UnicodeNormalization; 
      println!("is_{}({:?}):", stringify!($norm_name), s); 
      let is_norm = s.chars().zip(s.$norm_name()) 
       .inspect(|&(a, b)| println!(" - ({:x}, {:x})", a as u32, b as u32)) 
       .all(|(a, b)| a == b); 
      println!(" is_norm: {}", is_norm); 
     } 
    }; 
} 

norm_test! { is_nfd, nfd } 
norm_test! { is_nfkd, nfkd } 
norm_test! { is_nfc, nfc } 
norm_test! { is_nfkc, nfkc } 

这将产生以下输出:

is_nfd("a\u{300}"): 
- (61, 61) 
- (300, 300) 
is_norm: true 
is_nfkd("a\u{300}"): 
- (61, 61) 
- (300, 300) 
is_norm: true 
is_nfc("a\u{300}"): 
- (61, e0) 
is_norm: false 
is_nfkc("a\u{300}"): 
- (61, e0) 
is_norm: false 
is_nfd("\u{e0}"): 
- (e0, 61) 
is_norm: false 
is_nfkd("\u{e0}"): 
- (e0, 61) 
is_norm: false 
is_nfc("\u{e0}"): 
- (e0, e0) 
is_norm: true 
is_nfkc("\u{e0}"): 
- (e0, e0) 
is_norm: true 

所以"a\u{300}"是NFD和NFKD,同时"\u{e0}"是NFC和NFKC。我不知道K和非K变体之间有什么不同,但Unicode FAQ on Normalization可能会比我更好地解释事情。

+0

因此,如果我想检测字符串内的非标准化范围,我需要首先查找字符的范围,其中只有第一个字符的'unicode_combining_class'为零,并检查它们是否正常(取决于定义,因为您概括)。 – llogiq

+1

@DK:你可以在http://www.unicode.org/reports/tr15/中看到K表格的例子(例如图6)。实质上,NFD和NFC是规范等价的,而它们的K对应关系是较弱的兼容性等价,包括将子脚本/超级脚本更改为常规字符(等等)。例如,在NFD或NFC(其中2075在超级脚本位置中为5)中编码为0032 2075,但在NFKD或NFKC(其中0035是常规5)中编码为0032 0035。 –