如何检测非规范化的 unicode 字符?
How to detect unicode characters that are non-normalized?
给定一个 UTF-8 字符串 (&str
),我想找出任何未规范化的字符范围(例如 a\u{300}
而不是 \u{e0}
)。
我该怎么做?
编辑:感谢 DK 纠正了我错误的 UTF-8 序列。组合字符在 a
之后出现 ,而不是在
之前。
编辑:我刚刚意识到我得到结果的原因是您的示例字符串是向后。组合代码点应该排在第二位,而不是第一位。我已经相应地更新了答案。
嗯,这取决于 "normalized" 的定义。
例如:
/*!
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 可能比我能更好地解释事情。
给定一个 UTF-8 字符串 (&str
),我想找出任何未规范化的字符范围(例如 a\u{300}
而不是 \u{e0}
)。
我该怎么做?
编辑:感谢 DK 纠正了我错误的 UTF-8 序列。组合字符在 a
之后出现 ,而不是在
编辑:我刚刚意识到我得到结果的原因是您的示例字符串是向后。组合代码点应该排在第二位,而不是第一位。我已经相应地更新了答案。
嗯,这取决于 "normalized" 的定义。
例如:
/*!
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 可能比我能更好地解释事情。