Linux
從 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
GPX 是一種 XML 格式,因此您不能可靠地使用
awk
或sed
解析它。相反,使用類似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
您還可以使用
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"
請,請 - 不要使用基於正則表達式的解決方案,例如
awk
orsed
。
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(<>)'