【问题标题】:Lua, dealing with non-ascii byte streams, byteorder changeLua,处理非ascii字节流,字节序变化
【发布时间】: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

如果有人能举例说明就好了。

【问题讨论】:

    标签: encoding lua bytearray endianness decoding


    【解决方案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】:

    看看structlpack 库。

    在本例中,我使用 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
    

    【讨论】:

      猜你喜欢
      • 2011-06-03
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      • 2011-09-27
      • 2014-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多