【问题标题】:Replacing unicode punctuation with ASCII approximations用 ASCII 近似值替换 unicode 标点符号
【发布时间】:2011-06-16 02:09:00
【问题描述】:

我正在阅读 Java 程序中的一些文本文件,并想用 ASCII 近似值替换一些 Unicode 字符。这些文件最终会被分解成句子,然后提供给 OpenNLP。 OpenNLP 无法识别 Unicode 字符,并且在许多符号上给出了不正确的结果(它将“girl's”标记为“girl”和“'s”,但如果它是 Unicode 引用,则被视为单个标记)..

例如,源语句可能包含 Unicode 定向引用 U2018 ('),我想将其转换为 U0027 (')。最终我将剥离剩余的 Unicode。

我知道我正在丢失信息,并且我知道我可以编写正则表达式来转换这些符号中的每一个,但我想问是否有可以重用的代码来转换其中一些符号。

这是我能做到的,但我确信我会犯错/错过一些事情/等等:

    // double quotation (")
    replacements.add(new Replacement(Pattern.compile("[\u201c\u201d\u201e\u201f\u275d\u275e]"), "\""));

    // single quotation (')
    replacements.add(new Replacement(Pattern.compile("[\u2018\u2019\u201a\u201b\u275b\u275c]"), "'"));

replacements 是一个自定义类,我稍后会运行并应用替换。

    for (Replacement replacement : replacements) {
         text = replacement.pattern.matcher(text).replaceAll(r.replacement);
    }

如你所见,我必须找到:

  • 左单引号
  • 右单引号
  • 单低 9 引号(这是什么/我应该替换它吗?)
  • 单高反转 9 引号(这是什么/我应该替换它吗?)

【问题讨论】:

  • 您是否正在寻找特定语言的库和/或示例代码?或者您是否正在寻找 Unicode 字符到 ASCII 近似值的预先存在的映射?我不确定正则表达式和可以重用的代码之间有什么区别。
  • 我正在寻找一个 Java 库。我可以写正则表达式,但我确信我会在这个过程中遗漏一些东西。我想知道是否其他人已经为我做出了决定。你读过GEB吗,Mu Mind?
  • 那些 unicode 链接已失效

标签: java unicode ascii


【解决方案1】:
String lstring = "my string containing all different simbols";

lstring = lstring.replaceAll("\u2013", "-")
    .replaceAll("\u2014", "-")
    .replaceAll("\u2015", "-")
    .replaceAll("\u2017", "_")
    .replaceAll("\u2018", "\'")
    .replaceAll("\u2019", "\'")
    .replaceAll("\u201a", ",")
    .replaceAll("\u201b", "\'")
    .replaceAll("\u201c", "\"")
    .replaceAll("\u201d", "\"")
    .replaceAll("\u201e", "\"")
    .replaceAll("\u2026", "...")
    .replaceAll("\u2032", "\'")
    .replaceAll("\u2033", "\"");

