Можно ли использовать Regex для этой конкретной операции со строками? - PullRequest
7 голосов
/ 26 сентября 2008

Мне нужно заменить символ (скажем) x на символ (скажем) P в строке, но только если он содержится в кавычке подстроки Пример проясняет ситуацию:

axbx'cxdxe'fxgh'ixj'k  -> axbx'cPdPe'fxgh'iPj'k

Предположим, для простоты, что кавычки всегда идут парами.

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

Мой целевой язык - C #, но я предполагаю, что мой вопрос касается любого языка, имеющего встроенную или библиотечную поддержку регулярных выражений.

Ответы [ 9 ]

9 голосов
/ 26 сентября 2008

Я преобразовал код Python Грега Хьюгилла в C #, и это сработало!

[Test]
public void ReplaceTextInQuotes()
{
  Assert.AreEqual("axbx'cPdPe'fxgh'iPj'k", 
    Regex.Replace("axbx'cxdxe'fxgh'ixj'k",
      @"x(?=[^']*'([^']|'[^']*')*$)", "P"));
}

Этот тест пройден.

8 голосов
/ 26 сентября 2008

Я смог сделать это с Python:

>>> import re
>>> re.sub(r"x(?=[^']*'([^']|'[^']*')*$)", "P", "axbx'cxdxe'fxgh'ixj'k")
"axbx'cPdPe'fxgh'iPj'k"

Для этого нужно использовать сопоставление без захвата (? = ...), чтобы убедиться, что символ x находится внутри строки в кавычках. Он ищет некоторые символы не в кавычках вплоть до следующей кавычки, затем ищет последовательность либо отдельных символов, либо групп символов в кавычках до конца строки.

Это зависит от вашего предположения, что котировки всегда сбалансированы. Это тоже не очень эффективно.

2 голосов
/ 30 сентября 2008

Более общее (и более простое) решение, которое допускает непарные кавычки.

  1. Найти цитируемую строку
  2. Заменить '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
    
2 голосов
/ 26 сентября 2008
#!/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;
    }    
}

Разве этот кусок не сделал бы работу?

2 голосов
/ 26 сентября 2008

Хитрость заключается в том, чтобы использовать группу без захвата, чтобы соответствовать части строки после совпадения (символ 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|
1 голос
/ 26 сентября 2008
Pattern:     (?s)\G((?:^[^']*'|(?<=.))(?:'[^']*'|[^'x]+)*+)x
Replacement: \1P
  1. \G & mdash; Закрепите каждое совпадение в конце предыдущего или в начале строки.
  2. (?:^[^']*'|(?<=.)) & mdash; Если оно находится в начале строки, соответствует первой кавычке.
  3. (?:'[^']*'|[^'x]+)*+ & mdash; Совпадение любого блока символов без кавычек или любых (не кавычек) символов до 'x'.

Один проход по исходной строке, за исключением просмотра одного символа.

1 голос
/ 26 сентября 2008

Аналогичное обсуждение замены сбалансированного текста: Можно ли использовать регулярные выражения для сопоставления с вложенными шаблонами?

Хотя вы можете попробовать это в Vim, но это работает хорошо, только если строка находится в одной строке, и есть только одна пара символов.

:%s:\('[^']*\)x\([^']*'\):\1P\2:gci

Если есть еще одна пара или даже несбалансированная ', то она может потерпеть неудачу. Таким образом, я включил флаг подтверждения c a.k.a. в команду ex.

То же самое можно сделать с помощью sed, без взаимодействия - или с awk, чтобы вы могли добавить некоторое взаимодействие.

Одно из возможных решений - разбить строки на пары ' s, тогда вы можете сделать это с помощью vim solution.

1 голос
/ 26 сентября 2008

Не с простым регулярным выражением. Регулярные выражения не имеют «памяти», поэтому они не могут различить кавычки «внутри» или «снаружи».

Вам нужно что-то более мощное, например, используя gema , это будет просто:

'<repl>'=$0
repl:x=P
0 голосов
/ 26 сентября 2008

Извините, что разбил ваши надежды, но для этого вам нужен автомат с нажатием. Здесь больше информации: Автомат Pushdown

Короче говоря, регулярные выражения, которые являются конечными автоматами, могут только читать и не имеют памяти, в то время как у автомата нажатия есть стек и возможности манипулирования.

Редактировать: правописание ...

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