【问题标题】:Extraction of data from a simple XML file从简单的 XML 文件中提取数据
【发布时间】: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; 等也一样,当然它不会推广到用户定义的实体等。

标签: xml bash sed awk grep


【解决方案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 '.'

【讨论】:

【解决方案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:
          

          【讨论】:

            猜你喜欢
            • 2013-07-28
            • 2013-03-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-02-02
            相关资源
            最近更新 更多