我有一个程序通过删除任何不是字母或数字的字符来过滤掉字符串。该计划支持大量语言,包括中文,俄语,阿拉伯语等。该计划如下:
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < src.length(); i++) {
int ch = src.codePointAt(i);
if (Character.isLetterOrDigit(ch)) {
strBuilder.appendCodePoint(ch);
}
}
我使用codePointAt
方法来支持通过高和低代理以UTF 32位表示的字符。在执行过滤之前,我需要知道每个字符串是否需要规范化?我指的是在执行循环之前调用Normalizer.normalize
方法。如果是,我应该使用哪个Normalizer.Form
?
感谢。
答案 0 :(得分:4)
这完全取决于您真正希望算法的行为方式。
作为一个例子,让我们考虑字符串"a\u0308"
(U + 0061ʟᴀᴛɪɴsᴍᴀʟʟʟᴇᴛᴛᴇʀᴀ,然后是U + 0308ᴄᴏᴍʙɪɴɪɴɢᴅɪᴀᴇʀᴇsɪs),它在规范上等同于"ä"
或"\u00e4"
(U + 00E4ʟᴀᴛɪɴsᴍᴀʟʟʟᴇᴛᴛᴇʀᴀᴡɪᴛʜᴅɪᴀᴇʀᴇsɪs)。规范等效意味着您的算法不应该区分这两者。使规范等效字符串表现相同的一种简单方法是将两者标准化为相同的规范规范化形式:NFC或NFD。
根据这些字符串的含义,您可能希望使用兼容性等效(NFKC或NFKD)。通常建议用于例如标识符。这两个将兼容性字符转换为推荐的等价物(如U + 2126ᴏʜᴍsɪɢɴ到U + 03A9ɢʀᴇᴇᴋᴄᴀᴘɪᴛᴀʟʟᴇᴛᴛᴇʀᴏᴍᴇɢᴀ,或者将字符连接到它们所构成的字符序列)。
无论你想要哪种等价,原则都是一样的:如果你想对待等价的字符串,同样规范化两者是最简单的方法。
一旦你对所有等效字符串有相同的行为,你需要考虑另一个问题:如果你丢弃所有“不是字母或数字的字符”,那么字符串会发生什么字母和组合标记,如"\u092C\u093F"
(U + 092Cᴅᴇᴠᴀɴᴀɢᴀʀɪʟᴇᴛᴛᴇʀʙᴀ后跟U + 093Fᴅᴇᴠᴀɴᴀɢᴀʀɪᴠᴏᴡᴇʟsɪɢɴɪɢɴ,看起来像बि)?这是两个独立的代码点,U + 093F不是一个字母。这两个不以任何标准化形式组成。你想要删除组合标记(让你留下ब),或者不是吗?
如果删除它们很好,您可以使用当前的算法。否则,您可能希望迭代字形集群,粗略地说,它是基本字符序列,后面跟着组合标记。 Java和ICU都提供了用于查找字形集群的API(Java称这些字符为“字符符”)。
答案 1 :(得分:0)
请注意,用于迭代代码点的代码不太正确,我相信你想要:
for(int cp, i = 0; i < s.length(); i += Character.charCount(cp)) {
cp = s.codePointAt(i);
// Process cp...
}
抱歉,不知道你是否需要正常化。