时间:2010-07-23 20:33:28

标签: java string diacritics

12 个答案:

答案 0 :(得分:345)

答案 1 :(得分:113)

截至2011年,您可以使用Apache Commons StringUtils.stripAccents(input)(自3.0起):

    String input = StringUtils.stripAccents("Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ");
    System.out.println(input);
    // Prints "This is a funky String"

注意:

接受的答案(Erick Robertson's)不适用于Ø或Ł。 Apache Commons 3.5也不适用于Ø,但它确实适用于Ł。在阅读Wikipedia article for Ø之后,我不确定它应该被替换为“O”:它是挪威语和丹麦语中的单独字母,在“z”之后按字母顺序排列。这是“条带重音”方法局限性的一个很好的例子。

答案 2 :(得分:49)

@ virgo47的解决方案非常快,但近似。接受的答案使用Normalizer和正则表达式。我想知道Normalizer与正则表达式相比花费了多少时间,因为删除所有非ASCII字符都可以在没有正则表达式的情况下完成:

import java.text.Normalizer;

public class Strip {
    public static String flattenToAscii(String string) {
        StringBuilder sb = new StringBuilder(string.length());
        string = Normalizer.normalize(string, Normalizer.Form.NFD);
        for (char c : string.toCharArray()) {
            if (c <= '\u007F') sb.append(c);
        }
        return sb.toString();
    }
}

通过写入char []并且不调用toCharArray()可以获得小的额外加速,尽管我不确定代码清晰度的降低是否值得:

public static String flattenToAscii(String string) {
    char[] out = new char[string.length()];
    string = Normalizer.normalize(string, Normalizer.Form.NFD);
    int j = 0;
    for (int i = 0, n = string.length(); i < n; ++i) {
        char c = string.charAt(i);
        if (c <= '\u007F') out[j++] = c;
    }
    return new String(out);
}

这种变化的优点在于使用Normalizer的正确性以及使用表格的一些速度的正确性。在我的机器上,这个比你接受的答案快4倍,比@ virgo47慢6.6倍到7倍(接受的答案比我机器上的@ virgo47慢约26倍)。

答案 3 :(得分:27)

编辑:如果你没有坚持Java&lt; 6并且速度不是很关键和/或翻译表太有限,请使用David的回答。关键是使用Normalizer(在Java 6中引入)而不是循环内的转换表。

虽然这不是“完美”的解决方案,但是当你知道范围(在我们的例子中是Latin1,2),它在Java 6之前工作(虽然不是真正的问题)并且比最建议的版本快得多(可能是也可能不是问题):

    /**
 * Mirror of the unicode table from 00c0 to 017f without diacritics.
 */
private static final String tab00c0 = "AAAAAAACEEEEIIII" +
    "DNOOOOO\u00d7\u00d8UUUUYI\u00df" +
    "aaaaaaaceeeeiiii" +
    "\u00f0nooooo\u00f7\u00f8uuuuy\u00fey" +
    "AaAaAaCcCcCcCcDd" +
    "DdEeEeEeEeEeGgGg" +
    "GgGgHhHhIiIiIiIi" +
    "IiJjJjKkkLlLlLlL" +
    "lLlNnNnNnnNnOoOo" +
    "OoOoRrRrRrSsSsSs" +
    "SsTtTtTtUuUuUuUu" +
    "UuUuWwYyYZzZzZzF";

/**
 * Returns string without diacritics - 7 bit approximation.
 *
 * @param source string to convert
 * @return corresponding string without diacritics
 */
public static String removeDiacritic(String source) {
    char[] vysl = new char[source.length()];
    char one;
    for (int i = 0; i < source.length(); i++) {
        one = source.charAt(i);
        if (one >= '\u00c0' && one <= '\u017f') {
            one = tab00c0.charAt((int) one - '\u00c0');
        }
        vysl[i] = one;
    }
    return new String(vysl);
}

使用32位JDK对我的硬件进行测试表明,这可以在~100ms内执行从àèéľľť89899FDČ到aeelstc89FDC的100万次转换,而Normalizer方式则在3.7s(慢37倍)内完成转换。如果您的需求与性能有关并且您知道输入范围,则可能适合您。

享受: - )

