arrays - awk for 循环未将数组索引设置为正确的值
问题描述
我有这个小的地理位置数据集。
37.9636140,23.7261360
37.9440840,23.7001760
37.9637190,23.7258230
37.9901450,23.7298770
从一个随机位置。例如,37.97570, 23.66721
我需要使用 awk 创建一个 bash 命令,该命令返回具有简单欧几里得距离的距离。这是我使用的命令
awk -v OFMT=%.17g -F',' -v long=37.97570 -v lat=23.66721 '{for (i=1;i<=NR;i++) distances[i]=sqrt(($1 - long)^2 + ($2 - lat)^2 ); a[i]=$1; b[i]=$2} END {for (i in distances) print distances[i], a[i], b[i]}' filename
当我运行这个命令时,我得到这个不正确的奇怪结果,有人可以向我解释我做错了什么吗?
➜ awk -v OFMT=%.17g -F',' -v long=37.97570 -v lat=23.66721 '{for (i=1;i<=NR;i++) distances[i]=sqrt(($1 - long)^2 + ($2 - lat)^2 ); a[i]=$1; b[i]=$2} END {for (i in distances) print distances[i], a[i], b[i]}' filename
44,746962127881936 37.9440840 23.7001760
44,746962127881936 37.9901450 23.7298770
44,746962127881936 37.9636140 23.7261360
44,746962127881936
44,746962127881936 37.9637190 23.7258230
更新。
附加了@jas 提供的命令,我将其包含od -c
为@mark-fuso suggetsted。
现在的问题是我从@jas 得到不同的结果
展示新问题的命令输出。
awk -v OFMT=%.17g -F, -v long=37.97570 -v lat=23.66721 '
{distance=sqrt(($1 - long)^2 + ($2 - lat)^2 ); print distance, $1, $2}
' file
1,1820150904705098 37.9636140 23.7261360
1,1820150904705098 37.9440840 23.7001760
1,1820150904705098 37.9637190 23.7258230
1,1820150904705098 37.9901450 23.7298770
od -c
显示输入文件的内容。
od -c file
0000000 3 7 . 9 6 3 6 1 4 0 , 2 3 . 7 2
0000020 6 1 3 6 0 \n 3 7 . 9 4 4 0 8 4 0
0000040 , 2 3 . 7 0 0 1 7 6 0 \n 3 7 . 9
0000060 6 3 7 1 9 0 , 2 3 . 7 2 5 8 2 3
0000100 0 \n 3 7 . 9 9 0 1 4 5 0 , 2 3 .
0000120 7 2 9 8 7 7 0 \n
0000130
解决方案
虽然@jas 已经为这个问题提供了一个“修复”,但我想我会就 OP 的代码正在做什么发表一些评论......
一些基础...
awk
程序 ( ) 应用于输入文件的{for (i=1;i<=NR;i++) ... ; b[i]=$2}
每一行- 当从输入文件中读取每一行时,
awk
变量NR
会跟踪行号(即,NR=1
对于第一行,NR=2
对于第二行等) - 在最后一次通过
for
循环时,计数器(i
在这种情况下)将具有一个值NR+1
(即,i++
在最后一次通过循环时应用,从而离开i=NR+1
) - 除非对输入的每一行都有条件检查,否则
awk
程序将对输入文件中的每一行应用(包括空行 - 更多内容见下文) for (i in distances)...
不保证按数字顺序处理数组索引
该awk/for
循环正在执行以下操作:
- 对于第一个输入行 (
NR=1
) 我们得到for (i=1;i<=1;i++) ...
- 对于第二个输入行 (
NR=2
) 我们得到for (i=1;i<=2;i++) ...
- 对于第三个输入行 (
NR=3
) 我们得到for (i=1;i<=3;i++) ...
- 对于第 4 个输入行 (
NR=4
) 我们得到for (i=1;i<=4;i++) ...
对于程序处理的每一行,awk
将覆盖distance[]
数组中所有先前的条目;最终结果是最后一行 ( NR=4
) 将在distance[]
数组的所有 4 个条目中放置相同的值。
a[i]=$1; b[i]=$2
数组分配发生在循环范围之外,因此for
每个输入行将分配一次(即不会被覆盖)但是,数组分配是用i=NR+1
; 最终结果是第一行 ( NR=1
) 的内容存储在数组条目a[2]
中b[2]
,第二行 ( NR=2
) 的内容存储在数组条目中a[3]
,a[3]
等等。
print i, distances[i], a[i], b[i]}
使用我得到的 4 行输入文件修改 OP 的代码并对其运行:
1 0.064310270672728084 # no data for 2nd/3rd columns because a[1] and b[1] are never set
2 0.064310270672728084 37.9636140 23.7261360 # 2nd/3rd columns are from 1st row of input
3 0.064310270672728084 37.9440840 23.7001760 # 2nd/3rd columns are from 2nd row of input
4 0.064310270672728084 37.9637190 23.7258230 # 2nd/3rd columns are from 3rd row of input
由此我们可以看到输出的第一列是相同的(即distance[1]=distance[2]=distance[3]=distance[4]
),而第二列和第三列与输入列相同,只是它们“向下”移动了一行。
这给我们留下了两个悬而未决的问题......
- 为什么OP显示5行输出?
- 为什么第一列是垃圾
44,746962127881936
?
通过在输入文件的末尾添加一个空行,我能够重现此问题:
$ cat geo.dat
37.9636140,23.7261360
37.9440840,23.7001760
37.9637190,23.7258230
37.9901450,23.7298770
<<=== blank line !!
它使用 OP 的awk
代码生成以下内容:
44.746962127881936
44.746962127881936 37.9636140 23.7261360
44.746962127881936 37.9440840 23.7001760
44.746962127881936 37.9637190 23.7258230
44.746962127881936 37.9901450 23.7298770
注意事项:
- 此顺序与 OP 的示例输出不同,可能是由于 OP 的
awk
版本未按for (i in distances)...
数字顺序处理;OP 可以尝试类似for (i=1;i<=NR;i++)...
or的东西for (i=1;i in distances; i++)...
(尽管后者不适用于人口稀少的数组) - OP 输出(在问题中;在对@jas 的回答中)显示逗号 (
,
) 代替.
第一列的句点 (),所以我猜 OP 的 env 正在使用将逗号/句点切换为数千的语言环境/decimal 分隔符(尽管输入数据基于“相反”的语言环境)
请注意,我们终于可以看到来自第 4 行输入的数据(“向下移动”并显示在第 5 行),但第一列的值似乎是一个无意义的值......可以追溯到应用以下反对一个空行:
sqrt(($1 - long)^2 + ($2 - lat)^2 )
sqrt(( - long)^2 + ( - lat)^2 ) # empty line => $1 = $2 = undefined/empty
sqrt(( - 37.97570)^2 + ( - 23.66721^2 )
sqrt( 1442.153790 + 560.136829 )
sqrt( 2002.290619 )
44.746952... # contents of 1st column
要“修复”此问题,OP 可以a)从输入文件中删除空白行或b)向脚本添加一些逻辑以awk
仅在输入行在字段 #1 和 #2 中具有(数字)值时才执行计算(即,$1
并且$2
不为空);由编码人员决定应用多少验证(例如,字段是否为数字,字段是否在合法的 long/lat 值的范围内,等等)。
最后一个与设计相关的评论......如 jas 的回答所示,当所有所需的输出可以在处理每一行输入文件。
推荐阅读
- python - configparser.MissingSectionHeaderError:文件不包含节标题。Reddit 机器人
- java - 如何将此模式存储在 Java 数组列表中?
- discord.js - 我如何获得 guild.id
- python - 在 Python 中使用 ANTLR4 解析数学表达式
- php - 满足特定条件时如何停止 webhook 执行?
- python - 模板渲染期间出错。过滤器无效:'bootstrap'
- python-3.x - Django 模型:“ModelFormOptions”对象没有属性“private_fields”
- reactjs - TypeError:无法读取 tsx 文件上未定义的属性“地图”
- selenium-webdriver - Selenium Webdriver + Jmeter + StormRunner 进行性能测试
- angular - 重置表单值后无法设置文本框值