Відповіді:
Використовуйте java.text.Normalizer
для вирішення цього для вас.
string = Normalizer.normalize(string, Normalizer.Form.NFD);
// or Normalizer.Form.NFKD for a more "compatable" deconstruction
Це відокремить усі знаки наголосу від символів. Тоді вам просто потрібно порівняти кожного символу з буквою і викинути ті, які не є.
string = string.replaceAll("[^\\p{ASCII}]", "");
Якщо ваш текст є унікодом, вам слід скористатися цим:
string = string.replaceAll("\\p{M}", "");
Для Unicode \\P{M}
відповідає основний гліф і \\p{M}
(нижній регістр) відповідає кожному акценту.
Дякуємо GarretWilson за вказівник та regular-expressions.info за чудовий посібник з унікодом .
string.replaceAll("\\p{M}", "")
. Додаткову інформацію див. У розділі regular-expressions.info/unicode.html .
Станом на 2011 рік ви можете використовувати Apache Commons StringUtils.stripAccents (вхід) (з 3.0):
String input = StringUtils.stripAccents("Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ");
System.out.println(input);
// Prints "This is a funky String"
Примітка:
Прийнята відповідь (Ерік Робертсон) не працює для Ø або Ł. Apache Commons 3.5 також не працює для Ø, але це працює для Ł. Прочитавши статтю у Вікіпедії для Ø , я не впевнений, що її слід замінити на "O": це окрема літера норвезькою та датською мовами, що надписом після "z". Це хороший приклад обмежень підходу "наголоси на смужку".
Рішення від @ virgo47 дуже швидке, але приблизне. У прийнятій відповіді використовується нормалізатор і регулярний вираз. Мені було цікаво, яку частину часу 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 [], а не зателефонувавши доCharArray (), хоча я не впевнений, що зменшення чіткості коду цього заслуговує:
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);
}
Ця зміна має перевагу в правильності використання режиму "Нормалізатор" та деякої швидкості використання "таблиці". На моїй машині цей показник приблизно в 4 рази швидший, ніж прийнята відповідь, і на 6.6x до 7 разів повільніше, ніж @ virgo47 (прийнята відповідь приблизно на 26 разів повільніше, ніж у @ virgo47's на моїй машині).
out
необхідно змінити розмір, щоб відповідати кількості дійсних символів, j
перш ніж він буде використаний для побудови об'єкта рядка.
flattenToAscii
створює результат "aa ..", де точки представляють \ u0000. Це не добре. Перше питання - як представити "ненормалізованих" персонажів? Скажімо, це буде ?, або ми можемо залишити NULL char там, але в будь-якому випадку ми маємо зберегти правильне положення їх (як це робить рішення геджекс). Для цього цикл if у циклі повинен бути чимось на кшталт: if (c <= '\u007F') out[j++] = c; else if (Character.isLetter(c)) out[j++] = '?';
Це трохи сповільнить його, але в першу чергу має бути правильним. ;-)
isLetter
) не є правильним, але я не знайшов кращого. Я не є експертом Unicode, тому не знаю, як краще визначити клас одного символу, який замінює оригінальний символ. Листи справні для більшості програм / звичаїв.
EDIT: Якщо ви не зациклювалися на Java <6 і швидкість не є критичною, та / або таблиця перекладу занадто обмежує, скористайтеся відповіддю Девідом. Сенс полягає у використанні 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);
}
Тести на моїй HW з 32-бітною JDK показують, що це здійснює перетворення з àèéľšťč89FDČ в aeelstc89FDC в 1 мільйон разів за ~ 100 мс, тоді як нормалізатор робить це в 3,7 секунди (на 37 разів повільніше). У випадку, якщо ваші потреби не відрізняються ефективністю, і ви знаєте діапазон введення, це може бути для вас.
Насолоджуйтесь :-)
System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""));
працював на мене. Вихід фрагмента вище дає "ае", що я хотів, але
System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", ""));
не робив жодної заміни.
Залежно від мови, це можуть вважатися не наголосами (які змінюють звучання букви), а діакритичними позначками
https://en.wikipedia.org/wiki/Diacritic#Languages_with_letters_contain_diacritics
"У боснійській та хорватській мовах є символи č, ć, đ, š і ž, які вважаються окремими літерами і перераховані як такі в словниках та інших контекстах, у яких слова перераховані за алфавітом".
Видалення їх може по суті змінити значення цього слова або змінити букви на зовсім інші.
Я зіткнувся з тією ж проблемою, що стосується перевірки рівності рядків. Один із рядків, що порівнюють, має код символів ASCII 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, 10132
, 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();
}
Trans транслітерації ASCII рядка Unicode для Java. unidecode
String initials = Unidecode.decode( s2 );
➩ за допомогою Guava
: Google Core Libraries for Java
.
String replaceFrom = CharMatcher.WHITESPACE.replaceFrom( s2, " " );
Для кодування URL-адреси для простору використовуйте лайблітек Guava.
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(" ", " ");
➩ Використання java.text.Normalizer.Form . Цей перелік містить константи чотирьох форм нормалізації Unicode, які описані в стандартному додатку Unicode № 15 - Форми нормалізації Unicode та два способи доступу до них.
s2 = Normalizer.normalize(s2, Normalizer.Form.NFKC);
Тестування рядків та результатів на різних підходах, таких як ➩ 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");
}
}
Я пропоную Junidecode . Він буде обробляти не тільки "Ł" та "Ø", але також добре працює для транскрибування з інших алфавітів, наприклад китайської, на латинський алфавіт.
Рішення @David Conrad - це найшвидший тест, який я намагався використовувати Normalizer, але в ньому є помилка. Це в основному смужки символів, які не є наголосами, наприклад, китайські символи та інші літери на зразок æ, всі позбавлені. Символи, які ми хочемо зняти, не мають проміжків, символи, які не займають додаткової ширини в остаточному рядку. Ці символи нульової ширини в основному поєднуються з деякими іншими символами. Якщо ви можете бачити їх ізольованими як персонаж, наприклад, подібний `, я гадаю, що він поєднується з символом пробілу.
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);
}
Одним з найкращих способів використання регулярних символів та нормалізатора, якщо у вас немає бібліотеки, є:
public String flattenToAscii(String s) {
if(s == null || s.trim().length() == 0)
return "";
return Normalizer.normalize(s, Normalizer.Form.NFD).replaceAll("[\u0300-\u036F]", "");
}
Це більш ефективно, ніж substituAll ("[^ \ p {ASCII}]", ""), і якщо вам не потрібні діакритики (як у вашому прикладі).
В іншому випадку вам доведеться використовувати шаблон {{ASCII}.
З повагою
Я думаю, що найкраще рішення - перетворити кожну таблицю на HEX та замінити її іншою HEX. Це тому, що є 2 типи Unicode:
Composite Unicode
Precomposed Unicode
Наприклад, "Ồ", написаний Composite 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. Тому я вважаю, що перехід на HEX та його заміна - це найнадійніший спосіб.
Якщо хтось бореться за це в котліні, цей код працює як шарм. Щоб уникнути невідповідностей, я також використовую .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