Слияние с 685ec97 (третья ступень NER для юридических реквизитов),
код приведён к google-java-format, добавлены юнит-тесты
AdaptiveConcurrencyLimiter/SystemsConfig/PayloadCipher.
141 lines
9.2 KiB
Java
141 lines
9.2 KiB
Java
package ru.pdguard.detect;
|
||
|
||
/**
|
||
* Общие фрагменты регулярных выражений, переиспользуемые между группами правил в {@link
|
||
* RuleRegistry}. Вынесены отдельно, чтобы не дублировать их в каждой группе — «серия и номер»,
|
||
* разрывы между якорем и значением, формы дат и т.п. встречаются в правилах разных категорий
|
||
* (документы, банк, ФИО, адрес).
|
||
*/
|
||
final class RulePatterns {
|
||
|
||
private RulePatterns() {}
|
||
|
||
/**
|
||
* Слово с заглавной буквы; остальные буквы любого регистра, чтобы «ИВАНОВ» распознавался наравне
|
||
* с «Иванов».
|
||
*/
|
||
static final String CAPITALISED = "\\p{Lu}[\\p{Lu}\\p{Ll}]+";
|
||
|
||
/** Якорное слово-основа: держатель карты, держателем и т.п. */
|
||
static final String HOLDER_STEM = "держател";
|
||
|
||
/** Разрыв между якорем и значением, когда между ними ролевое слово («ИНН плательщика»). */
|
||
static final String ROLE_GAP = "(?:\\s+[\\p{L}-]+){0,5}\\W{0,10}";
|
||
|
||
/**
|
||
* То же самое, но только строчные слова-филлеры: ролевые слова перед значением гражданства всегда
|
||
* строчные («бенефициара», «поручителя»), а само значение — с заглавной («Республики»,
|
||
* «Соединенные»). Обычный {@link #ROLE_GAP} жадно поглощал бы и заглавное слово значения как
|
||
* будто это ролевое слово, оставляя значение только хвостом («Республики Беларусь» → «Беларусь»).
|
||
*/
|
||
static final String CITIZENSHIP_GAP = "(?:\\s+\\p{Ll}[\\p{L}-]*){0,5}\\W{0,10}";
|
||
|
||
/**
|
||
* Название улицы: от одного до трёх слов с заглавной буквы либо чисел — «Тверская», «Малая
|
||
* Никитская», «8 Марта». Ограничение по форме обязательно: без него правило дожёвывало строку до
|
||
* конца, и «Проспект Вернадского перекрыт до вечера» оказывался под маской целиком.
|
||
*/
|
||
static final String STREET_NAME =
|
||
"(?:\\p{Lu}[\\p{L}-]+|\\d+[\\p{L}-]*)(?:\\s+(?:\\p{Lu}[\\p{L}-]+|\\d+[\\p{L}-]*)){0,2}";
|
||
|
||
/**
|
||
* Фамилия по словообразованию: Иванов, Ковалёва, Троицкий, Шевченко, Мкртчян. Хвост из двух букв
|
||
* покрывает падежные окончания: Ковалёв-ой, Иванов-а.
|
||
*/
|
||
static final String SURNAME =
|
||
"\\p{Lu}[\\p{Lu}\\p{Ll}]*(?iu:ов|ев|ёв|ин|ын|ск(?:ий|ая|ого|ой|ом)|цк(?:ий|ая)"
|
||
+ "|енко|ко|ук|юк|ян|швили|дзе)\\p{L}{0,2}";
|
||
|
||
/**
|
||
* Отчество: признак надёжный, ни одно другое слово так не оканчивается. Основы даны без падежного
|
||
* окончания — Иванович, Ивановича, Ивановне.
|
||
*/
|
||
static final String PATRONYMIC =
|
||
"\\p{Lu}[\\p{Lu}\\p{Ll}]+(?iu:ович|евич|ьич|мич|нич|тич|лич|кич|бич|сич"
|
||
+ "|овн|евн|иничн|ичн)\\p{L}{0,2}";
|
||
|
||
/**
|
||
* Серия и номер: «4509 123456», «45 09 123456», «4509123456», «45 09 № 123456», а также с
|
||
* произвольным числом пробелов и словом «номер» между частями — «12 34 номер 567890» (реальный
|
||
* кейс из бланка).
|
||
*/
|
||
static final String SERIES_AND_NUMBER =
|
||
"\\d{2}\\s*\\d{2}(?:\\s*(?:№|N|номер)\\s*|[\\s№N]{0,3})\\d{6}";
|
||
|
||
/**
|
||
* Название месяца: полная форма («январь»), сокращение («янв») и плейсхолдер «ммм» (в логах
|
||
* встречается и латинская «M»). Сокращения нужны, потому что в датах вида «15 ЯНВ 10» месяц
|
||
* записан тремя буквами.
|
||
*/
|
||
static final String MONTH =
|
||
"(?iu:январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр"
|
||
+ "|янв|фев|мар|апр|авг|сен|окт|ноя|дек|[МM]мм)\\p{L}*";
|
||
|
||
/** Числовая запись при любом порядке частей: дд.мм.гггг, мм/дд/гггг, гггг-мм-дд. */
|
||
static final String DATE_DIGITS = "\\b\\d{1,4}[.\\-/]\\d{1,2}[.\\-/]\\d{1,4}\\b";
|
||
|
||
/** «15 03 1990», «15 03 10» — числовая дата с пробелами вместо разделителей. */
|
||
static final String DATE_DIGITS_SPACE = "\\b\\d{1,2}\\s+\\d{1,2}\\s+\\d{2,4}\\b";
|
||
|
||
/** «15 03», «15/03» — день и месяц без года. */
|
||
static final String DATE_DAY_MONTH = "\\b\\d{1,2}\\s*[-/.]?\\s*\\d{1,2}\\b";
|
||
|
||
/** «12 мая 1985 г.», «15-ЯНВ-10», «15 января» — месяц словом, год 2-4 цифры или без года. */
|
||
static final String DATE_MONTH_WORD =
|
||
"\\b\\d{1,2}\\s*[-/.]?\\s*"
|
||
+ MONTH
|
||
+ "\\s*[-/.]?\\s*(?:\\d{2,4})?\\b"
|
||
+ "(?:\\s*(?iu:года|г\\.|г\\b))?";
|
||
|
||
/** «двенадцатого мая тысяча девятьсот восемьдесят пятого года» */
|
||
static final String DATE_WORDS =
|
||
"\\b(?:(?iu:двадцать|тридцать)\\s+)?"
|
||
+ "(?iu:перв|втор|треть|четв[её]рт|пят|шест|седьм|восьм|девят|десят|одиннадцат|двенадцат"
|
||
+ "|тринадцат|четырнадцат|пятнадцат|шестнадцат|семнадцат|восемнадцат|девятнадцат|двадцат|тридцат)"
|
||
+ "(?iu:ьего|ого|его|ое)\\s+"
|
||
+ MONTH
|
||
+ "\\s+(?:\\d{4}|(?iu:тысяча)(?:\\s+\\p{L}+){1,8})\\s*(?iu:года|год\\b|г\\.)";
|
||
|
||
/** Любая из записей даты; внутри только незахватывающие группы. */
|
||
static final String DATE_ANY =
|
||
"(?:"
|
||
+ DATE_WORDS
|
||
+ "|"
|
||
+ DATE_MONTH_WORD
|
||
+ "|"
|
||
+ DATE_DIGITS
|
||
+ "|"
|
||
+ DATE_DIGITS_SPACE
|
||
+ "|"
|
||
+ DATE_DAY_MONTH
|
||
+ ")";
|
||
|
||
/**
|
||
* Промежуток между якорем даты («дата рождения») и самой датой: слова, скобочные группы («(день и
|
||
* месяц)») и знаки препинания. Без скобочной ветки «Дата рождения клиента (день и месяц): 15
|
||
* января» не находилась бы: «день и месяц» — это слова, а не дата. Ветка со словами требует
|
||
* пробела перед словом ({@code \s+}), иначе она неоднозначна с веткой {@code \W}, которая тоже
|
||
* матчит пробелы, — это приводило к катастрофическому возврату на длинных текстах. Отдельная
|
||
* ветка с дефисом нужна для слитных слов без пробела внутри: «клиента-нерезидента» — дефис сам по
|
||
* себе ловится веткой {@code \W}, но следующие за ним буквы без пробела перед ними не покрывала
|
||
* ни одна ветка.
|
||
*/
|
||
static final String DATE_GAP = "(?:\\s+\\([^)]*\\)|\\s+\\p{L}+|-\\p{L}+|\\W){0,30}";
|
||
|
||
/**
|
||
* Значение гражданства: «рф»/«росс…»(любая форма, включая строчную «российское»)/ «республики X»
|
||
* — частые формы отдельным списком; последняя ветка — страна из 1-4 слов с заглавной буквы
|
||
* («Армения», «Соединенные Штаты Америки»). Хвост идёт после якоря «гражданств», поэтому
|
||
* «Двойное» перед якорем не попадёт.
|
||
*/
|
||
static final String CITIZENSHIP_VALUE =
|
||
"\\p{Lu}\\p{Ll}+(?:[\\s/]+\\p{Lu}\\p{Ll}+){0,3}|\\p{Ll}+(?:[\\s/]+\\p{Ll}+){0,3}";
|
||
|
||
/**
|
||
* Слова, при которых адрес/имя принадлежит организации, а не человеку: адрес отделения банка
|
||
* персональными данными не является.
|
||
*/
|
||
static final String ORGANISATION_NEARBY =
|
||
"(?iu:отделени|филиал|банкомат|доп\\.?\\s?офис|офис|головн|юридическ\\p{L}*\\s+адрес)";
|
||
}
|