【问题标题】:Regex to match only book('knjiga') with specific name('naslov')正则表达式仅匹配具有特定名称('naslov')的书('knjiga')
【发布时间】:2020-02-20 07:13:50
【问题描述】:

我有一个简单的 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'&lt;book\s*rbr="\d+"\s*&gt;.*?&lt;/book&gt;'

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

r'&lt;book\s*rbr="\d+"\s*&gt;(?P&lt;book&gt;.*?&lt;title&gt; Python Standard Library &lt;/title&gt;.*?)&lt;/book&gt;'

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

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

【问题讨论】:

    标签: python regex pattern-matching regex-lookarounds regex-greedy


    【解决方案1】:

    &lt;title&gt; 标签并非始终是&lt;book&gt; 下的第一个子标签,这一事实使问题变得非常复杂。如果是,您可以使用:

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

    即用\s*替换.*?

    诀窍是确保在匹配&lt;book&gt; 标记后,您正在寻找的&lt;title&gt; 标记不会出现在未来的&lt;/book&gt; 标记之后。这可以通过消极的前瞻来完成(它并不漂亮):

    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>
    

    See demo

    如果您的 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)
    

    另一种方法

    这种方法会构建一个包含所有单独 &lt;book&gt; 标记的列表,然后搜索每个标记以查找感兴趣的标题:

    # 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>
    

    【讨论】:

    • 已经够漂亮了。我理解它为什么有效,但想法对我来说还不是很直接。谢谢!
    • 我更新了解决方案以添加一种希望更直接的替代方法。
    • 我昨天就是这样解决的,不过还是谢谢!我想我需要更多关于正则表达式的理论和实践。你帮了我很多!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 2016-01-22
    • 1970-01-01
    • 2016-09-12
    • 2015-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多