【讨论】:

    【解决方案2】:

    我按照@marek-stoj 的链接创建了一个 Scala 应用程序,它可以在保持字符串长度的同时从字符串中清除 unicode。它删除变音符号(重音符号)并使用 @marek-stoj 建议的映射将非 Ascii unicode 字符转换为其 ascii 近似值。

    import java.text.Normalizer
    
    object Asciifier {
      def apply(string: String) = {
        var cleaned = string
          for ((unicode, ascii) <- substitutions) {
            cleaned = cleaned.replaceAll(unicode, ascii)
          }
    
        // convert diacritics to a two-character form (NFD)
        // http://docs.oracle.com/javase/tutorial/i18n/text/normalizerapi.html
        cleaned = Normalizer.normalize(cleaned, Normalizer.Form.NFD)
    
        // remove all characters that combine with the previous character
        // to form a diacritic.  Also remove control characters.
        // http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html
        cleaned.replaceAll("[\\p{InCombiningDiacriticalMarks}\\p{Cntrl}]", "")
    
        // size must not change
        require(cleaned.size == string.size)
    
        cleaned
      }
    
      val substitutions = Set(
          (0x00AB, '"'),
          (0x00AD, '-'),
          (0x00B4, '\''),
          (0x00BB, '"'),
          (0x00F7, '/'),
          (0x01C0, '|'),
          (0x01C3, '!'),
          (0x02B9, '\''),
          (0x02BA, '"'),
          (0x02BC, '\''),
          (0x02C4, '^'),
          (0x02C6, '^'),
          (0x02C8, '\''),
          (0x02CB, '`'),
          (0x02CD, '_'),
          (0x02DC, '~'),
          (0x0300, '`'),
          (0x0301, '\''),
          (0x0302, '^'),
          (0x0303, '~'),
          (0x030B, '"'),
          (0x030E, '"'),
          (0x0331, '_'),
          (0x0332, '_'),
          (0x0338, '/'),
          (0x0589, ':'),
          (0x05C0, '|'),
          (0x05C3, ':'),
          (0x066A, '%'),
          (0x066D, '*'),
          (0x200B, ' '),
          (0x2010, '-'),
          (0x2011, '-'),
          (0x2012, '-'),
          (0x2013, '-'),
          (0x2014, '-'),
          (0x2015, '-'),
          (0x2016, '|'),
          (0x2017, '_'),
          (0x2018, '\''),
          (0x2019, '\''),
          (0x201A, ','),
          (0x201B, '\''),
          (0x201C, '"'),
          (0x201D, '"'),
          (0x201E, '"'),
          (0x201F, '"'),
          (0x2032, '\''),
          (0x2033, '"'),
          (0x2034, '\''),
          (0x2035, '`'),
          (0x2036, '"'),
          (0x2037, '\''),
          (0x2038, '^'),
          (0x2039, '<'),
          (0x203A, '>'),
          (0x203D, '?'),
          (0x2044, '/'),
          (0x204E, '*'),
          (0x2052, '%'),
          (0x2053, '~'),
          (0x2060, ' '),
          (0x20E5, '\\'),
          (0x2212, '-'),
          (0x2215, '/'),
          (0x2216, '\\'),
          (0x2217, '*'),
          (0x2223, '|'),
          (0x2236, ':'),
          (0x223C, '~'),
          (0x2264, '<'),
          (0x2265, '>'),
          (0x2266, '<'),
          (0x2267, '>'),
          (0x2303, '^'),
          (0x2329, '<'),
          (0x232A, '>'),
          (0x266F, '#'),
          (0x2731, '*'),
          (0x2758, '|'),
          (0x2762, '!'),
          (0x27E6, '['),
          (0x27E8, '<'),
          (0x27E9, '>'),
          (0x2983, '{'),
          (0x2984, '}'),
          (0x3003, '"'),
          (0x3008, '<'),
          (0x3009, '>'),
          (0x301B, ']'),
          (0x301C, '~'),
          (0x301D, '"'),
          (0x301E, '"'),
          (0xFEFF, ' ')).map { case (unicode, ascii) => (unicode.toChar.toString, ascii.toString) }
    }
    

    【讨论】:

    • 你有一个错误:replaceAll 不会改变字符串。您需要将replaceAll 的结果分配回已清理。
    【解决方案3】:

    这是一个很好的 Python 包。它基于 Perl 模块 Text::Unidecode。我认为这可以移植到 Java。

    http://www.tablix.org/~avian/blog/archives/2009/01/unicode_transliteration_in_python/

    http://pypi.python.org/pypi/Unidecode

    【讨论】:

      【解决方案4】:

      【讨论】:

      【解决方案5】:

      我对类似替换所做的是创建一个 Map(通常是 HashMap),其中 Unicode 字符作为键,它们的替代作为值。

      伪Java; for 取决于您使用哪种字符容器作为执行此操作的方法的参数,例如字符串、字符序列等

      StringBuilder output = new StringBuilder();
      for (each Character 'c' in inputString)
      {
          Character replacement = xlateMap.get( c );
          output.append( replacement != null ? replacement : c );
      }
      return output.toString();
      

      Map 中的任何内容都被替换,Map 中没有的任何内容都保持不变并复制到输出。

      【讨论】:

        【解决方案6】:

        虽然这不能完全回答您的问题,但您可以将 Unicode 文本转换为 US-ASCII,用“?”替换非 ASCII 字符符号。

        String input = "aáeéiíoóuú"; // 10 chars.
        
        Charset ch = Charset.forName("US-ASCII");
        CharsetEncoder enc = ch.newEncoder();
        enc.onUnmappableCharacter(CodingErrorAction.REPLACE);
        enc.replaceWith(new byte[]{'?'});
        
        ByteBuffer out = null;
        
        try {
            out = enc.encode(CharBuffer.wrap(input));
        } catch (CharacterCodingException e) { 
            /* ignored, shouldn't happen */ 
        }
        
        String outStr = ch.decode(out).toString();
        
        // Prints "a?e?i?o?u?"
        System.out.println(outStr);
        

        【讨论】:

        • 我用 Normalizer.normalize(text, Normalizer.Form.NFD) 删除变音符号,然后用 Pattern.compile("\\p{InCombiningDiacriticalMarks}+") 替换。
        • 使用此解决方案,应该映射的基本标点符号(如引号)不会映射到 ASCII 引号。您会说“这与此 ASCII 字符基本相同”的许多其他 Unicode 字符将无法正确映射。因此,我认为使用所有合理替换的自定义地图会取得更好的效果。
        【解决方案7】:

        每个 unicode 字符都分配有一个 category。存在两个不同的类别 引用:

        使用这些列表,如果您想手动编写正则表达式,您应该能够正确处理所有引号。

        Java Character.getType 为您提供字符类别,例如FINAL_QUOTE_PUNCTUATION

        现在您可以获取每个(标点符号)字符的类别并将其替换为 ASCII 中的适当补充。

        您可以相应地使用其他标点符号类别。在 'Punctuation, Other' 中有一些字符,例如 PRIME ,您可能还想用撇号代替。

        【讨论】:

        • 我只使用自定义地图,可以定义尽可能多的字符,因为分配给基本字符的 Unicode 类别似乎不足。例如,基本的单引号和双引号字符(例如,您使用键盘在记事本中键入的字符)被归类为“其他标点符号”,而不是您希望它们归类的标点符号初始和标点结尾类别.
        • @Triynko - 存在的问题:只有一个“正常”(ASCII)单引号和一个双引号,因此将其标记为 INITIALFINAL 引号标点符号也可以错了。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        • 1970-01-01
        • 2011-02-07
        • 2023-04-04
        • 2012-06-08
        • 2018-07-02
        • 2016-04-23
        相关资源
        最近更新 更多