使用正则表达式从字符串中提取信息

时间:2011-08-10 12:20:54

标签: python regex

这是此问题的后续问题和复杂问题:Extracting contents of a string within parentheses

在那个问题中,我有以下字符串 -

"Will Farrell (Nick Hasley), Rebecca Hall (Samantha)"

我想以(actor, character) -

的形式获取元组列表
[('Will Farrell', 'Nick Hasley'), ('Rebecca Hall', 'Samantha')]

为了概括问题,我有一个稍微复杂的字符串,我需要提取相同的信息。我的字符串是 -

"Will Ferrell (Nick Halsey), Rebecca Hall (Samantha), Glenn Howerton (Gary), 
with Stephen Root and Laura Dern (Delilah)"

我需要格式化如下:

[('Will Farrell', 'Nick Hasley'), ('Rebecca Hall', 'Samantha'), ('Glenn Howerton', 'Gary'),
('Stephen Root',''), ('Lauren Dern', 'Delilah')]

我知道我可以替换填充词(with,and,&,等),但不能完全弄清楚如何添加空白条目 - '' - 如果没有字符演员的名字(在这种情况下是斯蒂芬根)。这样做最好的方法是什么?

最后,我需要考虑一个actor是否有多个角色,并为actor所拥有的每个角色构建一个元组。我的最后一个字符串是:

"Will Ferrell (Nick Halsey), Rebecca Hall (Samantha), Glenn Howerton (Gary, Brad), with
 Stephen Root and Laura Dern (Delilah, Stacy)"

我需要构建一个元组列表,如下所示:

[('Will Farrell', 'Nick Hasley'), ('Rebecca Hall', 'Samantha'), ('Glenn Howerton', 'Gary'),    
 ('Glenn Howerton', 'Brad'), ('Stephen Root',''), ('Lauren Dern', 'Delilah'), ('Lauren Dern', 'Stacy')]

谢谢。

4 个答案:

答案 0 :(得分:4)

import re
credits = """Will Ferrell (Nick Halsey), Rebecca Hall (Samantha), Glenn Howerton (Gary, Brad), with
 Stephen Root and Laura Dern (Delilah, Stacy)"""

# split on commas (only if outside of parentheses), "with" or "and"
splitre = re.compile(r"\s*(?:,(?![^()]*\))|\bwith\b|\band\b)\s*")

# match the part before the parentheses (1) and what's inside the parens (2)
# (only if parentheses are present)
matchre = re.compile(r"([^(]*)(?:\(([^)]*)\))?")

# split the parts inside the parentheses on commas
splitparts = re.compile(r"\s*,\s*")

characters = splitre.split(credits)
pairs = []
for character in characters:
    if character:
        match = matchre.match(character)
        if match:
            actor = match.group(1).strip()
            if match.group(2):
                parts = splitparts.split(match.group(2))
                for part in parts:
                    pairs.append((actor, part))
            else:
                pairs.append((actor, ""))

print(pairs)

输出:

[('Will Ferrell', 'Nick Halsey'), ('Rebecca Hall', 'Samantha'), 
 ('Glenn Howerton', 'Gary'), ('Glenn Howerton', 'Brad'), ('Stephen Root', ''), 
 ('Laura Dern', 'Delilah'), ('Laura Dern', 'Stacy')]

答案 1 :(得分:1)

Tim Pietzcker的解决方案可以简化为(注意模式也被修改):

import re
credits = """   Will Ferrell (Nick Halsey), Rebecca Hall (Samantha), Glenn Howerton (Gary, Brad), with
 Stephen Root and Laura Dern (Delilah, Stacy)"""

# split on commas (only if outside of parentheses), "with" or "and"
splitre = re.compile(r"(?:,(?![^()]*\))(?:\s*with)*|\bwith\b|\band\b)\s*")

# match the part before the parentheses (1) and what's inside the parens (2)
# (only if parentheses are present)
matchre = re.compile(r"\s*([^(]*)(?<! )\s*(?:\(([^)]*)\))?")

# split the parts inside the parentheses on commas
splitparts = re.compile(r"\s*,\s*")

pairs = []
for character in splitre.split(credits):
    gr = matchre.match(character).groups('')
    for part in splitparts.split(gr[1]):
        pairs.append((gr[0], part))

print(pairs)

然后:

import re
credits = """   Will Ferrell (Nick Halsey), Rebecca Hall (Samantha), Glenn Howerton (Gary, Brad), with
 Stephen Root and Laura Dern (Delilah, Stacy)"""

# split on commas (only if outside of parentheses), "with" or "and"
splitre = re.compile(r"(?:,(?![^()]*\))(?:\s*with)*|\bwith\b|\band\b)\s*")

# match the part before the parentheses (1) and what's inside the parens (2)
# (only if parentheses are present)
matchre = re.compile(r"\s*([^(]*)(?<! )\s*(?:\(([^)]*)\))?")

