![为什么 awk 中的浮点比较没有达到预期结果?](https://linux22.com/image/199590/%E4%B8%BA%E4%BB%80%E4%B9%88%20awk%20%E4%B8%AD%E7%9A%84%E6%B5%AE%E7%82%B9%E6%AF%94%E8%BE%83%E6%B2%A1%E6%9C%89%E8%BE%BE%E5%88%B0%E9%A2%84%E6%9C%9F%E7%BB%93%E6%9E%9C%EF%BC%9F.png)
我有以下 awk 脚本:
{
if ($1 > 1000) {
print $0
}
}
它应该打印第一列也是唯一一列的值高于 1000 的所有行。
这是测试数据:
1,151
1001,055
756,75788
使用awk -f my_script.awk my_data
,我有以下输出:
1001,055
756,75788
我期待的地方:
1001,055
awk 版本是:
GNU Awk 5.0.0, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2)
我做错了什么?
编辑:
正如评论中所说:
逗号在这里不是分隔符,它是小数点分隔符,在法语中使用,根据维基百科,在除英语之外的所有符号系统中使用。
编辑2:示例数据中只有一列。真实数据中,字段分隔符为“;”。
答案1
免责声明由于对原始问题的误解,以下第一个解决方案已过时。请参阅编辑 1 和 2 以获取匹配的解决方案。
awk
默认情况下不将逗号识别为分隔符。它仅对制表符和空格执行此操作。因此,您需要显式定义分隔符,否则awk
比较字符串值。
BEGIN {FS=","}
$1 > 1000
请注意,我还使用了简化的表示法,即满足条件时打印一行。这只是作为更简单代码的提示。
或者在命令行中指定分隔符:
awk -F, -f script.awk infile
编辑1以下规范,
将用作小数分隔符。请注意,awk
将被视为.
小数分隔符,并且使用小数分隔符的区域设置通常很麻烦。
对于选项 1,我建议使用一个小技巧:仍然将整数和分数作为单独的、逗号分隔的字段并单独评估它们:
BEGIN {FS=","}
$1==1000 && $2>0 || $1 > 1000
这将 a) 跳过尝试使用语言环境, b) 跳过尝试在- 和-separationawk
之间来回翻译。缺点是,如果浮点数据较多,字段编号可能与列标题不匹配。然而,如果它真的只是打印匹配线,那么这不起作用。,
.
像这样的 infile
1,151
1001,055
756,75788
1000
1000,00
1000,000001
会回来
1001,055
1000,000001
编辑2另一个可能更优雅的选项是将第一个字段转换为点分隔的浮动以进行比较:
gensub(/,/,".","g",$1)+0 > 1000
其工作原理如下:将字段 1 解释为字符串,替换,
为.
,添加0
以使其成为 -logic 中的数字awk
,进行比较并在条件为 true 时打印。优点是,通过;
作为字段分隔符的规范,该解决方案不会引入字段编号问题。
一般来说,我建议,
尽可能避免使用小数分隔符。当然这取决于谁提供数据。
答案2
要将 @Ed Morton 和 @steeldriver 的评论放入答案中,如果您确保使用定义它的语言环境,则可以让 GNU awk 将逗号视为小数分隔符,并启用--posix
或--use-lc-numeric
/ -N
。
例如:
$ LC_NUMERIC=fi_FI.UTF-8 awk -N '$1 > 1000' data.txt
1001,055
或者:
$ LC_NUMERIC=fi_FI.UTF-8 awk --posix '$1 > 1000' data.txt
1001,055
只要它只将点视为小数点分隔符,类似的东西756,75788
就不会被识别为数字,而是被视为字符串,并且比较是基于字符串的。7
排序在 后面1
,,
排序在前面0
,因此756,75788
>1000
和1,151
< 1000
。 (虽然我不确定它是否也使用区域设置的整理规则,但这可能会影响如何,
解释。)
您可能会尝试使用 强制它将值视为数字($1 + 0)
,但这只会查看逗号之前的部分,因为它本身不会使其将逗号作为小数分隔符。对于问题中的数据,这似乎可行,但它会变成例如1000,1
,1000
而不会被打印。如果您想检查“至少 1000”,而不是“大于 1000”,那么您可以使用($1 + 0) >= 1000
并忽略小数部分。
看:6.1.4.2 语言环境会影响转换和6.3.2.1 字符串类型与数字类型在 GNU awk 手册中。(后一页上的示例很愚蠢,因为37
<42
无论比较是文本还是数字。)