В общем, я бы рекомендовал использовать Collator
с настройкой силы Collator.PRIMARY
для сравнения строк, содержащих акценты и различные варианты (например, N
против n
и é
против e
). К сожалению, Collator
не имеет функции contains()
.
Таким образом, мы сделаем нашу собственную.
private static boolean contains(String source, String target) {
if (target.length() > source.length()) {
return false;
}
Collator collator = Collator.getInstance();
collator.setStrength(Collator.PRIMARY);
int end = source.length() - target.length() + 1;
for (int i = 0; i < end; i++) {
String sourceSubstring = source.substring(i, i + target.length());
if (collator.compare(sourceSubstring, target) == 0) {
return true;
}
}
return false;
}
Это перебирает исходную строку и проверяет, каждая ли подстрока сДлина, соответствующая цели поиска, равна цели поиска в том, что касается Collator.
Например, давайте представим, что нашей исходной строкой является "This is a Tèst"
, и мы ищем слово "test"
. Этот метод будет перебирать каждую подстроку из четырех букв:
This
his
is i
s is
is
is a
s a
a T
a Tè
Tès
Tèst
и вернет true, как только найдет совпадение. Так как сила установлена на Collator.PRIMARY
, сборщик считает, что "Tèst"
и "test"
равны, и поэтому наш метод возвращает true
.
Вполне возможно, что нужно сделать больше оптимизацийк этому методу, но это должно быть разумной отправной точкой.
Редактировать : Одна из возможных оптимизаций заключается в использовании ключей сопоставления, а также известных деталей реализации RuleBasedCollator
и RuleBasedCollationKey
(при условии, что у вас есть Google Guava в вашем проекте):
private static boolean containsBytes(String source, String target) {
Collator collator = Collator.getInstance();
collator.setStrength(Collator.PRIMARY);
byte[] sourceBytes = dropLastFour(collator.getCollationKey(source).toByteArray());
byte[] targetBytes = dropLastFour(collator.getCollationKey(target).toByteArray());
return Bytes.indexOf(sourceBytes, targetBytes) >= 0;
}
private static byte[] dropLastFour(byte[] in) {
return Arrays.copyOf(in, in.length - 4);
}
Это значительно более хрупко (вероятно, не работает для всех языков), но в моих тестах это примерно в 2-10 раз быстрее.
Редактировать : для поддержки выделения вы должны преобразовать contains()
в indexOf()
, а затем использовать эту информацию:
private static int indexOf(String source, String target) {
if (target.length() > source.length()) {
return -1;
}
Collator collator = Collator.getInstance();
collator.setStrength(Collator.PRIMARY);
int end = source.length() - target.length() + 1;
for (int i = 0; i < end; i++) {
String sourceSubstring = source.substring(i, i + target.length());
if (collator.compare(sourceSubstring, target) == 0) {
return i;
}
}
return -1;
}
И затем вы можете применить еекак это:
String guestWholeName = guest.getGuestFirstName() + " " + guest.getGuestLastName();
int wholeNameIndex = indexOf(guestWholeName, searchText);
if (wholeNameIndex > -1) {
Timber.d("guest name first : guest.getGuestFirstName() %s", guest.getGuestFirstName());
Timber.d("guest name last : guest.getGuestLastName() %s", guest.getGuestLastName());
int endPos = wholeNameIndex + searchText.length();
Spannable spannable = new SpannableString(guestWholeName);
Typeface firstNameFont = Typeface.createFromAsset(context.getAssets(), "fonts/Graphik-Semibold.otf");
spannable.setSpan(new CustomTypefaceSpan("", firstNameFont), wholeNameIndex, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Objects.requireNonNull(guestName).setText(spannable);
} else {
Objects.requireNonNull(guestName).setText(guestWholeName);
}