Как мне использовать re() в Python и возвращать группы захвата в операторе «If»?

#python #regex

#python #регулярное выражение

Вопрос:

Хотя я использую Perl много лет, у меня всегда были проблемы с чем-то большим, чем простое использование регулярных выражений в языке. Сейчас ситуация только хуже, поскольку я пытаюсь выучить Python… и использование re () для меня еще более непонятно.

Я пытаюсь проверить соответствие, есть ли подстрока в строке, используя re(), а также использую группы захвата для извлечения некоторой информации из процесса сопоставления. Однако я не могу заставить вещи работать в нескольких контекстах; при использовании вызова re () и присвоении возвращаемых значений всем в операторе «if» .. и как справиться с ситуацией, когда элементы .groups не определены в объектах сопоставления (когда сопоставление не выполнено).

Итак, ниже приведены примеры того, что я пытаюсь сделать, закодированные на Perl и Python, с их соответствующими выводами.

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

Perl-код:

 use strict;
use warnings;

my ($idx, $dvalue);

while (my $rec = <DATA>) {
   chomp($rec);
   if ( ($idx, $dvalue) = ($rec =~ /^XA([0-9] )=(.*?)!/) ) {
      printf("  Matched:n");
      printf("    rec: >%s<n", $rec);
      printf("    index = >%s<  value = >%s<n", $idx, $dvalue);

   } elsif ( ($idx, $dvalue) = ($rec =~ /^PZ([0-9] )=(.*?[^#])!/) ) {
      printf("  Matched:n");
      printf("    rec: >%s<n", $rec);
      printf("    index = >%s<  value = >%s<n", $idx, $dvalue);

   } else {
      printf("n  Unknown Record format, \%s\nn", $rec);

   }
}
close(DATA);

exit(0)      

__DATA__
DUD=ABC!QUEUE=D23!
XA32=7!P^=32!
PZ112=123^!PQ=ABC!
  

Вывод на Perl:

   Unknown Record format, DUD=ABC!QUEUE=D23!

  Matched:
    rec: >XA32=7!P^=32!<
    index = >32<  value = >7<
  Matched:
    rec: >PZ112=123^!PQ=ABC!<
    index = >112<  value = >123^<
  

Код Python:

 import re

string = 'XA32=7!P^=32!'

with open('data.dat', 'r') as fh:
   for rec in fh:
      orec = '    rec: >'   rec.rstrip('n')   '<'
      print(orec)

      # always using 'string' at least lets this program run          
      (index, dvalue) = re.search(r'^XA([0-9] )=(.*?[^#])!', string).groups()

      # The following works when there is a match... but fails with an error when
      # a match is NOT found, viz:-
      # ...    
      #     (index, dvalue) = re.search(r'^XA([0-9] )=(.*?[^#])!', rec).groups()
      #
      #   Traceback (most recent call last):
      #     File "T:tmpa.py", line 13, in <module>
      #       (index, dvalue) = re.search(r'^XA([0-9] )=(.*?[^#])!', rec).groups()
      #   AttributeError: 'NoneType' object has no attribute 'groups'
      #

      buf = '    index = >'   index   '<'   '  value = >'   dvalue   '<'     
      print(buf)

exit(0)      
  

содержимое data.dat:

 DUD=ABC!QUEUE=D23!
XA32=7!P^=32!
PZ112=123^!PQ=ABC!
  

Вывод на Python:

     rec: >DUD=ABC!QUEUE=D23!<
    index = >32<  value = >7<
    rec: >XA32=7!P^=32!<
    index = >32<  value = >7<
    rec: >PZ112=123^!PQ=ABC!<
    index = >32<  value = >7<
  

Еще одна разработка: еще немного кода, который поможет мне лучше понять это… но я не уверен, когда / как использовать match.group() or match.groups()

Код Python:

 import re

rec = 'XA22=11^!S^=64!ABC=0,0!PX=0!SP=12B!'
print("rec = >{}<".format(rec))

# ----

index = 0 ; dvalue = 0 ; x = 0 
match = re.match(r'XA([0-9] )=(.*?[^#])!(.*?)!', rec) 
if match:
   (index, dvalue, x) = match.groups()
   print("3 ():  index = >{}< value = >{}< x = >{}<".format(index, dvalue, x))

# ----

index = 0 ; dvalue = 0 ; x = 0 
match = re.match(r'XA([0-9] )=(.*?[^#])!', rec) 
if match:
   (index, dvalue) = match.groups()
   print("2 ():  index = >{}< value = >{}< x = >{}<".format(index, dvalue, x))

# ----

index = 0 ; dvalue = 0 ; x = 0 
match = re.match(r'XA([0-9] )=', rec) 
if match:
    #(index) = match.groups()  # Why doesn't this work like above examples!?
   (index, ) = match.groups()  # ...and yet this works!?
                               # Does match.groups ALWAYS returns a tuple!?
   #(index) = match.group(1)    # This also works; 0 = entire matched string?
   print("1 ():  index = >{}< value = >{}< x = >{}<".format(index, dvalue, x))

# ----

index = 0 ; dvalue = 0 ; x = 0 
match = re.search(r'S^=([0-9] )!', rec) 
if match:
   (index, ) = match.groups()  # Returns tuple(?!)
   print("1 ():  index = >{}< value = >{}< x = >{}<".format(index, dvalue, x))
  

Опять же, я был бы признателен за любые мысли о том, какой способ является «предпочтительным» .. или если есть другой способ работы с группами.

Комментарии:

1. Если вы хотите узнать о регулярных выражениях, я бы рекомендовал прочитать Джеффри Фридла — Освоение регулярных выражений .

2. @StefanBecker Я хорошо знаю книгу Friedl.. но мне трудно разобраться (очень похоже на «Программирование на Python» О’Рейли.. Фух! ). Я чаще всего ссылаюсь на Кулинарную книгу по регулярным выражениям Гойвартса и Левитана. .

3. Лучше всего, если вы скажете, чего вы хотите достичь, а не предоставляете это на другом языке. (Если, конечно, проблема не в понимании кода Perl, и в этом случае это должен быть ваш вопрос.) Поскольку этот вопрос имеет не больше отношения к Perl, чем к английскому, я удаляю тег.

Ответ №1:

Сначала вам нужно проверить соответствие, а затем использовать группы. Т.е.

  • скомпилируйте регулярные выражения (необязательно для большинства случаев в настоящее время, согласно документации)
  • применяйте каждое регулярное выражение к строке, чтобы сгенерировать объект соответствия
    • match() совпадает только в начале строки, т. Е. с неявным ^ привязкой
    • search() совпадает с любым местом в строке
  • проверьте, является ли объект соответствия допустимым
    • извлеките группы
    • переход к следующей итерации цикла
 # works with Python 2 and Python 3
import re

with open('dummy.txt', 'r') as fh:
    for rec in fh:
        orec = '    rec: >'   rec.rstrip('n')   '<'
        print(orec)

        match = re.match(r'XA([0-9] )=(.*?[^#])!', rec)
        if match:
            (index, dvalue) = match.groups()
            print("    index = >{}<  value = >{}<".format(index, dvalue))
            continue

        match = re.match(r'PZ([0-9] )=(.*?[^#])!', rec)
        if match:
            (index, dvalue) = match.groups()
            print("    index = >{}<  value = >{}<".format(index, dvalue))
            continue

        print("    Unknown Record format")
  

Вывод:

 $ python dummy.py
    rec: >DUD=ABC!QUEUE=D23!<
    Unknown Record format
    rec: >XA32=7!P^=32!<
    index = >32<  value = >7<
    rec: >PZ112=123^!PQ=ABC!<
    index = >112<  value = >123^<
  

Но мне интересно, почему вы не упростите свой код на Perl и Python, просто используя вместо этого одно регулярное выражение? Например.:

 match = re.match(r'(?:XA|PZ)([0-9] )=(.*?[^#])!', rec)
if match:
    (index, dvalue) = match.groups()
    print("    index = >{}<  value = >{}<".format(index, dvalue))
else:
    print("    Unknown Record format")
  

Комментарии:

1. Есть несколько отличных идей, спасибо. В «реальной» программе ‘XA’, ‘PZ’ и т.д. насчитывают около 10 элементов… и выполняемые действия различаются в зависимости от каждого «тега», отсюда и эффективное действие «переключения»… но хорошая мысль.