Lua,处理非ascii字节流,字节序变化
Posted
技术标签:
【中文标题】Lua,处理非ascii字节流,字节序变化【英文标题】:Lua, dealing with non-ascii byte streams, byteorder change 【发布时间】:2011-07-11 15:19:43 【问题描述】:需要对字节流(可能包含非 ascii 字符)进行编码和解码,从/到 uint16、uint32、uint64(它们的典型 C/C++ 含义),注意字节顺序。在 Lua 中做这种事情的一种高效且有希望的跨平台方式是什么?
我的目标架构是 64 位 x86_64,但希望保持可移植性(如果它不会在性能方面花费我的话)。
例如
decode(比如当前在 Lua 字符串中)-- 0x00、0x1d、0xff、0x23、0x44、0x32(小端) 作为 - uint16: (0x1d00) = 7424 uint32: (0x324423ff) = 843326463
如果有人能举例说明就好了。
【问题讨论】:
【参考方案1】:用于从字节转换为整数(注意字节级别的字节序和符号):
function bytes_to_int(str,endian,signed) -- use length of string to determine 8,16,32,64 bits
local t=str:byte(1,-1)
if endian=="big" then --reverse bytes
local tt=
for k=1,#t do
tt[#t-k+1]=t[k]
end
t=tt
end
local n=0
for k=1,#t do
n=n+t[k]*2^((k-1)*8)
end
if signed then
n = (n > 2^(#t*8-1) -1) and (n - 2^(#t*8)) or n -- if last bit set, negative.
end
return n
end
虽然我们也在另一个方向:
function int_to_bytes(num,endian,signed)
if num<0 and not signed then num=-num print"warning, dropping sign from number converting to unsigned" end
local res=
local n = math.ceil(select(2,math.frexp(num))/8) -- number of bytes to be used.
if signed and num < 0 then
num = num + 2^n
end
for k=n,1,-1 do -- 256 = 2^8 bits per char.
local mul=2^(8*(k-1))
res[k]=math.floor(num/mul)
num=num-res[k]*mul
end
assert(num==0)
if endian == "big" then
local t=
for k=1,n do
t[k]=res[n-k+1]
end
res=t
end
return string.char(unpack(res))
end
欢迎任何评论,它已经过测试,但不是太彻底......
【讨论】:
很有指导意义。通过您的说明性示例,我似乎学到了很多 Lua,认真的。 在函数 bytes_to_int 中,行 n = (n > 2^(#t-1) -1) and (n - 2^#t) or n 我认为应该是#t*8相反或#t。是你想要的位数,而不是字节数。 看来你是对的!感谢您的建议,我已经在我的回答中更正了它。 @jpjacobs - 非常感谢。函数 int_to_bytes 在处理有符号数时也需要修正:if signed and num ,下一行 2^n 应该是 num = num + 2^( 8 * n) int_to_bytes 并不总是正确处理有符号数(当 frexp 中的 exp 是 8 的倍数时)。例如,-32769 应该是 0xFF 0xFF 0x80 0x00,但函数本身返回 0x7F 0xFF。【参考方案2】:看看struct 和lpack 库。
在本例中,我使用 struct.unpack
将 Lua 字符串解码为两个整数,并采用强制小端编码:
require 'struct'
-- convert character codes to a Lua string - this may come from your source
local str = string.char(0x00, 0x1d, 0xff, 0x23, 0x44, 0x32)
-- format string: < = little endian, In = unsigned int (n bytes)
local u16, u32 = struct.unpack('<I2I4', str)
print(u16, u32) --> 7424 843326463
【讨论】:
看起来非常简单优雅,但是我似乎没有'struct'扩展模块。 luarocks 无法构建/安装它并出现错误。将尝试解决这个问题并尝试这个。谢谢! @michal-kottman,在修复 luarocks 并安装 'struct' 后尝试了代码,但对于 lua 婴儿床来说,解压的第二个参数(即 str)不是字符串。调试我尝试了这个小代码(它没有婴儿床,但似乎也没有按预期工作——> str = string.char(0x00, 0xff)
> local u16 = struct.unpack('<I2', str)
> print(u16)
nil
@michal-kottman,对不起!修复。 Lua 5.1 需要稍作改动(至少在我的系统上)。只需要做一个:struct = require("struct")
奇怪,我测试了 Lua 5.1 中的代码,它工作正常(也许我有一个旧版本的struct
),但我很高兴它现在对你有用,它是一个非常方便的二进制数据工具...【参考方案3】:
我对不检查参数的“Int16ToByte”函数的建议:
function Int16ToBytes(num, endian)
if num < 0 then
num = num & 0xFFFF
end
highByte = (num & 0xFF00) >> 8
lowByte = num & 0xFF
if endian == "little" then
lowByte, highByte = highByte, lowByte
end
return string.char(highByte,lowByte)
end
【讨论】:
以上是关于Lua,处理非ascii字节流,字节序变化的主要内容,如果未能解决你的问题,请参考以下文章