2016-06-30 1 views
2

[Последующие от моего старого question с лучшего описания и ссылки]Регулярное выражение для сравнения ничего между комбинацией цитат

Попытка любой символ (включая символ новой строки, символы табуляции, пробелов и т.д.) между двумя символы, включая эти символы.

Например:

foobar89\n\nfoo\tbar; '''blah blah blah'8&^"'''

должны соответствовать

''blah blah blah'8&^"'''

и

fjfdaslfdj; '''blah\n blah\n\t\t blah\n'8&^"'''

необходимость соответствовать

'''blah\n blah\n\t\t blah\n'8&^"'''

Мой Python код (взято и адаптировано из here), на который я тестирование регулярных выражений:

import collections 
import re 

Token = collections.namedtuple('Token', ['typ', 'value', 'line', 'column']) 

def tokenize(code): 
    token_specification = [ 
     ('BOTH',  r'([\'"]{3}).*?\2'), # for both triple-single quotes and triple-double quotes 
     ('SINGLE', r"('''.*?''')"),  # triple-single quotes 
     ('DOUBLE', r'(""".*?""")'),  # triple-double quotes 
     # regexes which match OK 
     ('COM',  r'#.*'), 
     ('NEWLINE', r'\n'),   # Line endings 
     ('SKIP', r'[ \t]+'),  # Skip over spaces and tabs 
     ('MISMATCH',r'.'),   # Any other character 
    ] 

    test_regexes = ['COM', 'BOTH', 'SINGLE', 'DOUBLE'] 

    tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification) 
    line_num = 1 
    line_start = 0 
    for mo in re.finditer(tok_regex, code): 
     kind = mo.lastgroup 
     value = mo.group(kind) 
     if kind == 'NEWLINE': 
      line_start = mo.end() 
      line_num += 1 
     elif kind == 'SKIP': 
      pass 
     elif kind == 'MISMATCH': 
      pass 
     else: 
      if kind in test_regexes: 
       print(kind, value) 
      column = mo.start() - line_start 
      yield Token(kind, value, line_num, column) 

f = r'C:\path_to_python_file_with_examples_to_match' 

with open(f) as sfile: 
    content = sfile.read() 

for t in tokenize(content): 
    pass #print(t) 

где file_with_examples_to_match является:

import csv, urllib 

class Q(): 
    """ 
    This class holds lhghdhdf hgh dhghd hdfh ghd fh. 
    """ 

    def __init__(self, l, lo, d, m): 
     self.l= l 
     self.lo= longitude 
     self.depth = d 
     self.m= m 

    def __str__(self): 
     # sdasda fad fhs ghf dfh 
     d= self.d 
     if d== -1: 
      d= 'unknown' 
     m= self.m 
     if m== -1: 
      d= 'unknown' 

     return (m, d, self.l, self.lo) 

foobar89foobar; '''blah qsdkfjqsv,;sv 
        vqùlvnqùv 
        dqvnq 
        vq 
        v 

blah blah'8&^"''' 
fjfdaslfdj; '''blah blah 
    blah 
    '8&^"''' 

С this answer, я стараюсь r"('''.*?''')|"r'(""".*?""") соответствовать оба случая тройные одиночные кавычки и тройные двойные кавычки без успеха. То же самое при попытке r'([\'"]{3}).*?\2').

Я установил тег регулярного выражения online, где некоторые из регулярных выражений соответствуют, как они предполагалось, но когда в коде выше они терпят неудачу.

Я заинтересован в получении понимания в регулярных выражениях Python, поэтому я был бы признателен за решение (возможно, правильное регулярное выражение для выполнения нужного соответствия в моем коде) и краткое объяснение, чтобы я мог видеть мои недостатки.

+1

Я думаю, что не понимаю, что вы ищете. Из-за жадного характера регулярных выражений python '. *' Должен захватывать что-либо между двумя апострофами, включая любые апострофы. В чем именно проблема? –

+0

@JasonBray Проблема в том, что я пытаюсь сопоставить что-либо между тремя последовательными двойными кавычками или тремя последовательными одинарными кавычками. Когда я использую регулярные выражения 'r '(' ''. *? '' ')" ',' R' ("" ". *?" "") '',' R' ([\ '"] {3 }). *? \ 2 ') ', хотя онлайн-тестеры regex показывают, что эти регулярные выражения соответствуют друг другу, когда они используются в коде в моем описании, они не совпадают. Ищете для понимания, почему. –

