我需要对 XML 文件进行解析。我需要获取时间代码(开始和结束)以及与该时间相关的句子。
XML 文件是这样的:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Trans SYSTEM "trans-14.dtd">
<Trans scribe="jj" audio_filename="01" version="1" version_date="150211">
<Episode>
<Section type="report" startTime="0" endTime="50.28281021118164">
<Turn startTime="0" endTime="50.28281021118164">
<Sync time="0"/>
<Sync time="1.195"/>
Something
<Sync time="2.654"/>
Something 2
<Sync time="4.356"/>
Something 3
<Sync time="9.321"/>
Something 4
<Sync time="22.171"/>
Something 5
<Sync time="28.351"/>
Something 6
<Sync time="35.708"/>
Something 7
<Sync time="43.04"/>
Something 8
</Turn>
</Section>
</Episode>
我在 Perl 中尝试过这个,但效果不佳:
#!/usr/bin/perl -nw
next if ! /<Sync/;
$stime = "";
$sentence = "";
$etime = "";
$stime = $1 if (/Sync time="([0-9]+\.[0-9]*)"/);
$sentence = <>;
chomp($sentence);
if ($stime eq ''){ $stime = 0;}
print "$stime $sentence\n";
__END__
因为我想要的输出格式是:
0 1.195
1.195 2.654 Something
2.654 4.356 Something 2
4.356 9.321 Something 3
9.321 22.171 Something 4
22.171 28.351 Something 5
28.351 35.708 Something 6
35.708 43.04 Something 7
43.04 endTime Something 8
非常感谢
最佳答案
所以首先 - 对 XML 进行面向行的解析确实很糟糕。 XML 是一种数据格式,其结构非常重要 - 因此,有一些东西您可以用完全有效的方式重新构造它,但它会被破坏。
所以你的 10 份开胃菜:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig -> new -> parsefile ( 'sample.xml' );
my $previous_sync = 0;
foreach my $sync ( $twig -> get_xpath('Episode/Section/Turn/Sync') ) {
my $sync_time = $sync -> att('time');
print "$previous_sync $sync_time ", $sync->text,"\n";
$previous_sync = $sync_time;
}
print "$previous_sync ", $twig -> get_xpath('Episode/Section',0) -> att('endTime'),"\n";
现在,我遇到了一个小问题,因为您的“Somethings”实际上与相应的“sync”元素没有关联。它们是父级的“文本内容”Turn
。 (Sync
元素是一元标签)。
但是希望这说明了一种更好的解析 XML 的方法吗?
编辑:更新以按原样使用您的文本。
注意:我必须修改您的 XML 以包含 </Trans>
作为最后一行,例如:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Trans SYSTEM "trans-14.dtd">
<Trans scribe="jj" audio_filename="01" version="1" version_date="150211">
<Episode>
<Section type="report" startTime="0" endTime="50.28281021118164">
<Turn startTime="0" endTime="50.28281021118164">
<Sync time="0"/>
<Sync time="1.195"/>
Something
<Sync time="2.654"/>
Something 2
<Sync time="4.356"/>
Something 3
<Sync time="9.321"/>
Something 4
<Sync time="22.171"/>
Something 5
<Sync time="28.351"/>
Something 6
<Sync time="35.708"/>
Something 7
<Sync time="43.04"/>
Something 8
</Turn>
</Section>
</Episode>
</Trans>
因此,如果看起来仍然没问题,并且您实际上并没有尝试使用损坏的 XML,那么这会提供所需的输出。
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $previous_sync;
sub handle_sync {
my ( $twig, $sync ) = @_;
my $sync_time = $sync->att('time');
if ( not defined $previous_sync ) {
$previous_sync = $sync_time;
return;
}
print "$previous_sync $sync_time ";
$previous_sync = $sync_time;
my (@sync_text) = split( "\n", $sync->parent->text );
pop(@sync_text); #discard blank line.
my $line = pop(@sync_text);
if ( defined $line ) {
$line =~ s/^\s+//g;
print $line;
}
print "\n";
}
my $twig = XML::Twig->new( twig_handlers => { 'Sync' => \&handle_sync } )
->parsefile('sample.xml');
print "$previous_sync ",
$twig->get_xpath( 'Episode/Section', 0 )->att('endTime'), " ";
my @sync_text =
split( "\n", $twig->get_xpath( 'Episode/Section/Turn', 0 )->text );
my $line = $sync_text[-2];
$line =~ s/^\s+//g;
print $line, "\n";
这有点捏造,因为其中的“文本”是Turn
的一部分。元素 - 所以我采取“打印最后(完整)行”的方法。这似乎可行,但如果您有多行,则可能不会。
关于regex - Perl 读取行和下一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29962466/