Regex: как определить нечетное/четное число вхождений char, предшествующего данному char?

Я хотел бы заменить | на OR только в некотируемых терминах, например:

"this | that" | "the | other" -> "this | that" OR "the | other"

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

Ответы

Ответ 1

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

str = str.replace(/\|(?=(?:(?:[^"]*"){2})*[^"]*$)/g, "OR");

Прерывая это, (?:[^"]*"){2} соответствует следующей паре кавычек, если она есть, вместе с промежуточными не кавычками. После того, как вы сделали это как можно больше раз (это может быть ноль), [^"]*$ потребляет все оставшиеся не кавычки до конца строки.

Конечно, это предполагает, что текст хорошо сформирован. Он также не рассматривает проблему экранированных кавычек, но может, если вам это нужно.

Ответ 2

Регулярные выражения не учитываются. Для чего нужны парсеры.

Ответ 3

Вы можете найти Perl FAQ по этой проблеме.

#!/usr/bin/perl

use strict;
use warnings;

my $x = qq{"this | that" | "the | other"};
print join('" OR "', split /" \| "/, $x), "\n";

Ответ 4

Вам не нужно рассчитывать, потому что вы не вставляете кавычки. Это будет делать:

#!/usr/bin/perl

my $str = '" this \" | that" | "the | other" | "still | something | else"';
print "$str\n";

while($str =~ /^((?:[^"|\\]*|\\.|"(?:[^\\"]|\\.)*")*)\|/) {
        $str =~ s/^((?:[^"|\\]*|\\.|"(?:[^\\"]|\\.)*")*)\|/$1OR/;
}

print "$str\n";

Теперь объясните это выражение.

^  -- means you'll always match everything from the beginning of the string, otherwise
      the match might start inside a quote, and break everything

(...)\|   -- this means you'll match a certain pattern, followed by a |, which appears
             escaped here; so when you replace it with $1OR, you keep everything, but
             replace the |.

(?:...)*  -- This is a non-matching group, which can be repeated multiple times; we
             use a group here so we can repeat multiple times alternative patterns.

[^"|\\]*  -- This is the first pattern. Anything that isn't a pipe, an escape character
             or a quote.

\\.       -- This is the second pattern. Basically, an escape character and anything
             that follows it.

"(?:...)*" -- This is the third pattern. Open quote, followed by a another
              non-matching group repeated multiple times, followed by a closing
              quote.

[^\\"]    -- This is the first pattern in the second non-matching group. It anything
             except an escape character or a quote.

\\.       -- This is the second pattern in the second non-matching group. It an
             escape character and whatever follows it.

Результат следующий:

" this \" | that" | "the | other" | "still | something | else"
" this \" | that" OR "the | other" OR "still | something | else"

Ответ 5

Другой подход (похожий на рабочий ответ Алана М):

str = str.replace(/(".+?"|\w+)\s*\|\s*/g, '$1 OR ');

Часть внутри первой группы (на расстоянии от читаемости):

".+?"  |  \w+

... в основном означает, что-то цитируемое, или слово. Остальное означает, что за ним последовала "|" завернутый в необязательные пробелы. Замена заключается в том, что первая часть ( "$ 1" означает первую группу), а затем "OR".

Ответ 6

Возможно, вы ищете что-то вроде этого:

(?<=^([^"]*"[^"]*")+[^"|]*)\|

Ответ 7

Спасибо всем. Извинения за пренебрежение упоминанием об этом в javascript и что термины не обязательно должны быть указаны, и может быть любое количество котируемых/некотируемых слов, например:

"this | that" | "the | other" | yet | another  -> "this | that" OR "the | other" OR yet OR another 

Даниэль, кажется, что в футбольном поле, то есть в основном циклы соответствия/массажа. Спасибо за подробное объяснение. В js это выглядит как split, цикл forEach в массиве терминов, нажатие термина (после изменения | term на OR) обратно в массив и повторное объединение.

Ответ 8

@Alan M, работает красиво, избегая ненужных из-за разреженности SQL-возможностей FTS.

@epost, принятое решение для краткости и элегантности, спасибо. его нужно было просто ввести в более общую форму для юникода и т.д.

(".+?"|[^\"\s]+)\s*\|\s*

Ответ 9

Мое решение в С# для подсчета кавычек, а затем регулярное выражение для получения совпадений:

        // Count the number of quotes.
        var quotesOnly = Regex.Replace(searchText, @"[^""]", string.Empty);
        var quoteCount = quotesOnly.Length;
        if (quoteCount > 0)
        {
            // If the quote count is an odd number there a missing quote.
            // Assume a quote is missing from the end - executive decision.
            if (quoteCount%2 == 1)
            {
                searchText += @"""";
            }

            // Get the matching groups of strings. Exclude the quotes themselves.
            // e.g. The following line:
            // "this and that" or then and "this or other"
            // will result in the following groups:
            // 1. "this and that"
            // 2. "or"
            // 3. "then"
            // 4. "and"
            // 5. "this or other"
            var matches = Regex.Matches(searchText, @"([^\""]*)", RegexOptions.Singleline);
            var list = new List<string>();
            foreach (var match in matches.Cast<Match>())
            {
                var value = match.Groups[0].Value.Trim();
                if (!string.IsNullOrEmpty(value))
                {
                    list.Add(value);
                }
            }

            // TODO: Do something with the list of strings.
       }