从 gpx 到 csv 文件

从 gpx 到 csv 文件
<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 格式,因此您无法可靠地使用awksed解析它。

相反,使用类似的东西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

请,请 - 不要使用基于正则表达式的解决方案,例如awkor 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 (使用 安装)的作者之一

相关内容