ответ

1

Вы, вероятно, отсутствует флаги, чтобы сделать . матч новой строки также

re.finditer(tok_regex, code, flags = re.DOTALL) 

В этом случае выход

('BOTH', '"""\n This class holds lhghdhdf hgh dhghd hdfh ghd fh.\n """') 
('COM', '# sdasda fad fhs ghf dfh\n  d= self.d\n  if d== -1:\n   d= \'unknown\'\n  m= self.m\n  if m== -1:\n   d= \'unknown\'\n\n  return (m, d, self.l, self.lo)\n\nfoobar89foobar; \'\'\'blah qsdkfjqsv,;sv\n     vq\xc3\xb9lvnq\xc3\xb9v \n     dqvnq\n     vq\n     v\n\nblah blah\'8&^"\'\'\'\nfjfdaslfdj; \'\'\'blah blah\n  blah\n \'8&^"\'\'\'') 

COM теперь соответствие слишком много, так как . теперь получает все, конец файла. Если мы изменим эту модель немного, чтобы сделать его менее жадными

('COM',  r'#.*?$') 

теперь мы можем использовать re.MULTILINE, чтобы сделать его меньше совпадений

re.finditer(tok_regex, code, flags = re.DOTALL | re.MULTILINE) 

Выход теперь

('BOTH', '"""\n This class holds lhghdhdf hgh dhghd hdfh ghd fh.\n """') 
('COM', '# sdasda fad fhs ghf dfh') 
('BOTH', '\'\'\'blah qsdkfjqsv,;sv\n     vq\xc3\xb9lvnq\xc3\xb9v \n     dqvnq\n     vq\n     v\n\nblah blah\'8&^"\'\'\'') 
('BOTH', '\'\'\'blah blah\n  blah\n \'8&^"\'\'\'') 

Если вы определенно не хотите использовать флаги, вы можете использовать своего рода «взломать», чтобы обойтись без ., так как этот метасимвол соответствует почти всем, кроме строк новой строки. Вы можете создать группу соответствия, которая соответствовала бы всем, кроме одного символа, который вряд ли будет присутствовать в файлах, которые вы проанализировали. Например, вы можете использовать символ с кодом ASCII 0.Regex для такого символа будет \x00, соответствующий шаблон [^\x00] будет соответствовать каждому символу (даже символам новой строки), за исключением символа с кодом ASCII 0 (вот почему это хак, вы не можете сопоставить каждый символ без флагов). Вы должны будете держать начальное регулярное выражение для COM, и BOTH было бы

('BOTH',  r'([\'"]{3})[^\x00]*?\2') 

Очень рекомендуется для работы с регулярным выражением онлайн инструменты, объясняющие их, как regex101

Для более сложных случаев цитаты соответствия вам нужно написать парсер. См. Например, это Can the csv format be defined by a regex? и это When you should NOT use Regular Expressions?.

+0

Я понимаю, почему регулярные выражения не подходят для такого рода проблем, и что действительно парсер более подходит (отличные ссылки между прочим) .Однако, это больше подходит для меня, чтобы получить точное представление об ограничениях регулярных выражений. В этом случае, достаточно хорошее регулярное выражение, которое будет соответствовать любому из трех одиночных или двойных кавычек. –

+0

Это прекрасно. Я никогда бы не подумал, что «флаги» - это решение. Интересно, если, помимо того, что «COM» будет менее жадным , другие регулярные выражения также требуют аналогичной модификации, например, если у меня также есть '('NUMBER', r '\ d + (\. \ d *)?')' или '('ID', r '[A-Za-z ] + ') 'они также потребуют модификации, чтобы быть менее жадными? –

+0

Кроме того, интересно, что регулярное выражение '('NOFLAGS', r '/ \ * [^ *] * \ * + (?: [^/*] [^ *] * \ * +) * /')' Соответствует всем, начиная с с '/ *' и заканчивая '* /' без использования 'flags = re.DOTALL | re.MULTILINE'. В качестве понимания я был бы признателен, если бы я мог видеть, как ваши регулярные выражения могут быть изменены, чтобы соответствовать без использования флагов, таких как «NOFLAGS» (если это возможно, конечно). –