Tom Thorogood Tom Thorogood - 5 months ago 11
Python Question

Correctly parsing string literals with python's re module

I'm trying to add some light markdown support for a javascript preprocessor which I'm writing in Python.

For the most part it's working, but sometimes the regex I'm using is acting a little odd, and I think it's got something to do with raw-strings and escape sequences.

The regex is:

(?<!\\)\"[^\"]+\"


Yes, I am aware that it only matches strings beginning with a
"
character. However, this project is born out of curiosity more than anything, so I can live with it for now.

To break it down:

(?<\\)\" # The group should begin with a quotation mark that is not escaped
[^\"]+ # and match any number of at least one character that is not a quotation mark (this is the biggest problem, I know)
\" # and end at the first quotation mark it finds


That being said, I (obviously) start hitting problems with things like this:

"This is a string with an \"escaped quote\" inside it"


I'm not really sure how to say "Everything but a quotation mark, unless that mark is escaped". I tried:

([^\"]|\\\")+ # a group of anything but a quote or an escaped quote


, but that lead to very strange results.

I'm fully prepared to hear that I'm going about this all wrong. For the sake of simplicity, let's say that this regex will always start and end with double quotes (
"
) to avoid adding another element in the mix. I really want to understand what I have so far.

Thanks for any assistance.

EDIT

As a test for the regex, I'm trying to find all string literals in the minified jQuery script with the following code (using the unutbu's pattern below):

STRLIT = r'''(?x) # verbose mode
(?<!\\) # not preceded by a backslash
" # a literal double-quote
.*? # non-greedy 1-or-more characters
(?<!\\) # not preceded by a backslash
" # a literal double-quote
'''
f = open("jquery.min.js","r")
jq = f.read()
f.close()
literals = re.findall(STRLIT,jq)


The answer below fixes almost all issues. The ones that do arise are within jquery's own regular expressions, which is a very edge case. The solution no longer misidentifies valid javascript as markdown links, which was really the goal.

Answer

Perhaps use two negative look behinds:

import re

text = r'''"This is a string with an \"escaped quote\" inside it". While ""===r?+r:wt.test(r)?st.parseJSON(r)    :r}catch(o){}st.data(e,n,r)}else r=t}return r}function s(e){var t;for(t in e)if(("data" '''

for match in (re.findall(r'''(?x)   # verbose mode
    (?<!\\)    # not preceded by a backslash
    "          # a literal double-quote
    .*?        # 1-or-more characters
    (?<!\\)    # not preceded by a backslash
    "          # a literal double-quote
    ''', text)):
    print(match)

yields

"This is a string with an \"escaped quote\" inside it"
""
"data"

The question mark in .+? makes the pattern non-greedy. The non-greediness causes the pattern to match when it encounters the first unescaped double quotation mark.

Comments