Как не соответствовать первой пустой строке в этом регулярном выражении? - PullRequest
1 голос
/ 10 июля 2019

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

Описание высокого уровня

Я получаю строку ввода, которая содержит две интересующие меня информации:

  • Имя версии, которое 3.1.build и еще что-нибудь позже
  • Идентификатор сборки, который somenumbers-somenumbers-eitherwordsornumbers-somenumbers

Мне нужно извлечь их отдельно.

Подробнее о входах

У меня есть вход, который может поступать 4 разными способами:

Образец 1 : v3.1.build.dev.12345.team 12345-12345-cici-12345 (пробелы между ними сначала равны \t, а затем пробелами).

Образец 2 : v3.1.build.dev.12345.team 12345-12345-12345-12345 (это очень похоже на первый пример, за исключением того, что во второй части у нас есть только цифры и -, без буквенных символов).

Образец 3 :

v3.1.build.dev.12345.team
12345-12345-cici-12345

(приведенное выше очень похоже на пример 1, за исключением того, что вместо \t и пробелов есть просто новая строка.

Образец 4 :

v3.1.build.dev.12345.team
12345-12345-12345-12345

(то же, что и выше, только цифры и тире во второй строке).

Обратите внимание, что в образце 3 и образце 4 после обеих строк есть некоторые пробелы (здесь не видно).

Подводя итог, это 4 возможных входа:

    String str1 = "v3.1.build.dev.12345.team\t\t\t\t\t  12345-12345-cici-12345";
    String str2 = "v3.1.build.dev.12345.team\t\t\t\t\t  12345-12345-12345-12345";
    String str3 = "v3.1.build.dev.12345.team   \n12345-12345-cici-12345   ";
    String str4 = "v3.1.build.dev.12345.team   \n12345-12345-12345-12345   ";

Мой код сейчас

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

    String versionPattern = "^.+[\\s]";
    String buildIdPattern = "[\\s].+";

    Pattern pVersion = Pattern.compile(versionPattern);
    Pattern pBuildId = Pattern.compile(buildIdPattern);

    for (String str : possibilities) {
        Matcher mVersion = pVersion.matcher(str);
        Matcher mBuildId = pBuildId.matcher(str);
        while(mVersion.find()) {
            System.out.println("Version found: \"" +  mVersion.group(0).replaceAll("\\s", "") + "\"");
        }
        while (mBuildId.find()) {
            System.out.println("Build-id found: \"" +  mBuildId.group(0).replaceAll("\\s", "") + "\"");
        }
    }

Проблема, с которой я сталкиваюсь

Приведенный выше код работает, в значительной степени. Однако в Примере 3 и Примере 4 (те, где идентификатор сборки отделен версией с \n), я получаю два совпадения: первое - просто "", второе - это одно Хотелось бы.

Я не чувствую, что этот код стабилен, и я думаю, что что-то не так с шаблоном регулярных выражений для соответствия идентификатору сборки:

    String buildIdPattern = "[\\s].+";

Есть ли у кого-нибудь идеи, чтобы исключить первое пустое совпадение в идентификаторе сборки для примеров 3 и 4 при сохранении всех остальных совпадений? Или какой-нибудь лучший способ написать регулярные выражения (я открыт для улучшений, не большой эксперт по регулярным выражениям)?

Ответы [ 4 ]

1 голос
/ 10 июля 2019

На основании вашего описания похоже, что ваши данные в форме

NonWhiteSpaces whiteSpaces NonWhiteSpaces (optionalWhiteSpaces)

и вы хотите получить только NonWhiteSpaces запчасти.

Это может быть достигнуто различными способами. Одним из них будет trim() вашей строки, чтобы избавиться от потенциальных конечных пробелов, а затем split в пробелах (теперь они должны быть только в середине строки). Что-то вроде

String[] arr = data.trim().split("\\s+");// \s also represents line separators like \n \r
String version = arr[0];
String buildID = arr[1];
1 голос
/ 10 июля 2019

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

public class Other {

    static String patternStr = "^([\\S]{1,})([\\s]{1,})(.*)";

    static String str1 = "v3.1.build.dev.12345.team\t\t\t\t\t  12345-12345-cici-12345";
    static String str2 = "v3.1.build.dev.12345.team\t\t\t\t\t  12345-12345-12345-12345";
    static String str3 = "v3.1.build.dev.12345.team   \n12345-12345-cici-12345   ";
    static String str4 = "v3.1.build.dev.12345.team   \n12345-12345-12345-12345   ";

    static Pattern pattern = Pattern.compile(patternStr);

    public static void main(String[] args) {

        List<String> possibilities = Arrays.asList(str1, str2, str3, str4);

        for (String str : possibilities) {

            Matcher matcher = pattern.matcher(str);

            if (matcher.find()) {
                System.out.println("Version found:  \"" +  matcher.group(1).replaceAll("\\s", "") + "\"");

                System.out.println("Some whitespace found: \"" +  matcher.group(2).replaceAll("\\s", "") + "\"");

                System.out.println("Build-id found: \"" +  matcher.group(3).replaceAll("\\s", "") + "\"");
            } else {
                System.out.println("Pattern NOT found");
            }

            System.out.println();
        }
    }
}

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

Капитал S в [\\S] в основном означает совпадать со всем, кроме [\\s]. .+ хорошо сработало в вашем случае, но все, что на самом деле говорит, - это сопоставить все, что не пусто - даже пробел. Это не обязательно плохо, но будет неприятно, если вам когда-нибудь придется изменять регулярное выражение.

{1,} Простые средства one or more occurrences. {1,2}, чтобы привести другой пример, будет 1 или 2 вхождения. К вашему сведению, + обычно означает 0 или 1 вхождение (возможно, не в Java), а * означает одно или несколько вхождений.

Круглые скобки обозначают группы. Всё совпадение - группа 0. Когда вы добавляете круглые скобки, порядок слева направо представляет группу 1 .. группу N. Итак, что я сделал, так это объединил ваши шаблоны, используя группы, разделенные одним или несколькими вхождениями пробела. (.*) используется для группы 2, поскольку эта группа может иметь как пробельные, так и непробельные символы, если только она не начинается с пробела.

Если у вас есть какие-либо вопросы, не стесняйтесь спрашивать. Для записи, ваш текущий код подходит, если вы просто добавите «+» в шаблон buildId: [\\s]+.+.

Без этого ваше регулярное выражение говорит: match the whitespace that is followed by no characters or a single character. Поскольку за всем вашим пробелом следует больше пробелов, вы соответствуете только одному пробелу.

1 голос
/ 10 июля 2019

TLDR;

Используйте шаблон ^(v\\S+)\\s+(\\S+), где группы захвата захватывают версию и build соответственно, вот полный фрагмент:

String unitPattern ="^(v\\S+)\\s+(\\S+)";

    Pattern pattern = Pattern.compile(unitPattern);

    for (String str : possibilities) {
        System.out.println("Analyzing \"" + str + "\"");
        Matcher matcher = pattern.matcher(str);


        while(matcher.find()) {
            System.out.println("Version found: \"" +  matcher.group(1) + "\"");
            System.out.println("Build-id found: \"" +  matcher.group(2) + "\"");
        }

    }

Попробуйте поиграть.

Nitty Gritties

Причина появления пустых строк на выходе

Это из-за того, как класс Matcher интерпретирует .;. НЕ соответствует символам новой строки, он перестает совпадать непосредственно перед \n.Для этого вам нужно добавить флаг Pattern.DOTALL, используя Pattern.compile(String pattern, int flags).

Попытка

Но даже с Pattern.DOTALL выВы все равно не сможете соответствовать из-за того, как вы определили шаблон.Лучшим подходом является сопоставление полной сборки и версии как единицы , а затем извлечение необходимых частей.

^(v\\S+)\\s+(\\S+)

Это работает, когда:

  • ^(v\\S+) определяет запуск устройства, а также захватывает информацию о версии
  • \\s+ соответствует вкладкам, новой строке, пробелам и т. Д.
  • (\\S+) захватывает окончательный непрерывный идентификатор сборки
1 голос
/ 10 июля 2019

(^v\w.+)\s+(\d+-\d+-\w+-\d+)\s*

Захватит 2 группы.Один захватит первый раздел (v3.1.build.dev.12345.team), второй получит последний раздел (12345-12345-cici-12345)

Он разбивается следующим образом: (^v\w.+) обеспечиваетчто строка начинается с av, а затем захватывает все символы, являющиеся цифрой или буквой (остановка на пробелах и т. д.) \s+ соответствует любому пробелу или символам табуляции / новой строки и т. д. столько раз, сколько это возможно.(\d+-\d+-\w+-\d+) это считывает это, гарантируя, что это соответствует указанному вами форматированию.Обратите внимание, что это все равно будет читаться через тире, что облегчит разделение строки после получения необходимой информации.Если вы хотите, вы могли бы даже создать эти собственные группы захвата, чтобы было еще проще получить вашу информацию.

Затем она заканчивается на \s* только для того, чтобы убедиться, что она не испорчена концевым пробелом.Он использует * вместо +, потому что мы не хотим, чтобы он ломался, если нет пробела.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...