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.
}