Python re.finditer match.groups() не содержит все группы из match

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

AAA
АВС1
ABC2
ABC3
AAA
АВС1
ABC2
ABC3
ABC4
азбука
AAA
АВС1
AAA

Из которого я хочу получить ABC *, которые встречаются хотя бы один раз и перед ними стоит AAA.

Проблема в том, что несмотря на то, что группа ловит то, что я хочу:

match = <_sre.SRE_Match object; span=(19, 38), match='AAA\nABC2\nABC3\nABC4\n'>

... Я могу получить доступ только к последнему совпадению группы:

match groups = ('AAA\n', 'ABC4\n')

Ниже приведен пример кода, который я использую для этой проблемы.

#! python
import sys
import re
import os

string = "AAA\nABC1\nABC2\nABC3\nAAA\nABC1\nABC2\nABC3\nABC4\nABC\nAAA\nABC1\nAAA\n"
print(string)

p_MATCHES = []
p_MATCHES.append( (re.compile('(AAA\n)(ABC[0-9]\n){1,}')) ) #   
matches = re.finditer(p_MATCHES[0],string)

for match in matches:
    strout = ''
    gr_iter=0
    print("match = "+str(match))
    print("match groups = "+str(match.groups()))
    for group in match.groups():
    gr_iter+=1
    sys.stdout.write("TEST GROUP:"+str(gr_iter)+"\t"+group) # test output
    if group is not None:
        if group != '':
            strout+= '"'+group.replace("\n","",1)+'"'+'\n'
sys.stdout.write("\nCOMPLETE RESULT:\n"+strout+"====\n")

2 ответа

Решение

Вот ваше регулярное выражение:

(AAA\r\n)(ABC[0-9]\r\n){1,}

Визуализация регулярных выражений

Debuggex Demo

Ваша цель - захватить все ABC#с, которые следуют сразу AAA, Как вы можете видеть в этой демонстрации Debuggex, все ABC#s действительно сопоставляются (они выделены желтым цветом). Однако, поскольку только часть "что повторяется"

ABC[0-9]\r\n

захватывается (находится в скобках), и его квантификатор,

{1,}

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

AAA\r\n((?:ABC[0-9]\r\n){1,})

Визуализация регулярных выражений

Debuggex Demo

Я поместил часть "что повторяется" (ABC[0-9]\r\n) в не захватывающую группу. (Я также перестал захватывать AAA, как вам, кажется, не нужно.)

Захваченный текст может быть разделен на новую строку и даст вам все фрагменты, как вы пожелаете.

(Обратите внимание, что \n само по себе не работает в Debuggex. Это требует \r\n.)


Это обходной путь. Не многие разновидности регулярных выражений предлагают возможность повторения повторяющихся захватов (какие...?). Более нормальный подход состоит в том, чтобы проходить иобрабатывать каждое совпадение по мере его обнаружения. Вот пример из Java:

   import java.util.regex.*;

public class RepeatingCaptureGroupsDemo {
   public static void main(String[] args) {
      String input = "I have a cat, but I like my dog better.";

      Pattern p = Pattern.compile("(mouse|cat|dog|wolf|bear|human)");
      Matcher m = p.matcher(input);

      while (m.find()) {
         System.out.println(m.group());
      }
   }
}

Выход:

cat
dog

(От http://ocpsoft.org/opensource/guide-to-regular-expressions-in-java-part-1/, примерно на 1/4 вниз)


Пожалуйста, рассмотрите возможность добавления закладок в FAQ по регулярным выражениям в стеке. Ссылки в этом ответе взяты из него.

Вы хотите, чтобы последовательность ABC\n происходила после AAA\n самым жадным образом. Вам также нужна только группа следующих друг за другом ABC\ n, а не кортеж из этого и самого последнего ABC\n . Итак, в вашем регулярном выражении исключите подгруппу внутри группы. Обратите внимание на образец, напишите образец, представляющий всю строку.

      AAA\n(ABC[0-9]\n)+

Затем запишите интересующий вас объект с помощью (), не забывая при этом исключить подгруппу (ы)

      AAA\n((?:ABC[0-9]\n)+)

Затем вы можете использовать findall() или finditer(). Я считаю, что findIter проще, особенно когда вы имеете дело с более чем одним захватом. finditer:-

      import re
matches_iter = re.finditer(r'AAA\n((?:ABC[0-9]\n)+)', string)

[print(i.group(1)) for i in matches_iter]

findall использовал исходный {1,} как более подробную форму +:-

      matches_all = re.findall(r'AAA\n((?:ABC[0-9]\n){1,})', string)

[[print(x) for x in y.split("\n")] for y in matches_all]
Другие вопросы по тегам