2

我正在尝试获取通过 RS232 接收到的 ASCII 字符的值,以将它们转换为类似二进制的值。

例子:

0xFF-->########
0x01-->       #
0x02-->      #
...

我的问题是让 ASCII 字符的值高于 127。

获取 int 值的测试代码:
echo -e "\xFF" | gawk -l ordchr -e '{printf("%c : %i", ord($0),ord($0))}'
返回:
� : -1

测试代码 2:
echo -e "\x61" | gawk -l ordchr -e '{printf("%c : %i", ord($0),ord($0))}'
返回:
a : 97

所以我将值转换为无符号整数的解决方案是这样的:

if(ord($0)<0)
{
    new_char=ord($0)+256;
}
else new_char = ord($0)+0`

但我想知道是否有办法直接在 gawk 中int投射uint

后来我尝试编写自己的ord()函数。

#!/bin/bash

echo -e "\xFF" | awk 'BEGIN {_ord_init()}
{
    printf("%s : %d\n", $0, ord($0))
}

function _ord_init(    i, t)
{
    for (i=0; i <= 255; i++) {
         t = sprintf("%c", i)
        _ord_[t] = i
    }
}

function ord(str,    c)
{
    # only first character is of interest
    c = substr(str, 1, 1)
    return _ord_[c]
}'

0xFF返回:

� : 0

0x61返回:

a : 97

有人可以解释我的行为吗?

我在用着:
GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4-p1, GNU MP 6.1.1)

4

3 回答 3

1

但我想知道是否有办法在 gawk 中直接将 int 转换为 uint。

实际上,awk 中的任何字符串最终都是一个数字。

如果 awk 程序的上下文需要,则字符串会转换为数字,而数字会转换为字符串。[...] 通过将字符串的任何数字前缀解释为数字,将字符串转换为数字:“2.5”转换为 2.5,“1e3”转换为 1,000,“25fix”的数值为 25。不能解释为有效数字转换为零。资源

让我们做一个快速测试:

BEGIN { 
   print 0xff
   print 0xff + 0
   print 0xff +0.0
   print "0xff"
}

# 255
# 255
# 255
# 0xff

因此, anyhex会自动解释为uint. 将 aint转换uint为一个棘手的问题:通常,您应该将模数转换int为十六进制,然后将符号位添加为 MSB(即,如果数字为非正数)。但是您不需要在 awk 中这样做。

请记住,转换是作为调用进行的sprintf(),您可以通过CONVFMT变量控制它:

CONVFMT

控制数字到字符串的转换的字符串(请参阅字符串和数字的转换部分)。实际上,它是作为 sprintf() 函数的第一个参数传递的(参见字符串操作函数部分)。它的默认值为“%.6g”。CONVFMT 是由 POSIX 标准引入的。资源

请记住,区域设置可能会影响执行转换的方式,尤其是小数分隔符。有关更多信息,请参阅超出范围的此内容


有人可以解释我的行为吗?

我实际上无法重现它,但我怀疑这行代码:

# only first character is of interest
c = substr(str, 1, 1)

在您的示例中,第一个字符始终是0并且输出应该始终相同。我在网上测试这个。

我再举一个我的例子:

BEGIN {
    a = 0xFF
    b = 0x61
    printf("a: %d %f %X %s %c\n", a,a,a,a,a)
    printf("b: %d %f %X %s %c\n", b,b,b,b,b)
}

# a: 255 255.000000 FF 255 ÿ
# b: 97 97.000000 61 97 a
于 2020-12-21T14:42:54.153 回答
0

我自己也遇到了同样的问题。我首先使用了一个检测器,无论它是在 unicode 模式还是字节模式下运行 gawk(检查组成一个 UTF8 代码点的 3 个八进制值组合的长度()返回 1 或 3)

然后当它看到 gawk unicode 模式时,从 gawk 运行自定义 shell 命令并使用 unix printf 打印出字节 128-255,并将其分块回 gawk 到一个数组中。如果您需要它,我可以在某个时候粘贴代码(但它非常可怕,所以我希望我不会因为它缺乏优雅而受到抨击)

因为在 UTF8 中不存在像 C0、C1 或 FF 等简单的字节,所以无论您尝试哪种组合,您都无法在 gawk 中生成所有 256 个字节。我的意思是另一种方法是预先制作该链并使用 xxd -ps 将其存储为哈希字符串,仅在运行时将其转换回来,但它确实更慢。

于 2021-02-12T00:52:46.030 回答
0

以二进制模式运行 gawkgawk -b以阻止它预先拼接 UTF8 代码点。将其拆分为 // 空字符串,然后生成的数组中的每个点都将包含 1 字节宽的内容。

相反,只需预先制作一个从 0 到 256 的数组。Gawk 根本不会停在那里。0x3134F在我的例行 gawk 启动序列中,我从一直到零(大约210k左右)执行相同的自定义 ord 序列。向后做的原因是,无论出于何种原因,有些代码点会出现 gawk 无法区分的相同字符。反向执行将确保为其分配最低的#代码点。对于这种模式,我以常规 utf8 运行它。

对于您的场景,我将预先制作 4-hex 宽数组,从0x0000to 0xFFFF,回到它们的整数数组,然后对于 each 0xZZ 0xWWZZWW放入该查找字典并返回整数。

如果您只是尝试ord( )从 128 到 255,它通常不会那样工作,因为 128 是 unicode 开始 2 个字节的位置。0x800开始 3 个字节,0x10000开始 4 个字节。我不太熟悉将 ascii 扩展到 256 的那些——它们通常需要使用iconv或类似的方法才能首先返回 UTF-8。

快速说明,如果您想获取原始 UTF8 字节并试图找出有多少缝合的 UTF8 代码点,只需删除所有内容0x80 - 0xBF。残差的length()是代码点的数量。

在十进制术语中,从 0 到 255 的 64 个数字的 4 个范围中:

  • 000 - 063- ASCII

  • 064 - 127- ASCII

  • 128 - 191- UT8-多字节连续编码 (the 0x80 0xBF)

  • 192 - 255- UTF8 多字节字符的最高有效字节

这看起来很可怕。幸运的是,八进制的救援。范围0x80 - 0xBF只是. \200-\277您可以使用任何 AWK 的正则表达式来查找它们(也适用于 FS / RS 等)。在进行所有位移之前,我花时间手动编写 utf8 算法,后来我意识到我不需要它来达到我的最终目标。

如果您想在wc -m将上述逻辑与mawk2. 在我使用 2.5 年的笔记本电脑上,针对一个 1.83 GB 的纯文本文件,上面全是 unicode,我只用awk.

于 2020-12-21T16:44:31.253 回答