我有一个程序可以对文件中的一列求和:
awk -v col=2 '{sum+=$col}END{print sum}' input-file
Run Code Online (Sandbox Code Playgroud)
但是,它有一个问题:如果给它一个没有数字数据的文件,(或者如果缺少一个数字),它会将其解释为零。
如果其中一个字段无法解析为数字,我希望它产生错误。
这是一个示例输入:
bob 1
dave 2
alice 3.5
foo bar
Run Code Online (Sandbox Code Playgroud)
我希望它产生错误,因为 'bar' 不是数字,而不是忽略错误。
一种合理的测试方法是使用类似于 的测试来比较字段strtod
,这是awk 用于将字符串转换为数字的方法:
$2 !~ / *[+-]?[[:digit:]]/ { print "NAN: " $2; exit 1; }
Run Code Online (Sandbox Code Playgroud)
以上与 strtod 的不同之处在于它不将 INFINITY 或 NAN 视为“数字”。在 awk 的默认字段拆分行为下,前导空间要求可以放宽——这意味着字段永远不会包含前导空间:
$2 !~ /[+-]?[[:digit:]]/ { print "NAN: " $2; exit 1; }
Run Code Online (Sandbox Code Playgroud)
进一步改进,感谢 Stéphane 的评论和回答:
$2 !~ /^[+-]?([[:digit:]]*\.?[[:digit:]]*([eE][-+]?[[:digit:]]+)?|0[xX][[:xdigit:]]*\.?[[:xdigit:]]*([pP][-+]?[[:digit:]]+)?)$/ { print "NAN: " $2; exit 1; }
Run Code Online (Sandbox Code Playgroud)
为了稍微好一点的易读性,该正则表达式是:
/^[+-]?([[:digit:]]*\.?[[:digit:]]*([eE][-+]?[[:digit:]]+)?|\
0[xX][[:xdigit:]]*\.?[[:xdigit:]]*([pP][-+]?[[:digit:]]+)?)$/
Run Code Online (Sandbox Code Playgroud)
... 其目的是允许可能的前导 + 或 -,然后是浮点数或十六进制数。浮点数具有可选的前导数字、选项分隔符(此处固定为句点.
),后跟一定数量的数字,可选后跟指数。十六进制数字必须以0x
或开头0X
,后跟十六进制数字、分隔符、更多十六进制数字,并可选择后跟“幂”(指数)。整个第二个字段必须匹配这些格式之一(由^
和锚定$
)。出于这个问题的目的,这里省略了 NAN 和 INFINITY 选项。
另一种选择是强制进行数字转换,然后将其与零进行比较,然后进一步将原始输入与将转换为零的内容进行比较;更具体地说,它是否以可选的 + 或 - 开头,然后是后跟零,还是后跟一个句点和零:
{ number=0 + $2;
if (!number && $2 !~ /^[+-]?(0+)|\.0+/)
print "NAN: "$2;
}
Run Code Online (Sandbox Code Playgroud)
我结束了这个:
awk -v col=$col '
typeof($col) != "strnum" {
print "Error on line " NR ": " $col " is not numeric"
noprint=1
exit 1
}
{
sum+=$col
}
END {
if(!noprint)
print sum
}' $file
Run Code Online (Sandbox Code Playgroud)
这使用了 typeof,它是一个 GNU awk 扩展。typeof($col)
如果$col
是有效数字,则返回 'strnum',如果不是,则返回 ' string' 或 'unassigned'。