从简单的 XML 文件中提取数据

Posted

技术标签:

【中文标题】从简单的 XML 文件中提取数据【英文标题】:Extraction of data from a simple XML file 【发布时间】:2011-01-14 09:29:33 【问题描述】:

我有一个包含内容的 XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>

我需要一种方法来提取 &lt;job..&gt; &lt;/job&gt; 标记中的内容,在这种情况下为 programmin。这应该在 linux 命令提示符下使用 grep/sed/awk 完成。

【问题讨论】:

如果你的 XML 文件包含这个:sample.com/">Tom & Jerry 你想要让 XML 转义不理会的结果:Tom & Jerry 还是您希望撤消转义,就像 XML 解析器那样:Tom & Jerry 如果是后者,对不起,我不知道如何在 Unix 上做到这一点文本工具。 @Paul s/&amp;amp;/\&amp;/g&amp;quot; 等也一样,当然它不会推广到用户定义的实体等。 https://***.com/a/17333829/3291390 【参考方案1】:

您真的必须只使用这些工具吗?它们不是为 XML 处理而设计的,虽然在大多数情况下可以获得可以正常工作的东西,但在编码、换行等边缘情况下会失败。

我推荐xml_grep:

xml_grep 'job' jobs.xml --text_only

它给出了输出:

programming

在 ubuntu/debian 上,xml_grep 位于 xml-twig-tools 包中。

【讨论】:

严格的安装说明非常适合 xml_grep sudo apt-get install xml-twig-tools "grep" 只是无痛文本搜索的同义词。【参考方案2】:
 grep '<job' file_name | cut -f2 -d">"|cut -f1 -d"<"

【讨论】:

只有标签在不同的行时才会失败 格式良好的 XML 可以通过十几种其他方式使其失败。【参考方案3】:

使用 xmlstarlet:

echo '<job xmlns="http://www.sample.com/">programming</job>' | \
   xmlstarlet sel -N var="http://www.sample.com/" -t -m "//var:job" -v '.'

【讨论】:

有大量不同的工具使用标准 XPath 表示法从 XML 中提取信息——xmlstarlet 只是其中之一。其他包括xmllintxpath等。见***.com/questions/15461737/…【参考方案4】:

请不要在 XML 上使用基于行和正则表达式的解析。这是个坏主意。您可以使用不同格式的语义相同的 XML,而正则表达式和基于行的解析根本无法处理它。

一元标签和可变换行之类的东西——这些 sn-ps '说'同样的东西:

<root>
  <sometag val1="fish" val2="carrot" val3="narf"></sometag>
</root>


<root>
  <sometag
      val1="fish"
      val2="carrot"
      val3="narf"></sometag>
</root>

<root
><sometag
val1="fish"
val2="carrot"
val3="narf"
></sometag></root>

<root><sometag val1="fish" val2="carrot" val3="narf"/></root>

希望这能说明为什么制作基于正则表达式/行的解析器很困难?幸运的是,您不需要这样做。许多脚本语言至少有一个,有时更多的解析器选项。

正如之前的海报所暗示的 - xml_grep 可用。这实际上是一个基于XML::Twig perl 库的工具。然而,它的作用是使用“xpath 表达式”来查找某些内容,并区分文档结构、属性和“内容”。

例如:

xml_grep 'job' jobs.xml --text_only

但是,为了获得更好的答案,这里有几个根据您的源数据“自己动手”的示例:

第一种方式:

使用twig handlers 捕获特定类型的元素并对其进行操作。这样做的好处是它可以“随时”解析 XML,并让您在需要时随时修改它。当您使用purgeflush 处理大文件时,这对于丢弃“处理过的”XML 特别有用:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

XML::Twig->new(
    twig_handlers => 
        'job' => sub  print $_ ->text 
    
    )->parse( <> );

它将使用&lt;&gt; 获取输入(通过管道输入或通过命令行./myscript somefile.xml 指定)并处理它 - 每个job 元素,它将提取并打印任何相关的文本。 (您可能希望 print $_ -&gt; text,"\n" 插入换行符)。

因为它匹配 'job' 元素,所以它也会匹配嵌套的 job 元素:

<job>programming
    <job>anotherjob</job>
</job>

将匹配两次,但也会将某些输出打印两次。但是,如果您愿意,可以改为匹配 /job。有用 - 这让你例如打印和删除一个元素或复制并粘贴一个修改 XML 结构的元素。

或者 - 先解析,然后根据结构“打印”:

my $twig = XML::Twig->new( )->parse( <> );
print $twig -> root -> text;

由于job 是您的根元素,我们需要做的就是打印它的文本。

但我们可以更挑剔一点,寻找job/job 并专门打印出来:

my $twig = XML::Twig->new( )->parse( <> );
print $twig -> findnodes('/job',0)->text;

您也可以使用 XML::Twigs pretty_print 选项重新格式化您的 XML:

XML::Twig->new( 'pretty_print' => 'indented_a' )->parse( <> ) -> print;

有多种输出格式选项,但对于更简单的 XML(如您的),大多数看起来都非常相似。

【讨论】:

【参考方案5】:

只需使用 awk,无需其他外部工具。如果您想要的标签出现在 multitine 中,则以下工作。

$ cat file
test
<job xmlns="http://www.sample.com/">programming</job>
<job xmlns="http://www.sample.com/">
programming</job>

$ awk -vRS="</job>" 'gsub(/.*<job.*>/,"");print' file
programming

programming

【讨论】:

&lt;/ job&gt; 有效,但您的脚本无法识别它。 &lt;!-- &lt;/job&gt; --&gt; 是需要忽略的注释(&lt;!CDATA[[ &lt;/job&gt; ]]&gt; 是文字数据),但您的脚本不知道 。还有一些情况,比如有一个定义新宏的 DTD,这样&amp;foo; 会扩展为本地指定的东西,还有一些简单的情况,比如需要将&amp;amp; 转换为&amp;。尝试滚动您自己的 XML 解析(或更糟糕的是,生成)会导致无休止的极端情况和需要单独运行和修复的小细节。【参考方案6】:

使用 sed 命令:

例子:

$ cat file.xml
<note>
        <to>Tove</to>
                <from>Jani</from>
                <heading>Reminder</heading>
        <body>Don't forget me this weekend!</body>
</note>

$ cat file.xml | sed -ne '/<heading>/s#\s*<[^>]*>\s*##gp'
Reminder

说明:

cat file.xml | sed -ne '/&lt;pattern_to_find&gt;/s#\s*&lt;[^&gt;]*&gt;\s*##gp'

n - 禁止打印所有行e - 脚本

/&lt;pattern_to_find&gt;/ - 查找包含指定模式的行,例如&lt;heading&gt;

接下来是替换部分 s///p,它删除了除所需值之外的所有内容,其中 / 被替换为 # 以提高可读性:

s#\s*&lt;[^&gt;]*&gt;\s*##gp\s* - 如果存在则包括空格(最后相同)&lt;[^&gt;]*&gt; 表示 &lt;xml_tag&gt; 作为非贪婪正则表达式替代原因 &lt;.*?&gt; 不适用于 sed g - 替换所有内容,例如关闭 xml &lt;/xml_tag&gt; 标签

【讨论】:

【参考方案7】:

假设同一行,从标准输入输入:

sed -ne '/<\/job>/  s/<[^>]*>\(.*\)<\/job>/\1/; p '

注意:-n 停止它自动输出所有内容; -e 表示它是一个单行(aot 脚本)/&lt;\/job&gt; 就像一个 grep; s 去掉 opentag + 属性和 endtag; ; 是一个新的声明; p 打印; 使 grep 应用于两个语句,作为一个。

【讨论】:

【参考方案8】:

怎么样:

cat a.xml | grep '<job' | cut -d '>' -f 2 | cut -d '<' -f 1

【讨论】:

UUOC。 grep '&lt;job' a.xml | ... @ghost 但是但是,我认为它更清洁/更好/没有那么多浪费/我有特权浪费过程! partmaps.org/era/unix/award.html#cat(实际上,我认为它是更容易编辑文件名,因为更接近开始) 如果你使用&lt; a.xml | grep ...,你会更接近开始。【参考方案9】:

演出有点晚了。

xmlcutty 从 XML 中删除节点:

$ cat file.xml
<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>
<job xmlns="http://www.sample.com/">designing</job>
<job xmlns="http://www.sample.com/">managing</job>
<job xmlns="http://www.sample.com/">teaching</job>

path 参数指定要剪切的元素的路径。在这种情况下,由于我们对标签根本不感兴趣,我们将标签重命名为\n,所以我们得到了一个不错的列表:

$ xmlcutty -path /job -rename '\n' file.xml
programming
designing
managing
teaching

请注意,XML 开头无效(无根元素)。 xmlcutty 也可以处理稍微损坏的 XML。

【讨论】:

【参考方案10】:

你的xml文件.xml

<item> 
  <title>15:54:57 - George:</title>
  <description>Diane DeConn? You saw Diane DeConn!</description> 
</item> 
<item> 
  <title>15:55:17 - Jerry:</title> 
  <description>Something huh?</description>
</item>

grep 'title' yourxmlfile.xml

  <title>15:54:57 - George:</title>
  <title>15:55:17 - Jerry:</title>

grep 'title' yourxmlfile.xml | awk -F">" '打印 $2'

  15:54:57 - George:</title
  15:55:17 - Jerry:</title

grep 'title' yourxmlfile.xml | awk -F">" '打印 $2' | awk -F"

  15:54:57 - George:
  15:55:17 - Jerry:

【讨论】:

以上是关于从简单的 XML 文件中提取数据的主要内容,如果未能解决你的问题,请参考以下文章

Jquery datatable 在从简单的 Web 服务调用 Web 方法时未在表中显示任何数据

如何更新使用 C++ 中的 ATL OLE 数据库从简单的 SQL 服务器表中获取的行数据

PowerShell 从简单的 .ini 文件中读取单个值

从简单的HQL查询中获取无效的参数索引1

将数据从简单的 NSView 传递到 SwiftUI 视图

从简单的 python 列表创建图像和标签的 tensorflow 数据集