答案 4 :(得分:20)

System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""));

为我工作。上面代码段的输出给出了“aee”,这是我想要的,但是

System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", ""));

没有做任何替换。

答案 5 :(得分:6)

答案 6 :(得分:2)

@David Conrad解决方案是我尝试使用Normalizer的最快速度,但它确实有一个bug。它基本上剥离了非重音符号,例如汉字和其他字母,如æ,都被剥离。 我们要删除的字符是非间距标记,这些字符在最终字符串中不占用额外的宽度。这些零宽度字符基本上最终结合在一些其他字符中。如果你可以看到它们被隔离为一个角色,例如像这样的`,我的猜测就是它与空格字符结合在一起。

public static String flattenToAscii(String string) {
    char[] out = new char[string.length()];
    String norm = Normalizer.normalize(string, Normalizer.Form.NFD);

    int j = 0;
    for (int i = 0, n = norm.length(); i < n; ++i) {
        char c = norm.charAt(i);
        int type = Character.getType(c);

        //Log.d(TAG,""+c);
        //by Ricardo, modified the character check for accents, ref: http://stackoverflow.com/a/5697575/689223
        if (type != Character.NON_SPACING_MARK){
            out[j] = c;
            j++;
        }
    }
    //Log.d(TAG,"normalized string:"+norm+"/"+new String(out));
    return new String(out);
}

答案 7 :(得分:2)

我遇到了与字符串相等检查相关的问题,其中一个是比较字符串 ASCII character code 128-255

  

即,不间断空间 - [Hex - A0]空间[Hex - 20]。   显示HTML上的不间断空格。我使用了以下spacing entities。它们的字符和字节类似于 &emsp is very wide space[ ]{-30, -128, -125}, &ensp is somewhat wide space[ ]{-30, -128, -126}, &thinsp is narrow space[ ]{32} , Non HTML Space {}

String s1 = "My Sample Space Data", s2 = "My Sample Space Data";
System.out.format("S1: %s\n", java.util.Arrays.toString(s1.getBytes()));
System.out.format("S2: %s\n", java.util.Arrays.toString(s2.getBytes()));
     

字节输出:

     

S1:[77,121,32,83,97,109,112,108,101,32,83,112,97,99,101,{{1 },68,97,116,97]    S2:[77,121,32,83,97,109,112,108,101,-30, -128, -125,83,112,97,99,101,-30, -128, -125, 68,97,116,97]

使用以下代码表示不同的空格及其字节代码:wiki for List_of_Unicode_characters

-30, -128, -125
  • Java用于Java的Unicode字符串的ASCII音译。 unidecode

    String spacing_entities = "very wide space,narrow space,regular space,invisible separator";
    System.out.println("Space String :"+ spacing_entities);
    byte[] byteArray = 
        // spacing_entities.getBytes( Charset.forName("UTF-8") );
        // Charset.forName("UTF-8").encode( s2 ).array();
        {-30, -128, -125, 44, -30, -128, -126, 44, 32, 44, -62, -96};
    System.out.println("Bytes:"+ Arrays.toString( byteArray ) );
    try {
        System.out.format("Bytes to String[%S] \n ", new String(byteArray, "UTF-8"));
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    
  • ➩使用Guava:Google Core Libraries for Java

    String initials = Unidecode.decode( s2 );
    

    对于网址编码for the space,请使用番石榴图书馆。

    String replaceFrom = CharMatcher.WHITESPACE.replaceFrom( s2, " " );
    
  • ➩为了解决这个问题,String encodedString = UrlEscapers.urlFragmentEscaper().escape(inputString); 使用了一些RegularExpression

    String.replaceAll()
  • ➩使用java.text.Normalizer.Form。 此枚举提供了Unicode Standard Annex #15中描述的四种Unicode规范化表单的常量 - Unicode规范化表单以及两种访问它们的方法。

    enter image description here

    // \p{Z} or \p{Separator}: any kind of whitespace or invisible separator.
    s2 = s2.replaceAll("\\p{Zs}", " ");
    
    
    s2 = s2.replaceAll("[^\\p{ASCII}]", " ");
    s2 = s2.replaceAll(" ", " ");
    

测试不同方法的字符串和输出,例如➩Unidecode,Normalizer,StringUtils

s2 = Normalizer.normalize(s2, Normalizer.Form.NFKC);

使用 Unidecode best choice,我的最终代码如下所示。

String strUni = "Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ Æ,Ø,Ð,ß";

// This is a funky String AE,O,D,ss
String initials = Unidecode.decode( strUni );

// Following Produce this o/p: Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ Æ,Ø,Ð,ß
String temp = Normalizer.normalize(strUni, Normalizer.Form.NFD);
Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
temp = pattern.matcher(temp).replaceAll("");

String input = org.apache.commons.lang3.StringUtils.stripAccents( strUni );

答案 8 :(得分:2)

我建议Junidecode。它不仅可以处理'Ł'和'Ø',而且还可以很好地从其他字母表(例如中文)转换成拉丁字母。

答案 9 :(得分:1)

如果没有库,使用正则表达式和规范化器的最佳方法之一是:

    public String flattenToAscii(String s) {
                if(s == null || s.trim().length() == 0)
                        return "";
                return Normalizer.normalize(s, Normalizer.Form.NFD).replaceAll("[\u0300-\u036F]", "");
}

这比replaceAll(“ [^ \ p {ASCII}]”,“”))和如果不需要变音符号(就像您的示例)那样,效率更高。

>

否则,您必须使用p {ASCII}模式。

致谢。

答案 10 :(得分:0)

万一有人在Kotlin中苦苦挣扎,此代码的工作原理就像是一种魅力。为避免不一致,我还使用.toUpperCase和Trim()。然后我强制执行此功能:

   fun stripAccents(s: String):String{

   if (s == null) {
      return "";
   }

val chars: CharArray = s.toCharArray()

var sb = StringBuilder(s)
var cont: Int = 0

while (chars.size > cont) {
    var c: kotlin.Char
    c = chars[cont]
    var c2:String = c.toString()
   //these are my needs, in case you need to convert other accents just Add new entries aqui
    c2 = c2.replace("Ã", "A")
    c2 = c2.replace("Õ", "O")
    c2 = c2.replace("Ç", "C")
    c2 = c2.replace("Á", "A")
    c2 = c2.replace("Ó", "O")
    c2 = c2.replace("Ê", "E")
    c2 = c2.replace("É", "E")
    c2 = c2.replace("Ú", "U")

    c = c2.single()
    sb.setCharAt(cont, c)
    cont++

}

return sb.toString()

}

要使用这些有趣的代码,请执行以下操作:

     var str: String
     str = editText.text.toString() //get the text from EditText
     str = str.toUpperCase().trim()

     str = stripAccents(str) //call the function

答案 11 :(得分:0)

我认为最好的解决方案是将每个字符转换为十六进制并用另一个十六进制替换。这是因为有2种Unicode类型输入:

Composite Unicode
Precomposed Unicode

例如,复合Unicode编写的“Ồ”与预组合Unicode编写的“Ồ”不同。您可以复制我的示例字符并将其转换以查看区别。

In Composite Unicode, "Ồ" is combined from 2 char: Ô (U+00d4) and ̀ (U+0300)
In Precomposed Unicode, "Ồ" is single char (U+1ED2)

我已经为某些银行开发了此功能,以便在将信息发送到核心银行之前转换信息(通常不支持Unicode),并且当最终用户使用多种Unicode类型输入数据时会遇到此问题。因此,我认为转换为十六进制并替换它是最可靠的方法。

相关问题