Рекомендации по улучшению RegEx - PullRequest
0 голосов
/ 02 июля 2018

Учитывая строку как

Некоторый текст и [A ~ Token] и еще текст и [не токен] и [Другой ~ лексема]

Мне нужно извлечь "жетоны" для последующей замены. Токены определены как два идентификатора, разделенных символом ~ и заключенных в []. Я использовал $string -match "\[.*?~.*?\]", который работает. И, насколько я понимаю, я убираю обе скобки, выполняя любой символ ноль или более раз и вынужденный ленивый, затем ~ и затем ту же самую последовательность символов. Итак, моим первым улучшением было заменить .*? на .+?, так как я хочу 1 или больше, а не ноль или больше. Затем я перешел на $string -match "\[[A-Za-z0-9]+~[A-Za-z0-9]+\]", который ограничивает два идентификатора буквенными цифрами, что является большим улучшением. Итак, первый вопрос: Является ли это последнее решение лучшим подходом, или есть дальнейшие улучшения?

Кроме того, в настоящее время я получаю только один возвращенный токен, поэтому я перебираю строку, заменяю токены по мере их обнаружения и зацикливаюсь до тех пор, пока токенов не будет. Но, насколько я понимаю, по умолчанию RegEx является жадным, и поэтому я ожидал, что эта последняя версия выдаст два токена, и я мог бы циклически проходить по словарю, а не использовать цикл While. Итак, второй вопрос: Что я делаю не так, что получаю только один матч назад? Или я неправильно понимаю, как работает жадное сопоставление?

EDIT: чтобы уточнить, я использую $ match, как показано здесь, и все еще получаю счет только 1.

if ($string -match "\[[A-Za-z0-9]+~[A-Za-z0-9]+\]") {
    Write-Host "new2: $($matches.count)"
    foreach ($key in $matches.keys) {
        Write-Host "$($matches.$key)"
    }
}

Кроме того, я не могу использовать прямую замену в момент идентификации токена, потому что существует ТОНА потенциальных замен. Я беру токен, обрезаю квадратные скобки, затем разделяю на ~, чтобы получить значения префикса и суффикса, которые затем идентифицируют конкретное значение замены, которое я могу сделать с выделенным -relace. И последнее уточнение: количество токенов является переменным. Это может быть только один, это может быть три или четыре. Поэтому мое решение должно быть довольно гибким.

Ответы [ 3 ]

0 голосов
/ 02 июля 2018

Взяв пример строки

$String = "Some text and [A~Token] and more text and [not a token] and [another~token]"

Это RegEx с группами захвата

$RegEx = [RegEx]"\[(\w+~\w+)\][^\[]+\[[^\]]+\][^\[]+\[(\w+~\w+)\]"
if ($string -match $RegEX){
   "First token={0} Second token={1}" -f $matches[1],$matches[2]
}

возвращается:

First token=A~Token Second token=another~token

См. Выше RegEx, объясненный на https://regex101.com/r/tp6b9e/1

Область между двумя токенами совпадает с отрицательными классами для [ / ] и буквального символа [ / ]

0 голосов
/ 02 июля 2018

Чтобы перечислить все токены и использовать значения, вы можете использовать следующий код:

$matces = Select-String  '\[([\w]+)~([\w]+)\]' -input $string -AllMatches | Foreach {$_.matches}
foreach($value in  $matces){
    $fullToken = $value.Value;
    $firstPart = $value.Groups[1].Value;
    $secondPart = $value.Groups[2].Value;
    echo "full token found: '$fullToken' first part: '$firstPart' second part: '$secondPart'";
}

Примечание в регулярных выражениях, сгруппированных с (), позволяет получить доступ к частям вашего токена.

В этом цикле вы можете найти подходящее значение, которое вы хотите вставить вместо fullToken, используя firstPart и secondPart.

Что касается \[.*?~.*?\], который не работает должным образом, потому что он пытается сопоставить и успешно выполняет текст [not a token] and [another~token], так как в этом регулярном выражении символы ][ разрешены в частях токена. \[[^\]\[]*?~[^\]\[]*?\] (^ отрицает выражение, чтобы оно читалось: все символы, кроме ][) также было бы хорошо, но его нельзя было прочитать со всеми фигурными скобками, если \w достаточно хорошо, вы должны его использовать.

0 голосов
/ 02 июля 2018

Вы можете использовать \w, чтобы соответствовать символу слова (буква, цифра, подчеркивание). Это приводит к шаблону \[\w+~\w+\].
Теперь вы можете создать объект регулярного выражения с таким шаблоном:

$rgx = [Regex]::new($pattern)

и замените все вхождения этого шаблона оператором Replace:

$rgx.Replace($inputstring, $replacement)

Возможно, также стоит отметить, что регулярное выражение имеет оператор .Match, который возвращает первый вхождение шаблона, и оператор .Matches, который возвращает все вхождения шаблона.

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