为什么 gawk 在输入数据时将“0123”视为十进制数?

为什么 gawk 在输入数据时将“0123”视为十进制数?

根据$ man gawk,该strtonum()函数可以将字符串转换为数字:

strtonum(str) 检查 str 并返回其数值。如果 str 以前导 0 开头,则将其视为八进制数。如果 str 以前导 0x 或 0X 开头,则将其视为十六进制数。否则,假设它是十进制数。

如果字符串以 开头0,则该数字被视为八进制,而如果它以 开头,0x则被视为十六进制。

我运行了这些命令来检查我对该函数的理解:

$ awk 'END { print strtonum("0123") }' <<<''
83

$ awk 'END { print strtonum("0x123") }' <<<''
291

该字符串"0123"被正确地视为包含八进制数并转换为十进制数83。同样,字符串"0x123"被正确地视为包含十六进制数字并转换为十进制数字291

现在,如果我运行相同的命令,但将数字字符串从程序文本移动到输入数据,会发生以下情况:

$ awk 'END { print strtonum($1) }' <<<'0123'
123

$ awk 'END { print strtonum($1) }' <<<'0x123'
291

我理解第二个结果与前面的命令相同,但我不理解第一个结果。为什么 gawk 现在将其0123视为十进制数,即使它以0表征八进制数的前导开头?

我怀疑这与字符串属性,因为出于某种原因1,gawk 将此属性赋予0123但不赋予0x123

$ awk 'END { print typeof($1) }' <<<'0123'
strnum

$ awk 'END { print typeof($1) }' <<<'0x123'
string

1可能是由于变化awk 实现之间:

为了澄清,只有来自几个来源的字符串(这里引用 POSIX 规范): [...] 如果它们的值恰好是数字(允许前导和尾随空格, 支持十六进制、八进制的实现之间存在差异,inf,南...)。


我正在使用 gawk version 4.2.62,其输出$ awk -V是:

GNU Awk 4.2.62, API: 2.0 (GNU MPFR 3.1.4, GNU MP 6.1.0)

答案1

strnum这与GAWK 4.2 版本中的通用处理有关。

输入值看起来像数字被视为strnum值,在内部表示为同时具有字符串和数字类型。 “0123”看起来像一个数字,因此它被作为strnum.strtonum旨在处理字符串和数字输入;它首先寻找数字,当遇到输入数字时,返回该数字而不进行转换:

NODE *
do_strtonum(int nargs)
{
        NODE *tmp;
        AWKNUM d;

        tmp = fixtype(POP_SCALAR());
        if ((tmp->flags & NUMBER) != 0)
                d = (AWKNUM) tmp->numbr;
        else if (get_numbase(tmp->stptr, tmp->stlen, use_lc_numeric) != 10)
                d = nondec2awknum(tmp->stptr, tmp->stlen, NULL);
        else
                d = (AWKNUM) force_number(tmp)->numbr;

        DEREF(tmp);
        return make_number((AWKNUM) d);
}

这样“0123”就变成了数字123,直接strtonum返回。

“0x123”看起来不像一个数字(根据上面给出的链接中定义的规则),因此它被作为字符串处理,并按照您期望的方式进行处理strtonum

数字定义如下在 AWK 中:

输入字符串被分解为两部分:一个初始的、可能为空的空白字符序列(由空间()) 和解释为浮点常量的主题序列。

主题序列的预期形式是可选的'+''-'符号,然后是可选地包含 <period> 的非空数字序列,然后是可选的指数部分。指数部分由'e'或组成'E',后跟一个可选符号,最后跟一个或多个小数位。

以第一个数字或 <period>(以先出现者为准)开始的序列被解释为 C 语言的浮动常量,如果指数部分和 <period> 均未出现,则假定 a 位于最后一位数字之后字符串。如果主题序列以 <hyphen-minus> 开头,则转换产生的值将被否定。

相关内容