【问题标题】:How do I match "i" with Turkish i in java?如何在 java 中将“i”与土耳其语 i 匹配?
【发布时间】:2015-08-23 20:13:26
【问题描述】:

我想将英语 (i) 的小写“I”与土耳其语 (i) 的小写“İ”匹配。它们是相同的字形,但它们不匹配。当我做System.out.println("İ".toLowerCase()); 时,字符 i 和一个点被打印出来(这个网站不能正确显示)

有没有办法匹配这些?(最好不用硬编码)我想让程序匹配与语言和 utf 代码无关的相同字形。这可能吗?

我已经测试了标准化,但没有成功。

public static void main(String... a) {
    String iTurkish = "\u0130";//"İ";
    String iEnglish = "I";
    prin(iTurkish);
    prin(iEnglish);
}

private static void prin(String s) {
    System.out.print(s);
    System.out.print(" -  Normalized : " + Normalizer.normalize(s, Normalizer.Form.NFD));
    System.out.print(" - lower case: " + s.toLowerCase());
    System.out.print(" -  Lower case Normalized : " + Normalizer.normalize(s.toLowerCase(), Normalizer.Form.NFD));
    System.out.println();

}

结果未正确显示在站点中,但第一行(iTurkish)在小写 i 附近仍然有 ̇

目的和问题

这将是一本多语种词典。我希望程序能够识别“İFEL”以“if”开头。为了确保它们不区分大小写,我首先将两个文本都转换为小写。 İFEL 变成 i(dot)fel 并且“if”不被识别为它的一部分

【问题讨论】:

  • 这两个字母不是同一个 uni 码,所以它们不匹配。
  • 你可以用commons-lang从字符串中去掉变音符号:org.apache.commons.lang3.StringUtils.stripAccents(String)
  • @agad 不会阻止区分 i 和 ı 吗?如果没有办法,我会考虑。
  • @Zelldon true 但它们是相同的字形。归一化的点不匹配它们吗?

标签: java unicode normalization unicode-normalization


【解决方案1】:

如果你打印出你看到的字符的十六进制值,区别就很明显了:

İ 0x130 - Normalized : İ 0x49 0x307 - Lower case: i̇ 0x69 0x307 - Lower case Normalized : i̇ 0x69 0x307
I 0x49 - Normalized : I 0x49 - Lower case: i 0x69 - Lower case Normalized : i 0x69

规范化土耳其语İ 不会给你一个英语I,而是给你一个英语I,后跟一个变音符号0x307。这是正确的,并且是规范化过程所期望的。规范化不是“转换为 ASCII”操作。正如Normalizer 的文档所述,它遵循的过程是一个非常严格定义的标准,即Unicode Standard Annex #15 — Unicode Normalization Forms

在规范化之前或之后都有numerous ways to strip diacritics。您需要什么取决于您的用例的具体情况,但对于您的用例,我建议使用GuavaCharMatcher 类在规范化后去除非ASCII 字符,例如:

String asciiString = CharMatcher.ascii().retainFrom(normalizedString);

This answer 更深入地介绍了\p{InCombiningDiacriticalMarks} 的作用,以及为什么它不理想。我的CharMatcher 解决方案也不理想(链接的答案提供了更强大的解决方案),但为了快速修复,您可能会发现只保留“足够好”的 ASCII 字符。这比基于Pattern 的方法更接近“正确”并且更快。

【讨论】:

  • +1,有趣的副作用"İ".toLowerCase() 似乎决定它需要分解角色。至少在这里...
  • 似乎每个人都建议去掉变音符号。我可能会这样做。我想将“ıf”与“İF”匹配比不将“if”与“İF”匹配要好。很难,我不确定是否会是这种情况。
  • @WVrock - 正如您所介绍的那样,解决问题的最佳方法是去掉变音符号。您可能还有其他未告诉我们的要求,哪些可能需要不同的解决方案。但从广义上讲,如果您希望某人能够键入英文字符并将它们映射到土耳其语字符,您将不得不剥离 一些 信息,并且您将很难避免两者假阳性和假阴性。您的解决方案应尽量减少对您的用例不利的情况。
  • 尽管这是引导我走向正确方向的答案,但我更喜欢 Rafiq 链接中的代码
【解决方案2】:

您可以使用下面的代码:

public static void main(String... a) {

      String iTurkish = "\u0130";//"İ";
      String iEnglish = "I";
      prin(iTurkish);
      prin(iEnglish);


}

private static void prin(String s) {
    System.out.print(s);
    String nfdNormalizedString = Normalizer.normalize(s, Normalizer.Form.NFD);
    Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
    System.out.print(" -  Normalized : " + pattern.matcher(nfdNormalizedString).replaceAll(""));
    System.out.print(" - lower case: " + s.toLowerCase());
    System.out.print(" -  Lower case Normalized : " + Normalizer.normalize(pattern.matcher(nfdNormalizedString).replaceAll("").toLowerCase(), Normalizer.Form.NFD));
    System.out.println();

}

或查看Converting Symbols, Accent Letters to English Alphabet

【讨论】:

  • 从 Utils 类中复制代码并在此处显示为自己的代码并不是很好。
  • 为什么不投票?我提供了链接“stackoverflow.com/questions/1008802/converting-symbols-accent- letters-to-english-alphabet”。没看到吗? “阿加德”
  • +1 用于提供指向答案的链接并使其适应给定的代码。即使您先提供链接然后澄清您正在使用其他人的代码会更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-22
  • 2014-06-24
  • 1970-01-01
  • 2020-04-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多