# split the parts inside the parentheses on commas
splitparts = re.compile(r"\s*,\s*")

gen = (matchre.match(character).groups('') for character in splitre.split(credits))

pp = [ (gr[0], part) for gr in gen for part in splitparts.split(gr[1])]

print pp

诀窍是将groups('')与参数''

一起使用

答案 2 :(得分:0)

你想要的是识别以大写字母开头的单词序列,加上一些复杂功能(恕我直言,你不能假设每个名字都由名字姓氏组成,而且名称姓氏小名,或姓名M.姓氏,或其他本地化的变异,Jean-Claude van Damme,Louis da Silva等。)。

现在,对于您发布的示例输入,这可能是过度的,但正如我上面所写,我认为事情很快就会变得混乱,所以我会使用nltk解决这个问题。

这是一个非常粗糙且经过严格测试的代码片段,但它应该可以完成这项工作:

import nltk
from nltk.chunk.regexp import RegexpParser

_patterns = [
    (r'^[A-Z][a-zA-Z]*[A-Z]?[a-zA-Z]+.?$', 'NNP'),  # proper nouns
    (r'^[(]$', 'O'),
    (r'[,]', 'COMMA'),
    (r'^[)]$', 'C'),
    (r'.+', 'NN')                                   # nouns (default)
]

_grammar = """
        NAME: {<NNP> <COMMA> <NNP>}
        NAME: {<NNP>+}
        ROLE: {<O> <NAME>+ <C>}
        """    
text = "Will Ferrell (Nick Halsey), Rebecca Hall (Samantha), Glenn Howerton (Gary, Brad), with Stephen Root and Laura Dern (Delilah, Stacy)"
tagger = nltk.RegexpTagger(_patterns)    
chunker = RegexpParser(_grammar)
text = text.replace('(', '( ').replace(')', ' )').replace(',', ' , ')
tokens = text.split()
tagged_text = tagger.tag(tokens)
tree = chunker.parse(tagged_text)

for n in tree:
    if isinstance(n, nltk.tree.Tree) and n.node in ['ROLE', 'NAME']: 
        print n

# output is:
# (NAME Will/NNP Ferrell/NNP)
# (ROLE (/O (NAME Nick/NNP Halsey/NNP) )/C)
# (NAME Rebecca/NNP Hall/NNP)
# (ROLE (/O (NAME Samantha/NNP) )/C)
# (NAME Glenn/NNP Howerton/NNP)
# (ROLE (/O (NAME Gary/NNP ,/COMMA Brad/NNP) )/C)
# (NAME Stephen/NNP Root/NNP)
# (NAME Laura/NNP Dern/NNP)
# (ROLE (/O (NAME Delilah/NNP ,/COMMA Stacy/NNP) )/C)

然后,您必须处理标记的输出并将名称和角色放在列表中而不是打印,但是您可以获得图片。

我们在这里做的是第一次传递,我们根据_patterns中的正则表达式标记每个标记,然后根据您的简单语法进行第二次传递以构建更复杂的块。您可以根据需要使语法和模式复杂化,即。捕捉名称的变化,杂乱的输入,缩写等。

我认为使用单个正则表达式传递这样做对于非平凡的输入来说将是一种痛苦。

否则,Tim's solution正在为您发布的输入很好地解决问题,并且没有nltk依赖。

答案 3 :(得分:0)

如果您需要非正则表达式解决方案...(假设没有嵌套的括号。)

in_string = "Will Ferrell (Nick Halsey), Rebecca Hall (Samantha), Glenn Howerton (Gary, Brad), with Stephen Root and Laura Dern (Delilah, Stacy)"    

in_list = []
is_in_paren = False
item = {}
next_string = ''

index = 0
while index < len(in_string):
    char = in_string[index]  

    if in_string[index:].startswith(' and') and not is_in_paren:
        actor = next_string
        if actor.startswith(' with '):
            actor = actor[6:]
        item['actor'] = actor
        in_list.append(item)
        item = {}
        next_string = ''
        index += 4    
    elif char == '(':
        is_in_paren = True
        item['actor'] = next_string
        next_string = ''    
    elif char == ')':
        is_in_paren = False
        item['part'] = next_string
        in_list.append(item)
        item = {}                 
        next_string = ''
    elif char == ',':
        if is_in_paren:
            item['part'] = next_string
            next_string = ''
            in_list.append(item)
            item = item.copy()
            item.pop('part')                
    else:
        next_string = "%s%s" % (next_string, char)

    index += 1


out_list = []
for dict in in_list:
    actor = dict.get('actor')
    part = dict.get('part')

    if part is None:
        part = ''

    out_list.append((actor.strip(), part.strip()))

print out_list

输出: [('Will Ferrell','Nick Halsey'),('Rebecca Hall','Samantha'),('Glenn Howerton','Gary'),('Glenn Howerton','Brad'),('Stephen Root) ',''),('Laura Dern','Delilah'),('Laura Dern','Stacy')]