<wpt lat="1.345529841" lon="103.7577152"><time>2010-01-01T00:00:00Z</time</wpt>
<wpt lat="1.345529841" lon="103.7577152"><time>2010-01-01T00:00:00Z</time></wpt>
<wpt lat="1.3982529841" lon="103.90877152"><time>2010-01-01T00:00:00Z</time></wpt>
我有一个像上面的行一样的文件需要转换成
1.345529841,103.7577152,2010-01-01 00:00:00
1.345529841,103.7577152,2010-01-01 00:00:00
1.3982529841,103.90877152,2010-01-01 00:00:00
答案1
GPX 是一种 XML 格式,因此您无法可靠地使用awk
或sed
解析它。
相反,使用类似的东西XML小星(假设 XML 文档格式良好且不包含错误):
$ xmlstarlet sel -t -m '//wpt' \
-v '@lat' -o ',' \
-v '@lon' -o ',' \
-v 'time' -nl data.gpx
1.345529841,103.7577152,2010-01-01T00:00:00Z
1.345529841,103.7577152,2010-01-01T00:00:00Z
1.3982529841,103.90877152,2010-01-01T00:00:00Z
或者:
xmlstarlet sel -t -m '//wpt' -v 'concat(@lat, ",", @lon, ",", time)' -nl data.wpx
您还可以使用xq
(部分yq
来自https://kislyuk.github.io/yq/):
$ xq -r '.. | .wpt? // empty | .[] | map(values) | @csv' data.gpx
"1.345529841","103.7577152","2010-01-01T00:00:00Z"
"1.345529841","103.7577152","2010-01-01T00:00:00Z"
"1.3982529841","103.90877152","2010-01-01T00:00:00Z"
这将查找所有wpt
节点并提取所有属性和子节点的值,并从中创建 CSV 输出。
如果您需要重新排列列或挑选用于每列的值,您也可以这样做
$ xq -r '.. | .wpt? // empty | .[] | [."@lat", ."@lon", .time] | @csv' data.gpx
"1.345529841","103.7577152","2010-01-01T00:00:00Z"
"1.345529841","103.7577152","2010-01-01T00:00:00Z"
"1.3982529841","103.90877152","2010-01-01T00:00:00Z"
答案2
请,请 - 不要使用基于正则表达式的解决方案,例如awk
or sed
。
XML
是上下文相关的,而正则表达式不是 -所以它们永远无法正常工作,它们充其量只是一些黑客行为。
但 XML 确实有一个解决这个问题的方法 - 它被称为xpath
,它允许您以上下文方式“搜索”。
就拿你的例子来说:
#!/usr/bin/perl
use warnings;
use strict;
use XML::Twig;
my $xml = XML::Twig -> new -> parsefile('your_file.xml');
foreach my $wpt ( $xml -> get_xpath('//wpt') ) {
print join ",", $wpt -> att('lat'),
$wpt -> att('lon'),
$wpt -> first_child_text('time'), "\n";
}
它给出了所需的结果,但它也将处理各种完全有效且语义相同的 XML 形式。
就像缩进一样:
<xml>
<wpt lat="1.345529841" lon="103.7577152">
<time>2010-01-01T00:00:00Z</time>
</wpt>
<wpt lat="1.345529841" lon="103.7577152">
<time>2010-01-01T00:00:00Z</time>
</wpt>
<wpt lat="1.3982529841" lon="103.90877152">
<time>2010-01-01T00:00:00Z</time>
</wpt>
</xml>
全部在一行上:
<xml><wpt lat="1.345529841" lon="103.7577152"><time>2010-01-01T00:00:00Z</time></wpt><wpt lat="1.345529841" lon="103.7577152"><time>2010-01-01T00:00:00Z</time></wpt><wpt lat="1.3982529841" lon="103.90877152"><time>2010-01-01T00:00:00Z</time></wpt></xml>
另一种缩进样式:
<xml>
<wpt
lat="1.345529841"
lon="103.7577152">
<time>2010-01-01T00:00:00Z</time>
</wpt>
<wpt
lat="1.345529841"
lon="103.7577152">
<time>2010-01-01T00:00:00Z</time>
</wpt>
<wpt
lat="1.3982529841"
lon="103.90877152">
<time>2010-01-01T00:00:00Z</time>
</wpt>
</xml>
甚至:
<xml
><wpt
lat="1.345529841"
lon="103.7577152"
><time
>2010-01-01T00:00:00Z</time></wpt><wpt
lat="1.345529841"
lon="103.7577152"
><time
>2010-01-01T00:00:00Z</time></wpt><wpt
lat="1.3982529841"
lon="103.90877152"
><time
>2010-01-01T00:00:00Z</time></wpt></xml>
这些在语义上都是相同的,并且应该以同样的方式进行解析。希望大家都清楚,执行此操作的正则表达式比仅使用 XML 解析器复杂得多。
但为了简洁起见:
perl -MXML::Twig -0777 -e 'XML::Twig->new(twig_handlers=>{wpt=>sub{print join ",", $_->att("lat", $_->att("lon"),$_->first_child_text("time"), "\n" }})->parse(<>)'
答案3
您可以使用sed
删除不需要的字符:
sed 's/[^0-9.T:-]\+/,/g;s/T/ /;s/^,\|,$//g' file
s/[^0-9.T:-]\+/,/g
用逗号替换不需要的字符
s/T/ /
T
正在用空格替换该字符
s/^,\|,$//g
正在删除第一个和最后一个逗号
答案4
假设f.xml
我们的输入(有效的 xml):
$ perl -MXML::DT -E 'dt("f.xml",
time=>sub{$a=father;
$c =~ s/[TZ]/ /g;
say "$a->{lat},$a->{lon},$c"}
)'
-MXML::DT
加载 XML::DT 模块(xml 向下翻译器)dt( file, time => sub{....})
:解析文件,每次我们看到time
执行相应的子文件$a=father
:从父亲那里获取属性$c
: 是当前元素内容
cpan XML::DT
警告:我是 XML::DT (使用 安装)的作者之一