2012-11-05 8 views
5

I-Eingang haben, der wie eine Liste von Argumenten aus:Parsing einfache oder doppelte Anführungszeichen und Escape-Zeichen erlauben mit regulären Ausdrücken (in Python)

input1 = ''' 
title="My First Blog" author='John Doe' 
''' 

Die Werte können jedoch durch einfache oder doppelte Anführungszeichen gesetzt werden,

input2 = ''' 
title='John\'s First Blog' author="John Doe" 
''' 

gibt es eine Möglichkeit reguläre Ausdrücke zu verwenden, um die Schlüssel-Wert-Paare Anteil von entweder einfachen oder doppelten Anführungszeichen zu extrahieren und zu entkommen Zitate:, flüchtet auch erlaubt?

Python, kann ich den folgenden regulären Ausdruck verwenden und die nicht entgangen Zitate handhaben:

rex = r"(\w+)\=(?P<quote>['\"])(.*?)(?P=quote)" 

Die Renditen sind dann:

import re 
re.findall(rex, input1) 
[('title', '"', 'My First Blog'), ('author', "'", 'John Doe')] 

und

import re 
re.findall(rex, input2) 
[('title', "'", 'John'), ('author', '"', 'John Doe')] 

Die Letzteres ist falsch. Ich kann nicht herausfinden, wie man mit gematchten Zitaten umgeht - vermutlich im (. *?) Abschnitt. Ich habe mit der Lösung in den veröffentlichten Antworten auf Python regex to match text in single quotes, ignoring escaped quotes (and tabs/newlines) vergebens gearbeitet.

Technisch gesehen brauche ich findall nicht, um das Anführungszeichen zurückzugeben - eher nur die Schlüssel/Wert-Paare - aber das wird leicht gehandhabt.

Jede Hilfe wäre willkommen! Vielen Dank!

Antwort

4

EDIT

Meine inital regex Lösung einen Fehler drin hatte. Dass Fehler maskiert Zeichenfolge einen Fehler in Ihrer Eingabe: input2 ist nicht das, was Sie denken, es ist:

>>> input2 = ''' 
... title='John\'s First Blog' author="John Doe" 
... ''' 
>>> input2  # See - the apostrophe is not correctly escaped! 
'\ntitle=\'John\'s First Blog\' author="John Doe"\n' 

Sie benötigen input2 eine rohe string (oder verwenden Sie doppelte Schrägstriche) zu machen:

>>> input2 = r''' 
... title='John\'s First Blog' author="John Doe" 
... ''' 
>>> input2 
'\ntitle=\'John\\\'s First Blog\' author="John Doe"\n' 

Jetzt Sie können einen regulären Ausdruck verwenden, die richtig entgangen Zitate Griffe:

>>> rex = re.compile(
    r"""(\w+)# Match an identifier (group 1) 
    =  # Match = 
    (['"]) # Match an opening quote (group 2) 
    (  # Match and capture into group 3: 
    (?:  # the following regex: 
     \\. # Either an escaped character 
    |  # or 
     (?!\2) # (as long as we're not right at the matching quote) 
     .  # any other character. 
    )*  # Repeat as needed 
    )  # End of capturing group 
    \2  # Match the corresponding closing quote.""", 
    re.DOTALL | re.VERBOSE) 
>>> rex.findall(input2) 
[('title', "'", "John\\'s First Blog"), ('author', '"', 'John Doe')] 
+0

Könnten Sie bitte den „oder jedes andere Zeichen“ Teil erklären? Hat ein '.' in einem' oder' nicht immer Übereinstimmung? –

+1

@LevLevitsky: Der Punkt entspricht einem beliebigen Zeichen, ja.Aber die vorherige Lookahead-Assertion '(?! \ 2)' stellt sicher, dass es sich nicht um das schließende Zitat handelt, so dass dieser Punkt tatsächlich mit jedem Zeichen übereinstimmt, außer dem Schlusskurs. –

+0

@LevLevitsky: Aber Sie hatten absolut Recht, es gab einen großen Fehler in meiner Regex. Fixing es jetzt (der Umfang der Abwechslung war falsch). Danke, dass du mich darauf hingewiesen hast! –

5

ich Tims denke auch, macht i Verwendung von Rückreferenzierungen overcomplicates die Expression und (erraten hier) t langsamer. Der Standardansatz (in der Eule Buch) verwendete Ein- und Strings in doppelten Anführungszeichen separat entsprechen:

rx = r'''(?x) 
    (\w+) = (
     ' (?: \\. | [^'])* ' 
     | 
     " (?: \\. | [^"])* " 
     | 
     [^'"\s]+ 
    ) 
''' 

ein wenig Nachbearbeitung hinzufügen und Sie sind fein:

input2 = r''' 
title='John\'s First Blog' author="John Doe" 
''' 

data = {k:v.strip("\"\'").decode('string-escape') for k, v in re.findall(rx, input2)} 
print data 
# {'author': 'John Doe', 'title': "John's First Blog"} 

Als Bonus , dies entspricht auch nicht aufgeführten Attributen wie weight=150.

Anzahl: hier ist ein sauberer Weg ohne reguläre Ausdrücke:

input2 = r''' 
title='John\'s First Blog' author="John Doe" 
''' 

import shlex 

lex = shlex.shlex(input2, posix=True) 
lex.escapedquotes = '\"\'' 
lex.whitespace = ' \n\t=' 
for token in lex: 
    print token 

# title 
# John's First Blog 
# author 
# John Doe 
Verwandte Themen