Regex: Find text after last match of lookahead assertion until first match of lookahead assertion

Viewed 436

I have a text like this:

Hans Wurst, geboren 25.01.1987, zuletzt tätig als Metzger, Breslauer Str. 15, 02708 Löbau

from which I would like to get street name ("Breslauer Str. 15"). So I used a regex like the one below:

(?<=, )(.+?)(?=,[\s]?[0-9]{5})

But this is greedy and matches:

geboren 25.01.1987, zuletzt tätig als Metzger, Breslauer Str. 15

How can I make this less greedy so that essentially it takes the latest occurence of a comma taking into account the lookahead assertion (?=,[\s]?[0-9]{5})?

6 Answers

Simply add [^,].* in the beginning of your RegEx pattern. The pattern, [^,].* means 'not comma' followed by any character any number of times.

RegEx Demo

Python demo:

import re

s = 'Hans Wurst, geboren 25.01.1987, zuletzt tätig als Metzger, Breslauer Str. 15, 02708 Löbau'
m = re.search(r'[^,].*(?<=, )(.+)(?=,[\s]?[0-9]{5})', s)
if m:
    print(m.group(1))

Output:

Breslauer Str. 15

In your pattern, you can just change (.+?) to [^,]+. The dot can also match a comma and will match too much, the negated character class can not match a comma in this case.

As you use lookarounds, you can omit the capture group.

See a regex demo for pattern (?<=, )[^,]+(?=,\s?[0-9]{5})


But as you already make use of a capture group, you can change the lookaround into matches instead to make the pattern a bit more performant.

Note that the \s does not have to be in a character class.

, ([^,]+),[\s]?[0-9]{5}\b

The pattern matches:

  • , Match literally
  • ([^,]+) Capture group 1, match 1+ chars other than ,
  • ,\s? Match a comma and optional whitespace char
  • [0-9]{5}\b Match 5 digits and a word boundary to prevent a partial match

See a regex demo

import re

s="Hans Wurst, geboren 25.01.1987, zuletzt tätig als Metzger, Breslauer Str. 15, 02708 Löbau"
pattern = r", ([^,]+),[\s]?[0-9]{5}\b"
m = re.search(pattern, s)
if m:
    print (m.group(1))

Output

Breslauer Str. 15

You can use

m = re.search(r'.*,\s*([^,]*),\s*[0-9]{5}', text, re.DOTALL)
if m:
    print(m.group(1))

See the regex demo. Details:

  • .* - zero or more chars, as many as possible
  • , - a comma
  • \s* - zero or more whitespaces
  • ([^,]*) - Group 1: zero or more non-comma chars
  • , - a comma
  • \s* - zero or more whitespaces
  • [0-9]{5} - five digits.

Match everything between the last 2 commas, provided that after the last comma the requested number sequence occurs:

,([^,]+),([\s]?[0-9]{5}[^,]*)$

If you want to match the address using lookaheads, the first exercise is thinking about how you can do it without using regex. Going around this problem in this way will drive you to the answer. So, let's do it: If we split the text using commas, we find that the address is in the second group from the right to the left. Then, knowing that, we assume that after the address there is only one comma, one space, and finally, anything except a comma and the end of the string. This are translated to (?=, [^,]*$). So, in the remaining string, what wraps the address? Another comma, another space, and bingo! the address is the rest (that can be anything except a comma): (?<=, )[^,]*.

import re

text = "Hans Wurst, geboren 25.01.1987, zuletzt tätig als Metzger, Breslauer Str. 15, 02708 Löba"

result = re.search("(?<=, )[^,]*(?=, [^,]*$)", text).group()

print(result)
# Breslauer Str. 15

You can also try this on https://regex101.com/

regex101 result

PD: a little recommendation. if you can solve this only using python (regex is another language), do it. A workaround to this could be the following:

text = "Hans Wurst, geboren 25.01.1987, zuletzt tätig als Metzger, Breslauer Str. 15, 02708 Löba"

print(text.split(', ')[-2])
# 'Breslauer Str. 15'

This is more pythonic, easy to understand, and faster!

I have assumed that the street name:

  • begins after any spaces after the penultimate comma;
  • contains no commas; and
  • is immediately followed by the last comma in the string.

In that case one can match the regular expression

[^, ][^,]+(?=,(?!.*,))

Demo

The elements of the expression are as follows.

[^, ]     # match a character that is neither a comma nor a space
[^,]*     # match zero or more characters other than a comma     
(?=       # begin positive lookahead
  ,       # match a comma
  (?!     # begin negative lookahead
    .*,   # match zero or more characters then a comma
  )       # end negative lookahead
)         # end positive lookahead

You can alternatively hover the cursor over each element of the expression at the link to obtain an explanation of its function.

Related