将Unicode转换为ASCII而不更改字符串长度(在Java中)

将字符串从Unicode转换为ASCII而不改变其长度的最佳方法是什么(在我的情况下这非常重要)? 此外,没有任何转换问题的字符必须与原始字符串中的位置相同。 因此,“Ä”必须转换为“A”而不是具有更多字符的神秘内容。

编辑:
@novalis – 这些符号(例如亚洲语言)应该只转换为一些占位符。 我对这些词或他们的意思不太感兴趣。

@MtnViewMark – 在任何情况下我都必须保留所有字符的数量和ASCII可用字符的位置。

这里有一些更多的信息:我有一些只能处理ASCII字符串的文本挖掘工具。 大多数应该处理的文本是英文的,但有些文本包含非ASCII字符。 我对这些单词不感兴趣,但我必须确保我感兴趣的单词(那些只包含ASCII字符的单词)在字符串转换后处于相同的位置。

如本回答所述,以下代码应该有效:

String s = "口水雞 hello Ä"; String s1 = Normalizer.normalize(s, Normalizer.Form.NFKD); String regex = "[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+"; String s2 = new String(s1.replaceAll(regex, "").getBytes("ascii"), "ascii"); System.out.println(s2); System.out.println(s.length() == s2.length()); 

输出是

 ??? hello A true 

所以你首先删除diactrical标记,转换为ascii。 非ascii字符将成为问号。

java.text.Normalizer.normalize()Normalizer.Form.NFD ,然后过滤掉非ASCII字符。

警告:我不懂Java。 只是关于字符集。

您没有说明您正在使用哪个字符集。

但无论你使用哪种,都不可能将Unicode字符串转换为ASCII 保留原始长度和字符位置,因为Unicode字符集会对某些字符使用多个字节(显然)。

我所知道的唯一例外是UTF-8字符串,它只包含ASCII字符:这个字符串在UTF-8和ASCII中已经相同,因为UTF-8仅在必要时使用多字节字符。 (我不知道其他Unicode风格,可能还有其他动态风格)。

我能看到的唯一解决方法是为任何被ASCII替换的特殊字符添加一个空格,但这会Go teborg字符串(UTF8中的Göteborg必须成为Go teborg以保持长度)。

也许你想详细说明你想要/需要实现什么,所以这里的人可以建议解决方法。

正如Paul Taylor所说:如果您需要在1.6之前的版本中以及1.6及更高版本的java中可编译/可运行项目,则使用Normalizer存在问题。 你会遇到麻烦,因为Normalizer在不同的包中( java.text.Normalizer (用于1.6)而不是sun.text.Normalizer (用于1.6之前的版本))并且具有不同的方法签名。

通常建议使用reflection来调用相应的Normalizer.normalize()方法。 ( 例子可以在这里找到 )。
但是,如果您不想在代码中添加reflection混乱,则可以使用icu4j库 。 它包含带有normalize()方法的com.ibm.icu.text.Normalizer类,该方法执行与java.text.Normalizer / sun.text.Normalizer相同的工作。 Icu库有(应该有)自己的Normalizer实现,因此您可以与库共享您的项目,这应该是独立于Java的。
缺点是icu库非常大。

如果你使用Normalizer类只是为了从Strings中删除重音符号/变音符号,那么还有另一种方法。 您可以使用包含StringUtils Apache commons lang库(版本3)和方法stripAccents()

 String noAccentsString = org.apache.commons.lang3.StringUtils.stripAccents(s); 

Lang3库可能使用reflection来根据java版本调用适当的Normalizer。 所以优点是你的代码中没有reflection混乱。

一个问题是Normalizer是pre Java 1.6它在sun.text包中,而在1.6它在java.text包中它的方法签名已经改变。 因此,如果您的应用程序需要在两个平台上运行,则必须使用reflection。

另一种定制解决方案在此描述为技术3