除了使用String.replaceAll()
方法并逐个替换字母之外,是否有更好的方法来摆脱重音并使这些字母变得规则?例:
输入:orčpžsíáýd
输出:orcpzsiayd
它不需要包括所有带有重音符号的字母,如俄语字母或中文字母。
使用java.text.Normalizer
为您处理此问题。
string = Normalizer.normalize(string, Normalizer.Form.NFD);
// or Normalizer.Form.NFKD for a more "compatable" deconstruction
这会将所有重音符号与字符分开。然后,你只需要将每个角色与一个字母进行比较,然后扔掉那些不是。
string = string.replaceAll("[^\\p{ASCII}]", "");
如果你的文字是unicode,你应该使用它:
string = string.replaceAll("\\p{M}", "");
对于unicode,\\P{M}
匹配基本字形,\\p{M}
(小写)匹配每个重音。
感谢GarretWilson的指针和regular-expressions.info为伟大的unicode指南。
如果没有库,使用正则表达式和规范化程序的最佳方法之一是:
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}模式。
问候。
如果有人在kotlin中努力做到这一点,这个代码就像一个魅力。为了避免不一致,我还使用.UpperCase和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
截至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”之后按字母顺序排列。这是“条带重音”方法局限性的一个很好的例子。
@ 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倍)。
编辑:如果你没有坚持Java <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倍)中进行转换。如果您的需求与性能有关并且您知道输入范围,这可能适合您。
请享用 :-)
System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""));
为我工作。上面代码片段的输出给出了“aee”,这是我想要的,但是
System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", ""));
没有做任何替代。
根据语言的不同,这些可能不会被视为重音(这会改变字母的声音),而是变音符号
https://en.wikipedia.org/wiki/Diacritic#Languages_with_letters_containing_diacritics
“波斯尼亚语和克罗地亚语有符号č,ć,đ,š和ž,它们被视为单独的字母,并在字典和其他语境中按字母顺序列出。
删除它们可能会固有地改变单词的含义,或者将字母改为完全不同的字母。
我建议Junidecode。它不仅可以处理'Ł'和'Ø',而且还可以很好地从其他字母表(如中文)转换成拉丁字母。
@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);
}
我遇到了与字符串相等检查相关的相同问题,其中一个比较字符串有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,32
,68,97,116,97] S2 [77,121,-30, -128, -125
,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
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();
}
unidecode
String initials = Unidecode.decode( s2 );
Guava
:Google Core Libraries for Java
。
String replaceFrom = CharMatcher.WHITESPACE.replaceFrom( s2, " " );
对于URL编码,for the space使用番石榴图书馆。
String encodedString = UrlEscapers.urlFragmentEscaper().escape(inputString);
String.replaceAll()
和一些RegularExpression
。
// \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(" ", " ");
s2 = Normalizer.normalize(s2, Normalizer.Form.NFKC);
测试不同方法的字符串和输出,如Un Unidecode,Normalizer,StringUtils。
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 );
使用Unidecode是best choice
,我的最终代码如下所示。
public static void main(String[] args) {
String s1 = "My Sample Space Data", s2 = "My Sample Space Data";
String initials = Unidecode.decode( s2 );
if( s1.equals(s2)) { //[ , ] %A0 - %2C - %20 « http://www.ascii-code.com/
System.out.println("Equal Unicode Strings");
} else if( s1.equals( initials ) ) {
System.out.println("Equal Non Unicode Strings");
} else {
System.out.println("Not Equal");
}
}