使用sed筛选多行PCRE匹配

3

我有多个文本文件,其格式如下:

1  DAEJ             X            -3120041.6620      -3120042.0476     -0.3856      0.0014               
                    Y             4084614.2137       4084614.6871      0.4734      0.0015               
                    Z             3764026.4954       3764026.7346      0.2392      0.0014               

                    HEIGHT            116.0088           116.6419      0.6332      0.0017      0.0017    8.0
                    LATITUDE     36 23 57.946407    36 23 57.940907   -0.1699      0.0013      0.0012   57.5      0.0012   62.9
                    LONGITUDE   127 22 28.131395   127 22 28.132160    0.0190      0.0012      0.0013    2.3      0.0013

我希望您能将其通过过滤器运行,以便输出结果如下所示:
DAEJ: 36 23 57.940907, 127 22 28.132160, 116.6419

我可以使用grepWin和命名捕获来轻松完成,只需搜索:

(?<site>\w\w\w\w+)<filler>\r\n\r\n<filler>(?<height>\-?\d+\.\d+)<filler>(?<heightRMS>\d+\.\d+)<filler>\r\n<filler>(?<lat>\-?\ *\d+\ +\d+\ +\d+\.\d+)<filler>(?<latRMS>\d+\.\d+)<filler>\r\n<filler>(?<lon>\-?\ *\d+\ +\d+\ +\d+\.\d+)<filler>(?<lonRMS>\d+\.\d+)<filler>

并用(replacing with)(忽略未引用的组,我将在其他实现中使用它):

$+{site}: $+{lat}, $+{lon}, $+{height}

当然,您可以通过手动操作GUI来完成,但这需要付出一定的代价。我想知道是否有一种方法可以通过将pcregrep输出管道传输到sed进行文本替换的方式进行脚本编写?我了解 pcregrep -M选项以匹配上面的多行正则表达式模式,并且在此之前已经成功实现,但我卡在了sed的问题上。

我知道我可以使用\1\9的反向引用,而不是命名捕获,但出于个人偏好,我选择了后者。 - alfie
在史蒂夫下面的出色回答之后,我意识到我仍然需要捕获奇怪格式的负值情况中可能存在的减号,例如LATITUDE/LONGITUDE数据行中的“- 6 29 27.798898”。 - alfie
2个回答

3

我将使用 awk 处理您的文本文件:

awk '$1 ~ /^[0-9]+$/ { printf "%s: ", $2 } $1 == "HEIGHT" { height = $3 } $1 == "LATITUDE" { printf "%s %s %s, ", $2, $3, $4 } $1 == "LONGITUDE" { printf "%s %s %s, %s\n", $5, $6, $7, height }' file.txt

为了易读性,将内容分成多行:

$1 ~ /^[0-9]+$/ { 
    printf "%s: ", $2
}

$1 == "HEIGHT" {
    height = $3
}

$1 == "LATITUDE" {
    printf "%s %s %s, ", $2, $3, $4
}

$1 == "LONGITUDE" {
    printf "%s %s %s, %s\n", $5, $6, $7, height
}

结果:

DAEJ: 36 23 57.946407, 127 22 28.132160, 116.6419

编辑:

将以下代码放入名为 script.awk 的文件中:

$3 == "X" {
    printf "%s: ", $2
}

$1 == "HEIGHT" {
    height = $3
}

$1 == "LATITUDE" {
    if ($2 == "-" && $6 == "-") { printf "-%s %s %s, ", $7, $8, $9 }
    else if ($2 == "-") { printf "%s %s %s, ", $6, $7, $8 }
    else if ($5 == "-") { printf "-%s %s %s, ", $6, $7, $8 }
    else { printf "%s %s %s, ", $5, $6, $7 }
}

$1 == "LONGITUDE" {
    if ($2 == "-" && $6 == "-") { printf "-%s %s %s, %s\n", $7, $8, $9, height }
    else if ($2 == "-") { printf "%s %s %s, %s\n", $6, $7, $8, height }
    else if ($5 == "-") { printf "-%s %s %s, %s\n", $6, $7, $8, height }
    else { printf "%s %s %s, %s\n", $5, $6, $7, height }
}

这样运行:

awk -f script.awk file.txt

谢谢!这让我离解决方案更近了一步,做出以下修改:awk '$3 == "X" {printf "%s: ", $2} $1 == "HEIGHT" { height = $3 } $1 == "LATITUDE" { printf "%s %s %s, ", $5, $6, $7 } $1 == "LONGITUDE" { printf "%s %s %s, %s\n", $5, $6, $7, height }' file.txt。现在,我需要进一步修改它以允许纬度/经度的负DMS值,即正则表达式模式的\-?\ *\d+\ +\d+\ +\d+\.\d+部分,其中可能的减号可能与第一个整数值分隔开,也可能没有。 - alfie
我能像这样做吗 awk '$3 == "X" {printf "%s: ", $2} $1 == "HEIGHT" { height = $3 } $1 == "LATITUDE" { printf (($2 == "-") ? "%s%s %s %s, ", $6, $7, $8, $9 : "%s %s %s, ", $5, $6, $7) } $1 == "LONGITUDE" { printf (($2 == "-") ? "%s%s %s %s, %s\n, ", $6, $7, $8, $9, height : "%s %s %s, %s\n", $5, $6, $7, height) }' 来首先测试减号的存在吗? - alfie
@阿尔菲,我给你展示一个if else代码块,稍等一下。 - Steve
谢谢。我尝试了(http://ideone.com/p7AkL)上的一些代码,但是在我的机器上无法运行。我正在cygwin中运行它。我仍然在苦苦挣扎awk/gawk语法。 - alfie
@alfie,根据你的代码,在第二组三个数中没有测试负数。这些值不能为负数吗?另外,HEIGHT可以是负数吗? - Steve
显示剩余2条评论

0
这可能适用于你(GNU sed):
sed '/^DAEJ/,/^\s*LONGITUDE/!d;/HEIGHT/{s/^\s*\S*\s*\S*\s*\(\S*\).*/\1/;h};/LATITUDE/{s/^\s*\(\S*\s*\)\{4\}\(\(\S*\s*\)\{2\}\S*\).*/\2/;H};/LONGITUDE/!d;s/^\s*\(\S*\s*\)\{4\}\(\(\S*\s*\)\{2\}\S*\).*/ \2/;H;g;y/\n/,/;s/\([^,]*\),\(.*\)/DAEJ: \2, \1/' file1 file2 filen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接