Упрощение упрощения Java для сравнения [дубликат]

Это очень распространенная проблема, с которой мы сталкиваемся, борясь с «таинствами» JavaScript.

Давайте начнем с простой функции JavaScript:

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

Это простой синхронный вызов функции (где каждая строка кода выполняется одна за другой в последовательность), и результат будет таким же, как ожидалось.

Теперь добавим немного завихрения, введя небольшую задержку в нашей функции, чтобы все строки кода не выполнялись последовательно. Таким образом, он будет эмулировать асинхронное поведение функции:

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

Итак, вы идете, эта задержка просто сломала функциональность, которую мы ожидали! Но что именно произошло? Ну, на самом деле это довольно логично, если вы посмотрите на код. функция foo() после выполнения ничего не возвращает (таким образом, возвращаемое значение равно undefined), но оно запускает таймер, который выполняет функцию после 1s, чтобы вернуть «wohoo». Но, как вы можете видеть, значение, присвоенное бару, является немедленно возвращенным материалом из foo (), а не что-либо еще, что приходит позже.

Итак, как мы решаем эту проблему?

Давайте попросим нашу функцию для ОБЕЩАНИЯ. Обещание действительно о том, что это означает: это означает, что функция гарантирует, что вы предоставите любой результат, который он получит в будущем. поэтому давайте посмотрим на это в нашей маленькой проблеме выше:

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when exececution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

Таким образом, резюме - для решения асинхронных функций, таких как вызовы на основе ajax и т. д., вы можете использовать обещание resolve значение (которое вы намерены вернуть). Таким образом, короче говоря, вы разрешаете значение вместо возврата в асинхронных функциях.

190
задан Erick Robertson 12 October 2014 в 16:46
поделиться

9 ответов

Используйте java.text.Normalizer, чтобы обработать это для вас.

string = Normalizer.normalize(string, Normalizer.Form.NFD);

Это отделит все знаки акцента от символов. Затем вам просто нужно сравнить каждого персонажа с буквой и выбросить те, которые не являются.

string = string.replaceAll("[^\\p{ASCII}]", "");

Если ваш текст находится в юникоде, вы должны использовать это вместо:

string = string.replaceAll("\\p{M}", "");

Для юникода \\P{M} соответствует базовому глифу, а \\p{M} (нижний регистр) соответствует каждому акценту.

Благодаря GarretWilson для указателя и regular-expressions.info для большого руководства по юникоду.

310
ответ дан Erick Robertson 26 August 2018 в 01:35
поделиться

Решение @ 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);
}

У этого варианта есть преимущество правильности той, которая используется Нормализатор и часть скорости, используемой таблицей. На моей машине это примерно в 4 раза быстрее, чем принятый ответ, а от 6,6 до 7 раз медленнее, чем @ virgo47 (принятый ответ примерно на 26 раз медленнее, чем @ virgo47 на моей машине).

46
ответ дан David Conrad 26 August 2018 в 01:35
поделиться

С 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 для Ø , я не уверен, что ее следует заменить на «O»: это отдельное письмо на норвежском и датском языках, в алфавитном порядке после «z». Это хороший пример ограничений подхода «стриптиз-акцентов».

98
ответ дан DavidS 26 August 2018 в 01:35
поделиться

В зависимости от языка эти могут не считаться акцентами (которые изменяют звук буквы), но диакритические метки

https://en.wikipedia.org/wiki/Diacritic #Languages_with_letters_containing_diacritics

«Боснийский и хорватский имеют символы č, ć, đ, š и ž, которые считаются отдельными буквами и перечислены как таковые в словарях и других контекстах, в которых указаны слова в соответствии с алфавитным порядком ».

Удаление их может по своей сути изменить значение слова или изменить буквы на совершенно разные.

6
ответ дан Krenair 26 August 2018 в 01:35
поделиться
System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""));

работал для меня. Вывод фрагмента выше дает «aee», который я хотел, но

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

не выполнял никакой замены.

19
ответ дан Nico 26 August 2018 в 01:35
поделиться

Я предлагаю Junidecode . Он будет обрабатывать не только «Ł» и «Ø», но также хорошо подходит для переписывания из других алфавитов, таких как китайский, в латинский алфавит.

0
ответ дан OlgaMaciaszek 26 August 2018 в 01:35
поделиться

Решение @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);
}
2
ответ дан Ricardo Freitas 26 August 2018 в 01:35
поделиться

EDIT: Если вы не застряли с Java & lt; 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 показывает, что он выполняет преобразование из aeelstc89FDČ в aeelstc89FDC 1 миллион раз в ~ 100 мс, в то время как способ Normalizer делает его в 3.7s (37x медленнее).

Наслаждайтесь: -)

Наслаждайтесь: -)

Наслаждайтесь: -)

25
ответ дан virgo47 26 August 2018 в 01:35
поделиться

Я столкнулся с той же проблемой, связанной с проверкой равенства строк. Одна из строк сравнения имеет код символа ASCII 128-255 .

, т. е. неразрывная space - [Hex - A0] Space [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();
}
  • ➩ ASCII транслитерации Строка Unicode для Java. unidecode
    String initials = Unidecode.decode( s2 );
    
  • ➩ с помощью Guava : Google Core Libraries for Java .
    String replaceFrom = CharMatcher.WHITESPACE.replaceFrom( s2, " " );
    
    Для URL-кодирования для пробела используйте Guava laibrary.
    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");
    }

}
1
ответ дан Yash 26 August 2018 в 01:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: