从非XML文本文件中提取XML元素

时间:2012-12-15 12:52:05

标签: xml text command-line sed text-processing

有些文件采用Markdown,MediaWiki语法,Creole,源代码以及纯文本进行编码。

这些文件可能包含一个迷路XML元素。当我说流浪时,它们的文件不是XML,如下所示:

  • QUnit在单元测试中有<reference path=""/>
  • Javadoc包含XML元素

如何以最可靠的方式提取此元素?它不是XML文档,但XML元素本身是格式良好的

我一直在使用sed来提取元素的内容:

gsed  -n '/<myelement>/,/<\/myelement>/p' < test.txt > output.txt

这只是从文件中删除所有非XML,并留下我的自定义元素。这不允许我单独处理每一个。然后,我可以在生成的文件上运行xmlstarlet,但这并不能告诉我元素在源文档中的位置。

最好的方法是什么?如何修改sed以一次匹配一个(我可以替换自己)。

将整个文件读入根元素然后将文件处理成是一个带有XML工具的半结构化XML文件,然后在XML解析中处理替换,会不会更好?

2 个答案:

答案 0 :(得分:2)

如果gsed(基于正则表达式)解决方案提取正确的xml文本,那么您可以扩展解决方案以包括开始/结束位置,假设<myelement>未嵌套:

$ perl -0777 -ne 'print "start: $-[0], end: $+[0], xml: {{{$&}}}\n" while /<myelement>.*?<\/myelement>/gs' < input > output

输入

some arbitrary text
A well-formed xml:

<myelement>
... xml here
</myelement>

some arbitrary text follows more elements: <myelement>... xml</myelement> the end

Output

start: 40, end: 77, xml: {{{<myelement>
... xml here
</myelement>}}}
start: 122, end: 152, xml: {{{<myelement>... xml</myelement>}}}

这是一个Python解决方案,它构建正则表达式,匹配纯文本中的某些xml元素,假设每个根元素不是嵌套的,并且它不在注释或基于的cdata中     Matching patterns in Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
import sys
from xml.etree import ElementTree as etree

# build regex that matches xml element
# xml_element = start_tag <anything> end_tag
#             | self_close_tag
xml_element = '(?xs) {start_tag} (?(self_close) |.*? {end_tag})'

# start_tag = '<' name  *attr '>'
# self_close_tag = '<' name *attr '/>'
ws = r'[ \t\r\n]*'  # whitespace
start_tag = '< (?P<name>{name}) {ws} (?:{attr} {ws})* (?P<self_close> / )? >'
end_tag = '</ (?P=name) >'
name = '[a-zA-Z]+'  # note: expand if necessary but the stricter the better
attr = '{name} {ws} = {ws} "[^"]*"'  # match attribute
                                     #  - fragile against missing '"'
                                     #  - no “'” support
assert '{{' not in xml_element
while '{' in xml_element: # unwrap definitions
    xml_element = xml_element.format(**vars())

# extract xml from stdin
all_text = sys.stdin.read()
for m in re.finditer(xml_element, all_text):
    print("start: {span[0]}, end: {span[1]}, xml: {begin}{xml}{end}".format(
            span=m.span(), xml=m.group(), begin="{{{", end="}}}"))
    # assert well-formness of the matched xml text by parsing it
    etree.XML(m.group())

在匹配更多种类的xml元素和避免误报之间存在权衡。

更强大的解决方案应该考虑输入的格式,即QUnit,Javadoc词法分析器/解析器可以帮助提取稍后可以提供给xml解析器的xml片段。

请注意:

Why it's not possible to use regex to parse HTML/XML: a formal explanation in layman's terms

Can you provide some examples of why it is hard to parse XML and HTML with a regex?

答案 1 :(得分:1)

无需手动提取元素。您可以通过在处理期间将数据包装在根节点中来利用全面的XML生态系统。

例如,Java源文件或Javascript文件在技术上是XML,如果它位于根元素内。

然后,您可以使用为此目的设计的工具,例如XPath或SAX。我用过xmlstarlet。