Соответствовать при каждом втором появлении

Есть ли способ указать регулярное выражение для соответствия каждому второму вхождению шаблона в строку?

Примеры

  • поиск по против строки ABCDABCD должны найти одно вхождение в положении 5
  • поиск ab по строке abcdabcd должен найти одно вхождение в позиции 5
  • поиск dab по строке abcdabcd не должен найти вхождений
  • поиске в отношении строки AAAA должны найти два вхождения в положениях 2 и 4

Ответов (6)

Решение

Используйте группировку.

foo.*?(foo)

Обратные ссылки могут найти здесь интересные решения. Это регулярное выражение:

([a-z]+).*(\1)

найдет самую длинную повторяющуюся последовательность.

Здесь будет повторяться последовательность из 3 букв:

([a-z]{3}).*(\1)

Хотел бы что-нибудь

(pattern.*?(pattern))*

работать на вас?

Редактировать:

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

Предположим, что вам нужен шаблон abc + d. Вы хотите сопоставить второе вхождение этого шаблона в строке.

Вы бы построили следующее регулярное выражение:

abc+d.*?(abc+d)

Это будет соответствовать строки вида: <your-pattern>...<your-pattern> . Поскольку мы используем неохотный квалификатор *? мы в безопасности, потому что не может быть другого матча между ними. Используя группы сопоставления, которые предоставляют практически все реализации регулярных выражений, вы затем извлекаете строку в группе в квадратных скобках, которая вам и нужна.

Нет "прямого" способа сделать это, но вы можете указать шаблон дважды, как в:, a[^a]*a которые соответствуют до второго "a".

Альтернативой является использование вашего языка программирования (perl? C#? ...) для соответствия первому вхождению, а затем второму.

РЕДАКТИРОВАТЬ : Я видел, как другие отвечали, используя «нежадные» операторы, которые могут быть хорошим способом, если они у вас есть в вашей библиотеке регулярных выражений!

Если вы используете C#, вы можете получить все совпадения сразу (т.е. использовать Regex.Matches(), который возвращает a 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;
            }
        }
    }
}