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)不是字符串。调试我尝试了这个小代码(它没有婴儿床,但似乎也没有按预期工作——&gt; str = string.char(0x00, 0xff)&gt; local u16 = struct.unpack('&lt;I2', str)&gt; 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字节流,字节序变化的主要内容,如果未能解决你的问题,请参考以下文章

java里字符流 字节流和序列化的区别?

字节流和字符流

Java自学-I/O 字节流

字节流 字符流 输入流 输出流

字节流 字符流 输入流 输出流

字符流和字节流的区别