Regex: замена строки только в подстроке - PullRequest
1 голос
/ 04 ноября 2019

У меня есть специальный формат файла, в котором мне нужно заменить десятки строк и переформатировать его структуру. В качестве простейшего решения я подготовил файл шаблонов, в котором хранятся все определения / замены регулярных выражений (~ 100 замен). Я использую Perl для поиска и замены шаблонов (perl -p patterns source.file). Пока все хорошо.

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

Пример: Для простоты мне нужно заменить все буквы от "A" до "X "только в средней строке (ограничена;).

Строка ввода:

ABCD ABCD; ABCD ABCD; ABCD ABCD

Ожидаемый результат:

ABCD ABCD; XBCD XBCD; ABCD ABCD
           ^    ^
           the only replaced characters

Это правильно заменяет все символы:

s/A/X/g;

Но мне нужно заменить запятые только в среднем поле. Я попытался:

s/(.*?;.*?)A/\1X/g;
s/(.*?;.*)A(.*?;)/\1X\2/g;  # alternative to find the last A

Но это заменяет либо первый А. У меня может быть несколько таких шаблонов, чтобы повторить процедуру поиска и замены, но это не похоже на хорошее решение, так как я не знаю, сколько АЯ буду иметь в подстроке.

Я пытался играть со взглядом, но безуспешно. Пожалуйста, обратите внимание, мне просто нужно определение регулярного выражения, которое я мог бы использовать в моем файле шаблонов (т.е. без кода perl). Кроме того, я могу использовать sed или awk для обработки этого случая, но я не слишком знаком с ним.

Спасибо, сообщество!

Regex101: https://regex101.com/r/Ic4ciA/1

Ответы [ 2 ]

1 голос
/ 04 ноября 2019

Perl один вкладыш:

echo 'ABCD ABCD; ABCD ABCD; ABCD ABCD' | perl -pe 's/(?:.+?;|\G).*?\KA(?=.*?;)/X/g'
ABCD ABCD; XBCD XBCD; ABCD ABCD

Объяснение:

(?:             # non capture group
    .+?         # 1 or more any character but newline, not greedy
    ;           # semicolon
  |             # OR
    \G          # restart from last match position
)               # end group
.*?             # 0 or more any character but newline, not greedy
\K              # forget all we have seen until  this position
A               # letter A
(?=             # positive lookahead, make sure we have after:
    .*?         # 0 or more any character but newline, not greedy
    ;           # a semicolon
)               # end lookahead

Демо

0 голосов
/ 04 ноября 2019

Я не знаю чистого способа сделать это за один раз, используя только инструмент регулярных выражений. Но если вы открыты для более итеративного подхода, его можно довольно легко обработать на любом языке сценариев. Вот скрипт Python, который выполняет свою работу:

inp = "ABCD ABCD; ABCD ABCD; ABCD ABCD"
parts = inp.split(';')

index = 1
while index < len(parts)-1:
    parts[index] = parts[index].replace('A', 'X')
    index += 1

output = ';'.join(parts)
print(output)

Это печатает:

ABCD ABCD; XBCD XBCD; ABCD ABCD

Подход состоит в разделении входной строки на точку с запятой, генерируя список терминов. Затем выполните итерацию от второго до второго термина, выполнив замену буквы A на X. Наконец, объединитесь, чтобы получить желаемый результат.

...