pyparsing - 解析XML注释

4 投票
3 回答
1320 浏览
提问于 2025-04-17 04:38

我需要解析一个包含 XML 注释的文件。具体来说,这是一个使用 MS /// 规范的 C# 文件。

从中我需要提取出 foobar,或者 /// foobar 也可以。(注意 - 如果把 XML 全部放在一行,这样还是不行…)

testStr = """
    ///<summary>
    /// foobar
    ///</summary>
    """

这是我现在的代码:

import pyparsing as pp

_eol = pp.Literal("\n").suppress()
_cPoundOpenXmlComment = Suppress('///<summary>') + pp.SkipTo(_eol)
_cPoundCloseXmlComment = Suppress('///</summary>') + pp.SkipTo(_eol)
_xmlCommentTxt = ~_cPoundCloseXmlComment + pp.SkipTo(_eol)
xmlComment = _cPoundOpenXmlComment + pp.OneOrMore(_xmlCommentTxt) + _cPoundCloseXmlComment

match = xmlComment.scanString(testStr)

我想要输出:

for item,start,stop in match:
    for entry in item:
        print(entry)

但是我在处理多行时,语法一直没有成功。

(注意 - 我在 Python 3.2 中测试了上面的示例;它能运行,但(根据我的问题)没有打印出任何值)

谢谢!

3 个回答

1

你可以使用一个xml解析器来处理xml文件。这样提取出相关的注释行应该很简单:

import re
from xml.etree import cElementTree as etree

# extract all /// lines
lines = re.findall(r'^\s*///(.*)', text, re.MULTILINE)

# parse xml
root = etree.fromstring('<root>%s</root>' % ''.join(lines))
print root.findtext('summary')
# -> foobar
3

我觉得你用的 Literal('\n') 是个问题。你不应该用带空格的字符来构建一个 Literal(因为默认情况下,Literal 会跳过空格再尝试匹配)。试试用 LineEnd() 来代替。

编辑 1:仅仅因为你在使用 LineEnd 时出现了无限循环,并不意味着 Literal('\n') 就好一些。试着在你的 _eol 定义后面加上 .setDebug(),你会发现它根本匹配不到任何东西。

与其试图把你的评论内容定义为“一个或多个不是结束行的行,但要一直到行尾”,不如直接这样做:

xmlComment = _cPoundOpenXmlComment + pp.SkipTo(_cPoundCloseXmlComment) + _cPoundCloseXmlComment 

你在使用 LineEnd() 时出现无限循环的原因是,你实际上是在做 OneOrMore(SkipTo(LineEnd())),但从来没有消耗掉 LineEnd(),所以 OneOrMore 就一直在匹配、匹配、再匹配,解析并返回一个空字符串,因为解析的位置正好在行尾。

2

可以试试用 nestedExpr 这个方法:

import pyparsing as pp

text = '''\
///<summary>
/// foobar
///</summary>
blah blah
///<summary> /// bar ///</summary>
///<summary>  ///<summary> /// baz  ///</summary> ///</summary>    
'''

comment=pp.nestedExpr("///<summary>","///</summary>")
for match in comment.searchString(text):
    print(match)
    # [['///', 'foobar']]
    # [['///', 'bar']]
    # [[['///', 'baz']]]

撰写回答