【问题标题】:Extract an XML element from a non XML text file从非 XML 文本文件中提取 XML 元素
【发布时间】:2012-12-03 06:15:42
【问题描述】:

有些文件是用 Markdown、MediaWiki 语法、克里奥尔语、源代码和纯文本编码的。

这些文件可能包含一个杂散的 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 解析中处理替换会更好吗?

【问题讨论】:

  • 问题不清楚。是否要提取输入中对应位置的所有 xml 文本?
  • 是的。我想知道原始 XML 元素出现在哪些行和哪些列。现在我一直在解决这个问题,似乎将整个文件包装为 XML,然后只使用 XML 本身来提供替换,而不是尝试自己做。不过并没有真正帮助差异。
  • 如果你只是想知道它出现在哪里,使用 grep 可能是你想要做的。或者你需要对内容做些什么?
  • 只是发布一些示例输入和预期输出,所以我们没有猜测。
  • @EdMorton:想象一下这样的帖子或评论,我突然决定要包含一个文件,我写了 。这就是我的意思。

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


【解决方案1】:

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

$ 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 元素,假设每个根元素没有嵌套,并且它不在 cmets 或 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?

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 您不能用'&lt;root&gt;%s&lt;/root&gt;' % text 包装任意文本并期望它是格式良好的xml。例如,Java 源文件或 Javascript 文件可以包含文本(例如,独立的 &lt;),如果使用根元素包装,它们会成为格式错误的 xml。
    • 塞巴斯蒂安。你是对的。这就是我逃避这些元素的原因。您可以对这些实体进行编码(xml esc)或将块包装为CDATA
    • 如果您转义文本或使用 CDATA,那么您会丢失内部 xml 元素:您将只有根元素和一大块非结构化文本。
    • 如果您正在创建一个 %s 然后在 CData 节点中向其添加文本或在插入时转义。如果您对标签进行愚蠢的附加和前置操作,它们将只是内部 XML 节点,这很好。就我而言,我通常会在 之后添加我的内部节点,因此我通常会自己放入 CDATA 节点。 (< 到处都是丑陋的。)也就是说,当我第一次进行愚蠢的包装时,不可能或不可能有特殊节点。我想简单地对其进行编码,将其放入,然后寻找可解析的“特殊”节点并取消编码会很酷。
    • 我在管道中有一个名为“markupcontrol”的项目,它是一个标记语言聚合器 - 您可以使用 whatever 生成器,也可以使用您想要使用的生成器,也可以使用您的自定义替换。 (txt2tags、pandoc 等)。很快就会发布github链接。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    • 2013-11-06
    • 2018-12-05
    • 1970-01-01
    相关资源
    最近更新 更多