2010-01-19 61 views
8

将字符串从Unicode转换为ASCII而不更改其长度(在我的情况中非常重要)的最佳方法是什么?此外,没有任何转换问题的字符必须与原始字符串处于相同的位置。 所以一个“Ä”必须转换为“A”,而不是含有更多字符的神秘东西。将Unicode转换为ASCII而不更改字符串长度(用Java)

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

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

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

+5

你打算把口水鸡换成什么?我不知道如何用三个字母表达唾液鸡的概念。 – novalis 2010-01-19 20:12:22

+0

目前尚不清楚 - 您是否尝试保留字符数或字节数......或者显示时字符串的宽度? – MtnViewMark 2010-01-19 20:36:49

+0

@novalis +1唾液鸡:-) – 2010-01-19 20:51:03

回答

12

正如this回答说,下面的代码应该工作:

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字符将成为问号。

+0

谢谢......似乎工作得很好。 但'^'字符有问题。当它在一个字符串中时(比如“he ^^ o”),它失败(简单地被删除)。 – Zardoz 2010-01-24 01:57:42

+0

只需从正则表达式中删除\\ {IsLm} \\ p {IsSk}即可。 – 2010-01-24 04:06:06

+1

如果有人想删除问号,充分减少文字基本字母尝试:“[\\ p {} InBasicLatin] +”(注意大写P表示“不在)使用测试:rrrr̈r'ŕřttẗţỳỹẙy'yýÿŷpp̈sss̈s̊s's̸śŝŞşšddd̈ďd'ḑf̈f̸ggg̈g'ģqĝǧḧĥj̈j'ḱkk̈k̸ǩlll̈Łłẅẍcc̈c̊c'c̸Çççćĉčvv̈v'v̸bb̧ǹnn̈n̊n'ńņňñmmmm̈m̊m̌ǵß – RedYeti 2015-03-18 15:00:02

7

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

+0

这可能是Zardoz实际需要的,尽管对于不在拉丁文页面中的字符来说它是无效的。 – 2010-01-19 20:14:00

+0

+1这看起来像是问题的最佳解决方案(就问题而言)。 – 2010-01-19 20:17:36

+0

Unicode规范化仅适用于字符,它可以由ASCII字符集中的简单拉丁字符和变音符号组成。 – jarnbjo 2010-01-19 20:33:46

2

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

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

但无论哪一种,你使用,这是不可能的unicode字符串转换为ASCII 保留了原有的长度和字符的位置,只是因为Unicode字符集将使用多字节某些字符(显然)。

我知道的唯一例外将是仅包含ASCII字符的UTF-8字符串:由于UTF-8仅在必要时才使用多字节字符,因此该字符串在UTF-8和ASCII中都已相同。 (我不知道其他的Unicode风格,可能还有其他的动态风格)。

唯一的解决方法,我可以看到的是增加了空间,这是由ASCII一个替代任何特殊字符,但会搞砸了字符串(Göteborg在UTF8将不得不成为Go teborg保持长度)。

也许你想详细说明你想要/需要达到什么目标,所以这里的人可以提出解决方法。

+0

Java在内部使用UTF-16作为字符串,因此对于大多数常见的“Western”语言,原始文本和“ASCII-reduced”文本将具有相同的长度(保存偶尔的奇怪标点符号)。 – 2010-01-19 20:17:55

2

Normalizer的一个isssue是,它在sun.text包中的Java 1.6之前,而在1.6中它在java.text中的包和它的方法签名已经改变。所以如果你的应用程序需要在两个平台上运行,你必须使用反射。

另一种定制的解决方案被描述为techniwue 3 here

2

正如保罗·泰勒提到的:有一个与你需要的项目是编译/可运行在前期1.6以及在1.6以上Java中使用正规化问题。由于Normalizer采用不同的包(java.text.Normalizer(对于1.6)而不是sun.text.Normalizer(对于1.6之前的版本)),并且具有不同的方法签名,所以您会遇到麻烦。

通常建议使用反射来调用适当的Normalizer.normalize()方法。 (Example could be found here)。
但是,如果您不想在代码中放置反射混乱,则可以使用icu4j library。它包含com.ibm.icu.text.Normalizer类,normalize()方法执行与java.text.Normalizer/sun.text.Normalizer相同的工作。 Icu库具有(应该有)自己的Normalizer实现,因此您可以与库共享您的项目,并且应该与Java无关。
缺点是icu库很大。

如果您使用Normalizer类仅用于从字符串中删除重音/变音符号,还有另一种方法。您可以使用Apache commons lang library (ver. 3)包含StringUtils与方法stripAccents()

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

Lang3库可能使用反射根据Java版本调用适当的正规化。所以好处是你的代码中没有反射混乱。