Kannan Ekanath
Kannan Ekanath

Reputation: 17601

Python extract pattern matches

I am trying to use a regular expression to extract words inside of a pattern.

I have some string that looks like this

someline abc
someother line
name my_user_name is valid
some more lines

I want to extract the word my_user_name. I do something like

import re
s = #that big string
p = re.compile("name .* is valid", re.flags)
p.match(s)  # this gives me <_sre.SRE_Match object at 0x026B6838>

How do I extract my_user_name now?

Upvotes: 254

Views: 462171

Answers (11)

Grzesik
Grzesik

Reputation: 151

Below is a simple solution to the problem

import re

except_substring = "\w+_\w+_\w+"
original_string = """someline abc
someother line
name my_user_name is valid
some more lines"""

m=re.findall(except_substring, original_string)
if m:
    print(m[0]) #--> "my_user_name"
else:
    print("Substring was not found")

Or we can make the function:

def replace_all_exept_substring(original_string: str, except_substring: str) -> str:

     """
     The function gets the substring to be skipped from the whole string
     can get it in the form of a single word, e.g. "NoSuchUser"
     or a regular expression, e.g. "Message.*has.been.*rejected."
     original_string : the whole phrase to search for
     except_substring : the phrase/word to be returned
     return : the message sent to the following e mail address has been rejected
     """
        
     m = re.findall(except_substring, original_string)
     # A+1 if A > B else A-1
     return m[0] if m else original_string

m=replace_all_exept_substring(original_string, except_substring)

print(m) #--> "my_user_name" or "original_string"

Upvotes: 0

John
John

Reputation: 551

Maybe that's a bit shorter and easier to understand:

>>> import re
>>> text = '... someline abc... someother line... name my_user_name is valid.. some more lines'
>>> re.search('name (.*) is valid', text).group(1)
'my_user_name'

Upvotes: 15

user136036
user136036

Reputation: 12314

I found this answer via google because I wanted to unpack a re.search() result with multiple groups directly into multiple variables. While this might be obvious for some, it was not for me because I always used group() in the past, so maybe it helps someone in the future who also did not know about group*s*().

s = "2020:12:30"
year, month, day = re.search(r"(\d+):(\d+):(\d+)", s).groups()

Upvotes: 4

UltraInstinct
UltraInstinct

Reputation: 44424

You need to capture from regex. search for the pattern, if found, retrieve the string using group(index). Assuming valid checks are performed:

>>> p = re.compile("name (.*) is valid")
>>> result = p.search(s)
>>> result
<_sre.SRE_Match object at 0x10555e738>
>>> result.group(1)     # group(1) will return the 1st capture (stuff within the brackets).
                        # group(0) will returned the entire matched text.
'my_user_name'

Upvotes: 294

chiceman
chiceman

Reputation: 106

It seems like you're actually trying to extract a name vice simply find a match. If this is the case, having span indexes for your match is helpful and I'd recommend using re.finditer. As a shortcut, you know the name part of your regex is length 5 and the is valid is length 9, so you can slice the matching text to extract the name.

Note - In your example, it looks like s is string with line breaks, so that's what's assumed below.

## covert s to list of strings separated by line:
s2 = s.splitlines()

## find matches by line: 
for i, j in enumerate(s2):
    matches = re.finditer("name (.*) is valid", j)
    ## ignore lines without a match
    if matches:
        ## loop through match group elements
        for k in matches:
            ## get text
            match_txt = k.group(0)
            ## get line span
            match_span = k.span(0)
            ## extract username
            my_user_name = match_txt[5:-9]
            ## compare with original text
            print(f'Extracted Username: {my_user_name} - found on line {i}')
            print('Match Text:', match_txt)

Upvotes: 1

Eugene Yarmash
Eugene Yarmash

Reputation: 149736

You can use groups (indicated with '(' and ')') to capture parts of the string. The match object's group() method then gives you the group's contents:

>>> import re
>>> s = 'name my_user_name is valid'
>>> match = re.search('name (.*) is valid', s)
>>> match.group(0)  # the entire match
'name my_user_name is valid'
>>> match.group(1)  # the first parenthesized subgroup
'my_user_name'

In Python 3.6+ you can also index into a match object instead of using group():

>>> match[0]  # the entire match 
'name my_user_name is valid'
>>> match[1]  # the first parenthesized subgroup
'my_user_name'

Upvotes: 27

Ryan Stefan
Ryan Stefan

Reputation: 134

You can also use a capture group (?P<user>pattern) and access the group like a dictionary match['user'].

string = '''someline abc\n
            someother line\n
            name my_user_name is valid\n
            some more lines\n'''

pattern = r'name (?P<user>.*) is valid'
matches = re.search(pattern, str(string), re.DOTALL)
print(matches['user'])

# my_user_name

Upvotes: 5

wolfovercats
wolfovercats

Reputation: 500

Here's a way to do it without using groups (Python 3.6 or above):

>>> re.search('2\d\d\d[01]\d[0-3]\d', 'report_20191207.xml')[0]
'20191207'

Upvotes: 12

Henry Keiter
Henry Keiter

Reputation: 17168

You want a capture group.

p = re.compile("name (.*) is valid", re.flags) # parentheses for capture groups
print p.match(s).groups() # This gives you a tuple of your matches.

Upvotes: 13

Apalala
Apalala

Reputation: 9224

You could use something like this:

import re
s = #that big string
# the parenthesis create a group with what was matched
# and '\w' matches only alphanumeric charactes
p = re.compile("name +(\w+) +is valid", re.flags)
# use search(), so the match doesn't have to happen 
# at the beginning of "big string"
m = p.search(s)
# search() returns a Match object with information about what was matched
if m:
    name = m.group(1)
else:
    raise Exception('name not found')

Upvotes: 28

mgilson
mgilson

Reputation: 309821

You can use matching groups:

p = re.compile('name (.*) is valid')

e.g.

>>> import re
>>> p = re.compile('name (.*) is valid')
>>> s = """
... someline abc
... someother line
... name my_user_name is valid
... some more lines"""
>>> p.findall(s)
['my_user_name']

Here I use re.findall rather than re.search to get all instances of my_user_name. Using re.search, you'd need to get the data from the group on the match object:

>>> p.search(s)   #gives a match object or None if no match is found
<_sre.SRE_Match object at 0xf5c60>
>>> p.search(s).group() #entire string that matched
'name my_user_name is valid'
>>> p.search(s).group(1) #first group that match in the string that matched
'my_user_name'

As mentioned in the comments, you might want to make your regex non-greedy:

p = re.compile('name (.*?) is valid')

to only pick up the stuff between 'name ' and the next ' is valid' (rather than allowing your regex to pick up other ' is valid' in your group.

Upvotes: 104

Related Questions