【问题标题】:How to reduce a string to ASCII 7 characters for indexing purposes?如何将字符串减少为 ASCII 7 字符以进行索引?
【发布时间】:2017-02-22 12:54:18
【问题描述】:

我正在开发一个必须索引某些句子的应用程序。目前使用 Java 和 PostgreSQL。句子可能使用多种语言,例如法语和西班牙语,使用重音符号和其他非 ASCII 符号。

对于每个单词,我想创建一个可索引的等价词,以便用户可以执行对重音不敏感的搜索(音译)。例如,当用户搜索“nacion”时,即使应用程序存储的原始单词是“Nación”,它也必须找到它。

对此最好的策略是什么?我不一定只限于 PostgreSQL,内部索引值也不需要与原始单词有任何相似之处。理想情况下,它应该是一个通用的解决方案,用于将任何 Unicode 字符串转换为不区分大小写和重音的 ASCII 字符串。

到目前为止,我正在使用如下所示的自定义函数,它只是在存储索引值之前将一些字母替换为 ASCII 等效项,并对查询字符串执行相同操作。

public String toIndexableASCII (String sStrIn) {
  if (sStrIn==null) return null;
  int iLen = sStrIn.length();
  if (iLen==0) return sStrIn;
  StringBuilder sStrBuff = new StringBuilder(iLen);
  String sStr = sStrIn.toUpperCase();

  for (int c=0; c<iLen; c++) {
    switch (sStr.charAt(c)) {
      case 'Á':
      case 'À':
      case 'Ä':
      case 'Â':
      case 'Å':
      case 'Ã':
        sStrBuff.append('A');
        break;
      case 'É':
      case 'È':
      case 'Ë':
      case 'Ê':
        sStrBuff.append('E');
        break;
      case 'Í':
      case 'Ì':
      case 'Ï':
      case 'Î':
        sStrBuff.append('I');
        break;
      case 'Ó':
      case 'Ò':
      case 'Ö':
      case 'Ô':
      case 'Ø':
        sStrBuff.append('O');
        break;
      case 'Ú':
      case 'Ù':
      case 'Ü':
      case 'Û':
        sStrBuff.append('U');
        break;
      case 'Æ':
        sStrBuff.append('E');
        break;
      case 'Ñ':
        sStrBuff.append('N');
        break;
      case 'Ç':
        sStrBuff.append('C');
        break;
      case 'ß':
        sStrBuff.append('B');
        break;
      case (char)255:
        sStrBuff.append('_');
        break;
      default:
        sStrBuff.append(sStr.charAt(c));
    }
  }

  return sStrBuff.toString();
}

【问题讨论】:

  • 将字节解释为 ASCII 7 不会提供我想要实现的“信息丢失”。我希望“coraçón”与“coracon”相同,这样用户在搜索时是否输入重音并不重要。我不需要像谷歌这样的拼写或接近检查器“你的意思是......?”但我确实需要 "é" == "e"。
  • 您询问的映射称为“音译”。
  • 谢谢。我编辑了问题以添加音译,还帮助我谷歌了一些很好的匹配。

标签: indexing ascii non-ascii-characters transliteration


【解决方案1】:
    String s = "Nación";

    String x = Normalizer.normalize(s, Normalizer.Form.NFD);

    StringBuilder sb=new StringBuilder(s.length());
    for (char c : x.toCharArray()) {
        if (Character.getType(c) != Character.NON_SPACING_MARK) {
            sb.append(c);
        }
    }

    System.out.println(s); // Nación
    System.out.println(sb.toString()); // Nacion

这是如何工作的: 它将国际字符拆分为 NFD 分解(ó 变为 o◌́),然后去除组合的变音符号。

Character.NON_SPACING_MARK 包含组合变音符号(Unicode 将其称为 Bidi Class NSM [Non-Spacing Mark])。

【讨论】:

【解决方案2】:

对当前代码的一项明显改进:使用您的映射预填充Map&lt;Character, Character&gt;

然后简单地检查该 Map 是否有映射;如此;使用它;否则使用原始字符。

正如 Androbin 解释的那样,有一些特殊的映射不依赖于对象,而是使用原始类型,例如 trove。因此,取决于您的解决方案和要求;你可以调查一下。

【讨论】:

  • 谢天谢地,有 Map#getOrDefault
  • 我推荐一个原始的地图以提高效率
  • 例如有 FastUtil、HPPC、Koloboke 和 Trove
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-05
  • 2014-06-11
  • 1970-01-01
  • 2014-06-14
  • 1970-01-01
  • 2017-02-14
  • 1970-01-01
相关资源
最近更新 更多