Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python regex matching pattern not surrounded by double quotes

Tags:

python

regex

I'm not comfortable with regex, so I need your help with this one, which seems tricky to me.

Let's say I've got the following string :

string = 'keyword1 keyword2 title:hello title:world "title:quoted" keyword3'

What would be the regex to get title:hello, title:world, remove these strings from the original one and leave "title:quoted" in it, because it's surrounded by double quotes ?

I've already seen this similar SO answer, and here is what I ended up with :

import re

string = 'keyword1 keyword2 title:hello title:world "title:quoted" keyword3'

def replace(m):
    if m.group(1) is None:
        return m.group()

    return m.group().replace(m.group(1), "")

regex = r'\"[^\"]title:[^\s]+\"|([^\"]*)'
cleaned_string = re.sub(regex, replace, string)

assert cleaned_string == 'keyword1 keyword2 "title:quoted" keyword3'

Of course, it does not work, and I'm not surprised, because regex are esoteric to me.

Thank you for your help !

Final solution

Thanks to your answers, here is the final solution, working for my needs :

import re
matches = []

def replace(m):
    matches.append(m.group())
    return ""

string = 'keyword1 keyword2 title:hello title:world "title:quoted" keyword3'
regex = '(?<!")title:[^\s]+(?!")'
cleaned_string = re.sub(regex, replace, string)

# remove extra withespaces
cleaned_string = ' '.join(cleaned_string.split())

assert cleaned_string == 'keyword1 keyword2 "title:quoted" keyword3'
assert matches[0] == "title:hello"
assert matches[1] == "title:world"

2 Answers

You can check for word boundaries (\b):

>>> s = 'keyword1 keyword2 title:hello title:world "title:quoted" keyword3'
>>> re.sub(r'\btitle:\w+\b', '', s, re.I)
'keyword1 keyword2   "title:quoted" keyword3'

Or, alternatively, you can use negative look behind and ahead assertions to check for not having quotes around title:\w+:

>>> re.sub(r'(?<!")title:\w+(?!")', '', s)
'keyword1 keyword2   "title:quoted" keyword3'
like image 107
alecxe Avatar answered Aug 13 '26 04:08

alecxe


This situation sounds very similar to "regex-match a pattern unless..."

We can solve it with a beautifully-simple regex:

"[^"]*"|(\btitle:\S+)

The left side of the alternation | matches complete "double quoted strings" tags. We will ignore these matches. The right side matches and captures your title:hello strings to Group 1, and we know they are the right ones because they were not matched by the expression on the left.

This program shows how to use the regex (see the results at the bottom of the online demo):

import re
subject = 'keyword1 keyword2 title:hello title:world "title:quoted" keyword3'
regex = re.compile(r'"[^"]*"|(\btitle:\S+)')
def myreplacement(m):
    if m.group(1):
        return ""
    else:
        return m.group(0)
replaced = regex.sub(myreplacement, subject)
print(replaced)

Reference

How to match (or replace) a pattern except in situations s1, s2, s3...

like image 36
zx81 Avatar answered Aug 13 '26 04:08

zx81



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!