使用 jq 将数字字节值数组转换为字符串
Convert an array of numeric byte values into a string with jq
我将 UTF-8 字符串的二进制表示形式作为数值数组,每个值都在 0..255 范围内。
如何使用jq
将该数组转换为字符串?内置 implode
仅处理代码点数组。此外,jq
.
中没有按位运算的函数
此类数组在 newman (Postman CLI) json 输出中被观察为值,属性 response.stream.data
.
例如,字符串 "Hi, Мир!" 进入 [72,105,44,32,208,156,208,184,209,128,33]
字节数组,而它的代码点是 [72,105,44,32,1052,1080,1088,33]
。后者的 implode
给出原始字符串,而前者的 implode
给出 "Hi,ÐиÑ!" 或类似的东西。
def btostring:
if length == 0 then ""
elif .[0] >= 240 then
([((((.[0] - 240) * 64) + (.[1] - 128)) * 64 + (.[2] - 128)) * 64 + (.[3] - 128)]
| implode) + (.[4:] | btostring)
elif .[0] >= 224 then
([ ((.[0] - 224) * 64 + (.[1] - 128)) * 64 + (.[2] - 128)]
| implode) + (.[3:] | btostring)
elif .[0] >= 128 then
([ (.[0] - 192) * 64 + (.[1] - 128) ]
| implode) + (.[2:] | btostring)
else (.[0:1] | implode ) + (.[1:] | btostring)
end;
示例:
def hi: [72,105,44,32,208,156,208,184,209,128,33] ;
hi | btostring
输出(使用jq -r
):
Hi, Мир!
扩展示例:
def hi: [72,105,44,32,208,156,208,184,209,128,33];
def euro: [226,130,172]; # 11100010 10000010 10101100
def fire: [240,159,156,130]; # 11110000 10011111 10011100 10000010
(hi, euro, fire) | btostring
输出:
Hi, Мир!
€
(在某些设备上,上面的最后一行将是一个框而不是三角形。)
另一种使用 foreach
的方法。主要思想是保持当前 char 的剩余字节数 (.[0]
) 和到目前为止读取的位 (.[1]
)。这是过滤器:
[foreach .[] as $item (
[0, 0]
;
if .[0] > 0 then [.[0] - 1, .[1] * 64 + ($item % 64)]
elif $item >= 240 then [3, $item % 8]
elif $item >= 224 then [2, $item % 16]
elif $item >= 192 then [1, $item % 32]
elif $item < 128 then [0, $item]
else error("Malformed UTF-8 bytes")
end
;
if .[0] == 0 then .[1] else empty end
)] | implode
此外,对格式错误字节的错误检测尚未完成。
这里是本页其他地方给出的递归btostring
的非递归版本,主要是为了说明在 jq 中如何将递归实现转化为非递归实现。
def btostring:
. as $in
| [ foreach range(0;length) as $ix ({skip:0, point:[]};
if .skip > 0 then .skip += -1
elif $in[$ix] >= 240 then
.point = [(((($in[$ix] - 240) * 64)
+ ($in[$ix+1] - 128)) * 64
+ ($in[$ix+2] - 128)) * 64
+ ($in[$ix+3] - 128)]
| .skip = 3
elif $in[$ix] >= 224 then
.point = [ (($in[$ix] - 224) * 64
+ ($in[$ix+1] - 128)) * 64
+ ($in[$ix+2] - 128)]
| .skip = 2
elif $in[$ix] >= 128 then
.point = [ ($in[$ix] - 192) * 64
+ ($in[$ix+1] - 128)]
| .skip = 1
else .point = $in[$ix:$ix+1]
end;
if .skip == 0 then .point|implode else empty end) ]
| add ;
基于 that I used successully, here's an extension that does not break or fail with invalid UTF-8 encoding.
无效的 UTF-8 起始字节(128-193、245-255)或序列被解释为 ISO 8859-1。
def btostring:
if type != "array" then .
elif length == 0 then ""
elif .[0] >= 245 then
(.[0:1] | implode ) + (.[1:] | btostring)
elif .[0] >= 240 then
if length >= 4 and .[1] >= 128 and .[2] >= 128 and .[3] >= 128 then
([((((.[0] - 240) * 64) + (.[1] - 128)) * 64 + (.[2] - 128)) * 64 + (.[3] - 128)] | implode) + (.[4:] | btostring)
else
(.[0:1] | implode ) + (.[1:] | btostring)
end
elif .[0] >= 224 then
if length >= 3 and .[1] >= 128 and .[2] >= 128 then
([ ((.[0] - 224) * 64 + (.[1] - 128)) * 64 + (.[2] - 128)] | implode) + (.[3:] | btostring)
else
(.[0:1] | implode ) + (.[1:] | btostring)
end
elif .[0] >= 194 then
if length >= 2 and .[1] >= 128 then
([ (.[0] - 192) * 64 + (.[1] - 128) ] | implode) + (.[2:] | btostring)
else
(.[0:1] | implode ) + (.[1:] | btostring)
end
else
(.[0:1] | implode ) + (.[1:] | btostring)
end;
我将 UTF-8 字符串的二进制表示形式作为数值数组,每个值都在 0..255 范围内。
如何使用jq
将该数组转换为字符串?内置 implode
仅处理代码点数组。此外,jq
.
此类数组在 newman (Postman CLI) json 输出中被观察为值,属性 response.stream.data
.
例如,字符串 "Hi, Мир!" 进入 [72,105,44,32,208,156,208,184,209,128,33]
字节数组,而它的代码点是 [72,105,44,32,1052,1080,1088,33]
。后者的 implode
给出原始字符串,而前者的 implode
给出 "Hi,ÐиÑ!" 或类似的东西。
def btostring:
if length == 0 then ""
elif .[0] >= 240 then
([((((.[0] - 240) * 64) + (.[1] - 128)) * 64 + (.[2] - 128)) * 64 + (.[3] - 128)]
| implode) + (.[4:] | btostring)
elif .[0] >= 224 then
([ ((.[0] - 224) * 64 + (.[1] - 128)) * 64 + (.[2] - 128)]
| implode) + (.[3:] | btostring)
elif .[0] >= 128 then
([ (.[0] - 192) * 64 + (.[1] - 128) ]
| implode) + (.[2:] | btostring)
else (.[0:1] | implode ) + (.[1:] | btostring)
end;
示例:
def hi: [72,105,44,32,208,156,208,184,209,128,33] ;
hi | btostring
输出(使用jq -r
):
Hi, Мир!
扩展示例:
def hi: [72,105,44,32,208,156,208,184,209,128,33];
def euro: [226,130,172]; # 11100010 10000010 10101100
def fire: [240,159,156,130]; # 11110000 10011111 10011100 10000010
(hi, euro, fire) | btostring
输出:
Hi, Мир!
€
(在某些设备上,上面的最后一行将是一个框而不是三角形。)
另一种使用 foreach
的方法。主要思想是保持当前 char 的剩余字节数 (.[0]
) 和到目前为止读取的位 (.[1]
)。这是过滤器:
[foreach .[] as $item (
[0, 0]
;
if .[0] > 0 then [.[0] - 1, .[1] * 64 + ($item % 64)]
elif $item >= 240 then [3, $item % 8]
elif $item >= 224 then [2, $item % 16]
elif $item >= 192 then [1, $item % 32]
elif $item < 128 then [0, $item]
else error("Malformed UTF-8 bytes")
end
;
if .[0] == 0 then .[1] else empty end
)] | implode
此外,对格式错误字节的错误检测尚未完成。
这里是本页其他地方给出的递归btostring
的非递归版本,主要是为了说明在 jq 中如何将递归实现转化为非递归实现。
def btostring:
. as $in
| [ foreach range(0;length) as $ix ({skip:0, point:[]};
if .skip > 0 then .skip += -1
elif $in[$ix] >= 240 then
.point = [(((($in[$ix] - 240) * 64)
+ ($in[$ix+1] - 128)) * 64
+ ($in[$ix+2] - 128)) * 64
+ ($in[$ix+3] - 128)]
| .skip = 3
elif $in[$ix] >= 224 then
.point = [ (($in[$ix] - 224) * 64
+ ($in[$ix+1] - 128)) * 64
+ ($in[$ix+2] - 128)]
| .skip = 2
elif $in[$ix] >= 128 then
.point = [ ($in[$ix] - 192) * 64
+ ($in[$ix+1] - 128)]
| .skip = 1
else .point = $in[$ix:$ix+1]
end;
if .skip == 0 then .point|implode else empty end) ]
| add ;
基于
无效的 UTF-8 起始字节(128-193、245-255)或序列被解释为 ISO 8859-1。
def btostring:
if type != "array" then .
elif length == 0 then ""
elif .[0] >= 245 then
(.[0:1] | implode ) + (.[1:] | btostring)
elif .[0] >= 240 then
if length >= 4 and .[1] >= 128 and .[2] >= 128 and .[3] >= 128 then
([((((.[0] - 240) * 64) + (.[1] - 128)) * 64 + (.[2] - 128)) * 64 + (.[3] - 128)] | implode) + (.[4:] | btostring)
else
(.[0:1] | implode ) + (.[1:] | btostring)
end
elif .[0] >= 224 then
if length >= 3 and .[1] >= 128 and .[2] >= 128 then
([ ((.[0] - 224) * 64 + (.[1] - 128)) * 64 + (.[2] - 128)] | implode) + (.[3:] | btostring)
else
(.[0:1] | implode ) + (.[1:] | btostring)
end
elif .[0] >= 194 then
if length >= 2 and .[1] >= 128 then
([ (.[0] - 192) * 64 + (.[1] - 128) ] | implode) + (.[2:] | btostring)
else
(.[0:1] | implode ) + (.[1:] | btostring)
end
else
(.[0:1] | implode ) + (.[1:] | btostring)
end;