Ответ 1
Используйте группировку.
foo.*?(foo)
Есть ли способ указать регулярное выражение для соответствия каждому второму вхождению шаблона в строке?
Примеры
Используйте группировку.
foo.*?(foo)
Предположим, что вам нужен шаблон abc + d. Вы хотите совместить второе вхождение этого шаблона в строке.
Вы создадите следующее регулярное выражение:
abc+d.*?(abc+d)
Это будет соответствовать строкам формы: <your-pattern>...<your-pattern>
. Поскольку мы используем неохотный квалификатор *? мы в безопасности, что между ними не может быть другого совпадения. Используя группы сопряжений, которые в значительной степени реализуют все реализации регулярных выражений, вы затем извлекаете строку в группе в скобках, которая вам нравится.
Если вы используете С#, вы можете либо получить все совпадения одновременно (т.е. использовать Regex.Matches()
, который возвращает MatchCollection
, и проверить индекс элемента: index % 2 != 0
).
Если вы хотите найти вхождение, чтобы заменить его, используйте одну из перегрузок Regex.Replace()
которая использует MatchEvaluator
(например, Regex.Replace(String, String, MatchEvaluator)
. Вот код:
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string input = "abcdabcd";
// Replace *second* a with m
string replacedString = Regex.Replace(
input,
"a",
new SecondOccuranceFinder("m").MatchEvaluator);
Console.WriteLine(replacedString);
Console.Read();
}
class SecondOccuranceFinder
{
public SecondOccuranceFinder(string replaceWith)
{
_replaceWith = replaceWith;
_matchEvaluator = new MatchEvaluator(IsSecondOccurance);
}
private string _replaceWith;
private MatchEvaluator _matchEvaluator;
public MatchEvaluator MatchEvaluator
{
get
{
return _matchEvaluator;
}
}
private int _matchIndex;
public string IsSecondOccurance(Match m)
{
_matchIndex++;
if (_matchIndex % 2 == 0)
return _replaceWith;
else
return m.Value;
}
}
}
}
Было бы что-то вроде
(pattern.*?(pattern))*
работа для тебя?
Редактировать:
Проблема в том, что он использует не жадный оператор *?
, что может потребовать очень много возврата по строке вместо того, чтобы просто смотреть на каждую букву один раз. Это означает, что это может быть медленным для больших промежутков.
Обратные ссылки могут найти интересные решения здесь. Это регулярное выражение:
([a-z]+).*(\1)
найдет самую длинную повторяющуюся последовательность.
В этой последовательности будет повторяться последовательность из трех букв:
([a-z]{3}).*(\1)
Нет никакого "прямого" способа сделать это, но вы можете указать шаблон дважды, как в: a[^a]*a
, которые совпадают со вторым "a".
Альтернативой является использование вашего языка программирования (perl? С#?...) в соответствии с первым вступлением, а затем вторым.
EDIT. Я видел, как другие ответили, используя "неживые" операторы, которые могли бы быть хорошим способом, предполагая, что вы их используете в своей библиотеке регулярных выражений!