首页 > 解决方案 > 正则表达式仅匹配具有特定名称('naslov')的书('knjiga')

问题描述

我有一个简单的 xml:

<?xml version="1.0" encoding="utf-8" ?>
<book_list>
    <book rbr="1" >
        <title> Yacc </title>
        <author> Filip Maric </author>
        <year> 2004 </year>
        <publisher> Matematicki fakultet </publisher>
        <price currency="din"> 100 </price>
    </book>
    <book rbr="2" >
        <author> Fredrik Lundh </author>
        <price currency="eur"> 50 </price>
        <publisher> O’Reilly & Associates </publisher>
        <year> 2001 </year>
        <title> Python Standard Library </title>
    </book>
</book_list>

我需要将具有特定名称的书与 Python 中的正则表达式匹配。我可以轻松地将任何书籍与:

r'<book\s*rbr="\d+"\s*>.*?</book>'

(单行模式),然后检查它是否是正确的,但如果我想匹配特定的书 - 例如,Python标准库,直接使用正则表达式,我无法正确。如果我尝试

r'<book\s*rbr="\d+"\s*>(?P<book>.*?<title> Python Standard Library </title>.*?)</book>'

,在单行模式下,它会从头开始匹配所有内容,我明白为什么,但我找不到只匹配一个书标签的方法。我尝试了所有查找和所有不同的模式,但没有成功。

什么是正确的方法,它适用于 book_list 中的任意数量的书籍?

标签: pythonregexpattern-matchingregex-lookaroundsregex-greedy

解决方案


由于<title>标签并非始终是<book>. 如果是,您可以使用:

m = re.search(r'<book\s*rbr="\d+"\s*>\s*(?P<book><title> Python Standard Library </title>).*?</book>', xml, flags=re.DOTALL)

也就是说,替换.*?\s*.

诀窍是确保在您匹配一个<book>标签之后,<title>您正在寻找的标签不会出现在未来</book>标签之后。这可以通过负面的前瞻来完成(它并不漂亮):

import re

xml = """<?xml version="1.0" encoding="utf-8" ?>
<book_list>
    <book rbr="1" >
        <title> Yacc </title>
        <author> Filip Maric </author>
        <year> 2004 </year>
        <publisher> Matematicki fakultet </publisher>
        <price currency="din"> 100 </price>
    </book>
    <book rbr="2" >
        <author> Fredrik Lundh </author>
        <price currency="eur"> 50 </price>
        <publisher> O’Reilly & Associates </publisher>
        <year> 2001 </year>
        <title> Python Standard Library </title>
    </book>
</book_list>"""

m = re.search(r'<book\s*rbr="\d+"\s*>(?!.*</book>.*<title> Python Standard Library </title>).*(?P<book><title> Python Standard Library </title>).*?</book>', xml, flags=re.DOTALL)
print(m.group('book'))
m = re.search(r'<book\s*rbr="\d+"\s*>(?!.*</book>.*<title> Yacc </title>).*(?P<book><title> Yacc </title>).*?</book>', xml, flags=re.DOTALL)
print(m.group('book'))

印刷:

<title> Python Standard Library </title>
<title> Yacc </title>

看演示

如果您的 Python 支持,您可以使用格式化的字符串文字来减少冗余(str.format如果不支持,则使用该方法):

title = '<title> Python Standard Library </title>'
m = re.search(rf'<book\s*rbr="\d+"\s*>(?!.*</book>.*{title}).*(?P<book>{title}).*?</book>', xml, flags=re.DOTALL)

另一种方法

这种方法构建了所有单个<book>标签的列表,然后搜索每个标签以查找感兴趣的标题:

# create list of <book> ... </book> strings:
books = re.findall(r'<book\s*rbr="\d+"\s*>.*?</book>', xml, flags=re.DOTALL)
title = '<title> Python Standard Library </title>'
# now search each <book>...</book> string looking for the title string:
for book in books:
    if re.search(rf'{title}', book):
        print(title)
        print(book)

印刷:

<title> Python Standard Library </title>
<book rbr="2" >
        <author> Fredrik Lundh </author>
        <price currency="eur"> 50 </price>
        <publisher> O'Reilly & Associates </publisher>
        <year> 2001 </year>
        <title> Python Standard Library </title>
    </book>

推荐阅读