Может ли Regex использоваться для этой конкретной манипуляции с строкой?
Мне нужно заменить символ (скажем) x символом (скажем) P в строке, но только если он содержится в указанной подстроке.
Пример делает это более ясным:
axbx'cxdxe'fxgh'ixj'k -> axbx'cPdPe'fxgh'iPj'k
Предположим, для простоты, что цитаты всегда попадают в пары.
Очевидным способом является просто обрабатывать строку один символ за раз (простой подход с использованием конечных автоматов),
однако, мне интересно, можно ли использовать регулярные выражения для выполнения всей обработки за один раз.
Мой целевой язык - это С#, но, я думаю, мой вопрос относится к любому языку, имеющему встроенную или библиотечную поддержку для регулярных выражений.
Ответы
Ответ 1
Я смог сделать это с помощью Python:
>>> import re
>>> re.sub(r"x(?=[^']*'([^']|'[^']*')*$)", "P", "axbx'cxdxe'fxgh'ixj'k")
"axbx'cPdPe'fxgh'iPj'k"
Что это значит, это использование несоответствующего соответствия (? =...), чтобы проверить, что символ x находится в кавычной строке. Он ищет некоторые неквотированные символы до следующей цитаты, затем ищет последовательность как одиночных символов, так и кавычек групп символов до конца строки.
Это зависит от вашего предположения, что котировки всегда сбалансированы. Это также не очень эффективно.
Ответ 2
Я преобразовал код Python Грега Hewgill в С#, и он сработает!
[Test]
public void ReplaceTextInQuotes()
{
Assert.AreEqual("axbx'cPdPe'fxgh'iPj'k",
Regex.Replace("axbx'cxdxe'fxgh'ixj'k",
@"x(?=[^']*'([^']|'[^']*')*$)", "P"));
}
Этот тест прошел.
Ответ 3
Хитрость заключается в том, чтобы использовать группу, не связанную с захватом, для соответствия части строки после соответствия (символ x), который мы ищем.
Попытка сопоставить строку до x будет находить только первое или последнее вхождение, в зависимости от того, используются ли неживые кванторы.
Здесь идея Грега перенесена в Tcl с комментариями.
set strIn {axbx'cxdxe'fxgh'ixj'k}
set regex {(?x) # enable expanded syntax
# - allows comments, ignores whitespace
x # the actual match
(?= # non-matching group
[^']*' # match to end of current quoted substring
##
## assuming quotes are in pairs,
## make sure we actually were
## inside a quoted substring
## by making sure the rest of the string
## is what we expect it to be
##
(
[^']* # match any non-quoted substring
| # ...or...
'[^']*' # any quoted substring, including the quotes
)* # any number of times
$ # until we run out of string :)
) # end of non-matching group
}
#the same regular expression without the comments
set regexCondensed {(?x)x(?=[^']*'([^']|'[^']*')*$)}
set replRegex {P}
set nMatches [regsub -all -- $regex $strIn $replRegex strOut]
puts "$nMatches replacements. "
if {$nMatches > 0} {
puts "Original: |$strIn|"
puts "Result: |$strOut|"
}
exit
Отпечатки:
3 replacements.
Original: |axbx'cxdxe'fxgh'ixj'k|
Result: |axbx'cPdPe'fxgh'iPj'k|
Ответ 4
#!/usr/bin/perl -w
use strict;
# Break up the string.
# The spliting uses quotes
# as the delimiter.
# Put every broken substring
# into the @fields array.
my @fields;
while (<>) {
@fields = split /'/, $_;
}
# For every substring indexed with an odd
# number, search for x and replace it
# with P.
my $count;
my $end = $#fields;
for ($count=0; $count < $end; $count++) {
if ($count % 2 == 1) {
$fields[$count] =~ s/a/P/g;
}
}
Разве этот кусок не выполнит эту работу?
Ответ 5
Более общее (и более простое) решение, которое позволяет не парные кавычки.
- Найти строку с кавычками
-
Замените 'x' на 'P' в строке
#!/usr/bin/env python
import re
text = "axbx'cxdxe'fxgh'ixj'k"
s = re.sub("'.*?'", lambda m: re.sub("x", "P", m.group(0)), text)
print s == "axbx'cPdPe'fxgh'iPj'k", s
# -> True axbx'cPdPe'fxgh'iPj'k
Ответ 6
Не с обычным регулярным выражением. Регулярные выражения не имеют "памяти", поэтому они не могут различать "внутренние" или "внешние" кавычки.
Вам нужно что-то более мощное, например, используя gema, это было бы несправедливо:
'<repl>'=$0
repl:x=P
Ответ 7
Аналогичная дискуссия о сбалансированном тексте заменяет: Можно ли использовать регулярные выражения для соответствия вложенным шаблонам?
Хотя вы можете попробовать это в Vim, но он работает хорошо, только если строка находится в одной строке и есть только одна пара.
:%s:\('[^']*\)x\([^']*'\):\1P\2:gci
Если есть еще одна пара или даже неуравновешенный, то это может потерпеть неудачу. Таким образом, я включил флаг c
a.k.a. в команду ex
.
То же самое можно сделать с sed без взаимодействия - или с awk
, чтобы вы могли добавить какое-то взаимодействие.
Одно из возможных решений состоит в том, чтобы разбить линии на пары '
, тогда вы можете сделать это с помощью решения vim.
Ответ 8
Pattern: (?s)\G((?:^[^']*'|(?<=.))(?:'[^']*'|[^'x]+)*+)x
Replacement: \1P
-
\G
— Якорь каждого совпадения в конце предыдущего или начало строки.
-
(?:^[^']*'|(?<=.))
— Если он находится в начале строки, совпадайте с первой цитатой.
-
(?:'[^']*'|[^'x]+)*+
— Сопоставьте любой блок некотируемых символов или любые (не кавычные) символы до "x".
Один прогон по исходной строке, за исключением одиночного символа.
Ответ 9
Извините, что нарушал ваши надежды, но для этого вам нужны пусковые автоматы. Здесь больше информации:
Pushdown Automaton
Короче говоря, регулярные выражения, которые являются машинами с конечным состоянием, могут читать и не иметь памяти, тогда как автомат pushdown имеет стек и управляет возможностями.
Изменить: правописание...