Слияние с 685ec97 (третья ступень NER для юридических реквизитов),
код приведён к google-java-format, добавлены юнит-тесты
AdaptiveConcurrencyLimiter/SystemsConfig/PayloadCipher.
51 lines
3.2 KiB
Java
51 lines
3.2 KiB
Java
package ru.pdguard.detect;
|
|
|
|
import java.util.Locale;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Словарь населённых пунктов России — проверка того, что значение, пойманное правилом {@code
|
|
* ADDRESS_CITY}, действительно похоже на существующий город, село, посёлок или другой населённый
|
|
* пункт, а не на произвольное слово с заглавной буквы после якоря.
|
|
*
|
|
* <p>Не только официальные города (~1100 по классификатору): перепись добавляет сёла, деревни,
|
|
* хутора, станицы — «рп. Ильинское», «с. Кукуево» из ТЗ находятся ровно за счёт неё. Какой
|
|
* конкретно тип населённого пункта стоит перед названием, определяет якорь самого правила в {@link
|
|
* RuleRegistry}, а не этот словарь — он только подтверждает, что название реальное.
|
|
*
|
|
* <p>Сравнение по началу слова, а не точным совпадением: падежные окончания («в Москве», «из
|
|
* Казани») тем самым покрываются без отдельного разбора морфологии, как и у известных людей в
|
|
* {@link NameDictionary}.
|
|
*/
|
|
public final class ToponymDictionary {
|
|
|
|
private static final Set<String> SETTLEMENT_STEMS =
|
|
ResourceLoader.set("/names/settlements.txt").stream()
|
|
.map(Declension::withoutInflectedEnding)
|
|
.collect(java.util.stream.Collectors.toUnmodifiableSet());
|
|
|
|
private ToponymDictionary() {}
|
|
|
|
/**
|
|
* Похоже ли значение на название населённого пункта из словаря в любом падеже.
|
|
*
|
|
* <p>Названия на согласную склоняются добавлением окончания («Тамбов» → «Тамбове»), поэтому
|
|
* начало слова из словаря — уже достаточный признак. Названия на гласную меняют последнюю букву
|
|
* («Москва» → «Москве»), для них сравнение идёт по основе без неё — так же, как с личными именами
|
|
* в {@link NameDictionary}.
|
|
*
|
|
* <p>Проверяются префиксы значения по множеству, а не каждая из ~80 000 основ по значению:
|
|
* перебор списка на каждое совпадение правила был бы на порядки дороже, чем нужно — префиксов у
|
|
* слова не больше, чем в нём букв.
|
|
*/
|
|
public static boolean isKnownSettlement(String value) {
|
|
String lower = value.strip().toLowerCase(Locale.ROOT);
|
|
for (int length = lower.length(); length > 0; length--) {
|
|
if (SETTLEMENT_STEMS.contains(lower.substring(0, length))) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|