Linux

從 gpx 到 csv 文件

  • June 11, 2021
<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

GPX 是一種 XML 格式,因此您不能可靠地使用awksed解析它。

相反,使用類似XMLStarlet的東西(假設 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

您還可以使用xqyq來自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"

請,請 - 不要使用基於正則表達式的解決方案,例如awkor sed

XML是上下文相關的,而正則表達式則不是——所以它們永遠無法正常工作,它們充其量只是一個 hack

但是 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(<>)'

引用自:https://unix.stackexchange.com/questions/343636