Регулярное выражение не находит совпадения — ошибка в разнице между .find() и .matches()?

#java #regex

#java #регулярное выражение

Вопрос:

Некоторая предыстория перед кодом — я поддерживаю систему, которая по сути является автоматизированным решением SFTP, которое опрашивает удаленный сервер и отправляет некоторые файлы на нашу сторону, когда находит файлы в определенном каталоге, которые соответствуют списку утвержденных шаблонов регулярных выражений.

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

Вот пример шаблона, который был и остается нормально работающим в системе, а также пример файла, который мы получили, который соответствует ему (PickUp — это каталог, в котором происходит весь опрос файлов):

 /PickUp/(?<foreignid>\d{9})/rhinoceros\d{9}\d{8}.txt

/PickUp/123456789/rhinoceros98765432120201110.txt
 

Вот новое, которое не:

 /PickUp/(?<foreignid>\d{9})_\d{8}_\d{10}.zip

/PickUp/123456789_20201110_1234567890.zip
 

Ниже приведена часть функции, выполняющей сопоставление:

 int numMatches = 0;
for (PatternItem item : patternItems)
{
    matcher = item.getPattern().matcher(fileFound);
    if (matcher.find())
    {
        successfulMatch = matcher.group("foreignid");
        numMatches  ;
    }
}

if (numMatches == 1)
{
    //download the file in here
}
else if (numMatches > 1)
{
    logger.error("More than one match found for file: "   fileFound   " - regex may be too permissive.");
    return "UNKNOWN_MULTI_MATCH";
}
else
{
    logger.error("Could not find match for file: "   fileFound);
    return "UNKNOWN_NO_MATCH";
}
 

Я проверил, что шаблон действительно соответствует файлу, и что правильный шаблон включен в список шаблонов. Мой вопрос в том, возможно ли, что ошибка является результатом использования matcher.find() вместо matcher.matches() ?Похоже, что из моего чтения других ответов это matches() для полных совпадений, в то время find() как для частичных совпадений. Должен ли я, на самом деле, использовать matches() для своего варианта использования?

Если да, то почему это еще не сломалось для любых других шаблонов, все из которых похожи на рабочие, показанные выше? Единственное отличие здесь в том, что это первый ZIP-файл, а также первый раз, когда группа foreignid не представляет подпапку, содержащую сам файл — это просто часть имени.

Заранее спасибо за любую предоставленную помощь / ясность.

Комментарии:

1. Требуется больше контекста. Можете ли вы настроить минимальный исполняемый пример, в котором возникает эта проблема?

2. Я предполагаю, что фактические символы не такие, какими вы их считаете. Например, существуют разные символы косой черты, которые имеют разные кодовые точки в Юникоде, но выглядят одинаково, включая косую черту деления и косую черту дроби . Или могут быть непечатаемые символы, такие как выделение слева направо .

3. @xehpuk Это сложно сделать для демонстрации здесь, потому что система вращается вокруг таблицы DynamoDB с GSI, в которой находятся шаблоны. Вы можете запустить Hello World здесь, чтобы убедиться, что эти шаблоны и файлы будут совпадать: pastebin.com/gEwAvzk3

Ответ №1:

Проблема ни в том, ни в другом.

Я изменил ваш код следующим образом (для того, чтобы я мог запустить его на своей машине):

 public static void main(String[] args) {
    
    String regex = "/PickUp/(?<foreignid>\d{9})/rhinoceros\d{9}\d{8}.txt";
    String input = "/PickUp/123456789/rhinoceros98765432120201110.txt";
    // String regex = "/PickUp/(?<foreignid>\d{9})_\d{8}_\d{10}.zip";
    // String input = "/PickUp/123456789_20201110_1234567890.zip";
    
    
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(input);
    
    if (matcher.find()) {
        System.out.println(matcher.groupCount());
    } else {
        System.err.println("No match");
    }
}
 

Независимо от того, какой из них вы используете find() или matches() , ваши входные данные будут соответствовать только одной группе, как показано groupCount() . Похоже, ваша проблема заключается в различиях в шаблонах. Ваш /PickUp/(?<foreignid>\d{9})/rhinoceros\d{9}\d{8}.txt соответствует подкаталогу PickUp , а не имени файла. Нижний шаблон не использует первую числовую группу в качестве подкаталога. Вместо этого он использует его как часть имени файла; и соответствует только первой серии чисел.

Я не уверен, что это сделано специально, но проблема заключается в самих шаблонах; особенно в использовании «lookahead» (с использованием вопросительного знака внутри круглых скобок).

Комментарии:

1. Возможно, я был недостаточно ясен. Цель состоит в том, чтобы сопоставить шаблон, а затем извлечь группу, что полезно при обработке файлов. В утвердительном случае (файл / шаблон rhinoceros) выполняется сопоставление, затем группа foreignid сохраняется и используется в обработке. В проблемном случае совпадение вообще никогда не выполняется, хотя это должно быть путем моего тестирования простого шаблона против ввода. Мне интересно, связано ли это с каким-то поведением find() против matches(), учитывая, что я зацикливаю все шаблоны на вводе, используя один и тот же сопоставитель.

2. @Rome_Leader Между этими двумя методами есть различия, но не для предоставленных вами входных данных. Запустите приведенный выше код для проверки самостоятельно. Я не думаю, что вы делаете правильное сравнение.