我有 awk 的 gawk 版本。在gawk手册的这一部分中,说明了awk变量具有“属性”,用于确定在各种操作中如何对待它们。
例如," +3.14"通过解析输入获得的形式的字符串具有STRNUM属性,这使得它在与数字进行比较时表现为数字,而在 awk 程序中定义的相同字符串没有该属性。
OTOH,像这样的字符串"3.14"显然具有STRNUM属性,即使它是在程序中定义的,因为代码x = "3.14" { print x == 3.14 }打印 1。而如果我们将其定义为"+3.13"or " 3.14",它没有STRNUM属性,因为x = "+3.14" { print x == 3.14 }orx = " 3.14" { print x == 3.14 }打印 0。
我认为变量类型的这种简洁性可能会导致细微的错误。因此,为了帮助调试这种情况,有没有办法了解变量具有什么类型的“属性”?即,我们可以了解变量的类型吗?
Awk 有4 种类型:“数字”、“字符串”、“数字字符串”和“未定义”。这里有一个函数来检测:
function o_class(obj, q, x, z) {
q = CONVFMT
CONVFMT = "% g"
split(" " obj "\1" obj, x, "\1")
x[1] = obj == x[1]
x[2] = obj == x[2]
x[3] = obj == 0
x[4] = obj "" == +obj
CONVFMT = q
z["0001"] = z["1101"] = z["1111"] = "number"
z["0100"] = z["0101"] = z["0111"] = "string"
z["1100"] = z["1110"] = "strnum"
z["0110"] = "undefined"
return z[x[1] x[2] x[3] x[4]]
}
Run Code Online (Sandbox Code Playgroud)
对于 的第三个参数split,您需要一些不是空格的东西,而不是它的一部分,obj否则它将被视为分隔符。我选择了\1
基于Stéphane 的建议。该函数进行内部CONVFMT
切换,因此无论CONVFMT函数调用时的值如何,它都应该返回正确的结果:
split("12345.6", q); print 1, o_class(q[1])
CONVFMT = "%.5g"; split("12345.6", q); print 2, o_class(q[1])
split("nan", q); print 3, o_class(q[1])
CONVFMT = "%.6G"; split("nan", q); print 4, o_class(q[1])
Run Code Online (Sandbox Code Playgroud)
结果:
function o_class(obj, q, x, z) {
q = CONVFMT
CONVFMT = "% g"
split(" " obj "\1" obj, x, "\1")
x[1] = obj == x[1]
x[2] = obj == x[2]
x[3] = obj == 0
x[4] = obj "" == +obj
CONVFMT = q
z["0001"] = z["1101"] = z["1111"] = "number"
z["0100"] = z["0101"] = z["0111"] = "string"
z["1100"] = z["1110"] = "strnum"
z["0110"] = "undefined"
return z[x[1] x[2] x[3] x[4]]
}
Run Code Online (Sandbox Code Playgroud)
完整的测试套件:
print 1, o_class(0)
print 2, o_class(1)
print 3, o_class(123456.7)
print 4, o_class(1234567.8)
print 5, o_class(+"inf")
print 6, o_class(+"nan")
print 7, o_class("")
print 8, o_class("0")
print 9, o_class("1")
print 10, o_class("inf")
print 11, o_class("nan")
split("00", q); print 12, o_class(q[1])
split("01", q); print 13, o_class(q[1])
split("nan", q); print 14, o_class(q[1])
split("12345.6", q); print 15, o_class(q[1])
print 16, o_class()
Run Code Online (Sandbox Code Playgroud)
结果:
split("12345.6", q); print 1, o_class(q[1])
CONVFMT = "%.5g"; split("12345.6", q); print 2, o_class(q[1])
split("nan", q); print 3, o_class(q[1])
CONVFMT = "%.6G"; split("nan", q); print 4, o_class(q[1])
Run Code Online (Sandbox Code Playgroud)
值得注意的弱点是:如果您提供以下任何“数字字符串”,该函数将错误地返回“数字”:
inf-inf对于整数,解释如下:
与整数值完全相等的数值应通过等效调用以
sprintf字符串%d作为fmt参数的函数来转换为字符串
但是inf,也-inf以这种方式行事;也就是说,以上任何一项都不会受到CONVFMT变量的影响:
CONVFMT = "% g"
print "" .1
print "" (+"nan")
print "" 1
print "" (+"inf")
print "" (+"-inf")
Run Code Online (Sandbox Code Playgroud)
结果:
0.1
nan
1
inf
-inf
Run Code Online (Sandbox Code Playgroud)
在实践中这并不重要,请参阅Duck 测试。
从 GNU Awk 4.2 开始,有一个新函数typeof()可以检查这一点,如测试版本的发行说明中所示:
- 新的 typeof() 函数可用于指示变量或数组元素是否为数组、正则表达式、字符串或数字。isarray() 函数已被弃用,取而代之的是 typeof()。
所以现在你可以说:
$ awk 'BEGIN {print typeof("a")}'
string
$ awk 'BEGIN {print typeof(1)}'
number
$ awk 'BEGIN {print typeof(a[1])}'
unassigned
$ awk 'BEGIN {a[1]=1; print typeof(a)}'
array
$ echo ' 1 ' | awk '{print typeof($0)}'
strnum
Run Code Online (Sandbox Code Playgroud)