【问题标题】:Parsing XML file into fields将 XML 文件解析为字段
【发布时间】:2013-10-23 19:55:28
【问题描述】:

我需要从 XML 文件中读取行并将它们解析为字段。一行定义为以 结尾的文本。它可以是单行,也可以是由 CR/LF 分隔的多行。这是一个典型的行:

<Label Name="lblIncidentTypeContent" Increasable="true" Left="140" Top="60"
 Width="146 SpeechField="IncidentType_V" TextAlign="MiddleLeft" WidthPixel="-180"
 WidthPercent="50" />

读完该行后,我需要将其解析为名称、左侧、宽度等字段。然后我想以特定顺序输出包含数据的 CSV。然后读取下一行直到 EOF。

我已经很久没有进行 Perl(或任何其他类型的)编程了。欢迎任何帮助。

【问题讨论】:

  • 如果您使用 perl,请用它标记您的问题。
  • 我建议使用您所用语言的成熟 xml 解析器库,而不是手动解析它

标签: xml perl parsing


【解决方案1】:

不要将 XML 视为基于行的数据,因为它不是。相反,使用一个好的 XML 解析器,Perl 有很多。

不要使用 XML::Simple!

own documentation 表示已弃用:

不鼓励在新代码中使用此模块。其他模块也可以提供更直接和一致的界面。特别是,强烈推荐使用 XML::LibXML。

这个模块的主要问题是大量的选项以及这些选项交互的任意方式 - 通常会产生意想不到的结果。

所以我们将使用 XML::LibXML 模块,它与 GNOME 项目中的外部 libxml2 库接口。这样做的好处是我们可以使用 XPath 表达式来查询我们的数据。要读取或写入 CSV,应使用 Text::CSV 模块。

use strict; use warnings;
use XML::LibXML;
use Text::CSV;

# load the data
my $data = XML::LibXML->load_xml(IO => \*STDIN) or die "Can't parse the XML";

# prepare CSV output:
my $csv = Text::CSV->new({ binary => 1, escape_char => "\\", eol => "\n" });
# Text::CSV doesn't like bareword filehandles
open my $output, '>&:utf8', STDOUT or die "Can't dup STDOUT: $!";

my @cols  = qw/ name left width /; # the column names in the CSV
my @attrs = qw/ Name Left Width /; # the corresponding attr names in the XML

# print the header
$csv->print($output, \@cols);

# extract data
for my $label ($data->findnodes('//Label')) {
  my @fields = map { $label->getAttribute($_) } @attrs;
  $csv->print($output, \@fields);
}

测试数据(我冒昧关闭了Width attr的值):

<foo>
  <Label Name="lblIncidentTypeContent" Increasable="true" Left="140" Top="60"
    Width="146" SpeechField="IncidentType_V" TextAlign="MiddleLeft" WidthPixel="-180"
    WidthPercent="50" />
  <Label Name="Another TypeContent" Increasable="true"
         Width="123"                SpeechField="IncidentType_V"
         Left="41,42"               Top="13"
         TextAlign="TopLeft"        WidthPixel="-180"
         WidthPercent="50"
  />
</foo>

输出:

name,left,width
lblIncidentTypeContent,140,146
"Another TypeContent","41,42",123

【讨论】:

  • 这看起来就是我要找的东西。但是有一个问题,这段代码是否假设每条记录的属性总是以相同的顺序排列?如果是这样,那将不适用于我的数据。
  • @EdWall 不,代码不假定任何顺序:它使用@attrs 中的顺序。 map {BLOCK} @items 只是一个花哨的 foreach (@items) {BLOCK} 循环。 XML 属性本质上是无序的。
【解决方案2】:

嗯,这是 Perl,你有几种方法可以做到:

  • 蛮力。将文件插入,并跟踪您何时遇到开头的
  • 轻微用力。使用 XML::Simple 等基本库加载文件,然后使用 Data::Dumper 以您选择的格式将其输出。前者给你一个哈希,然后你可以随意使用键和值。
  • 使用 XML 库。 CPAN 中有很多,从非常接近底层 libxml 语义的到非常抽象的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-01
    • 1970-01-01
    • 2020-03-22
    • 2015-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多