新手问题 请教懂 Python 和 Ruby 高手,关于整数转换成网络上可以传输的 byte 流的(已解决)

haoshanshan · 2015年03月10日 · 最后由 luikore 回复于 2015年03月17日 · 3997 次阅读

python 对于整数有个处理方法 to_bytes,转换后可以进行网络的 socket 传输 int.to_bytes(length, byteorder, *, signed=False) Return an array of bytes representing an integer.

请问 ruby 有对应的方法吗? 在网上查了一下,array 有 pack 方法,但是试了所有的方法都达不到 python 的 to_bytes 方法的效果

@haoshanshan

哈,当年写的代码终于有用了:

https://github.com/windy/DIY-pcap/blob/master/lib/diy/parser/mu/pcap/ethernet.rb#L93

这是一段解析以太网 2 层报文的代码,估计比你的需求满足:

ADDR_TO_BYTES = {}
FMT_HEADER = 'a6a6n'
def write io
    dst_mac = ADDR_TO_BYTES[@dst] ||= @dst.split(':').inject('') {|m, b| m << b.to_i(16).chr}
    src_mac = ADDR_TO_BYTES[@src] ||= @src.split(':').inject('') {|m, b| m << b.to_i(16).chr}
    bytes = [dst_mac, src_mac, @type].pack(FMT_HEADER)
    io.write bytes
    if @payload.is_a? String
        io.write @payload
    else
        @payload.write io
    end
end

12.to_bytes(1,byteorder='big') 结果是 b'\x0c'

不是解析,是把整数转成 byte 流

你就给转成一个字节,还考虑什么字节序啊

没有字节序,发送到服务端不认识吧

@haoshanshan 例子都给你了,自己不会看吗?这段代码把数据转为二进制网络序,下一步就是发送。

具体的看 pack : http://ruby-doc.org/core-2.2.0/Array.html#method-i-pack

[123123].pack 'q'

固定长度的整数 (1, 2, 4, 8 字节) 用 pack 可以。

适合网络传输的任意长整数,用 der 格式最适合了,我推荐这个:

[70244200243420].pack 'w' #=> "\x8F\xFC\xB0\x84\xE9\xC1\\"
"\x8F\xFC\xB0\x84\xE9\xC1\\".unpack 'w' #=> [70244200243420]

如果是要生翻译 python 的 to_bytes, 就没内建的方法,得自己写了,只支持 unsigned 的例子如下:

class Integer
  def to_bytes size, byteorder: 'big'
    i = self
    rs = size.times.map do
      i, r = i.divmod 256
      r
    end
    rs.reverse! if byteorder == 'big'
    rs.pack 'C*'
  end
end

基本上搞定了。多谢 luikore irb(main):009:0> ["\x02"].pack("a2").reverse => "\x00\x02" 查看了一下需要实现的需求,就是只需要转换二进制后用 1,2 或 4 字节表示

之前一直纠结 12 转换后成/f,与/x0c 不一致,结果/f 与/x0c 是相等的。多谢 china ruby 的大牛们,终于搞定

使用 ["\x02"].pack("a2").reverse 处理 10004000,处理的不对,最后还是使用了@luikore的方法,在 integer 中加了这个方法,在 irb 中可以直接 12.to_bytes 1 方法,但是在文件中老是提示 fixnum 没有定义,找 fixnum 的定义文件,加了后还是不行,就直接改了一下 def to_bytes num,size
rs = size.times.map do num, r = num.divmod 256 r end rs.reverse.pack 'C*' end 问题最终得到解决,真是十二分感谢@luikore 如果来成都,找我(qq:30711905)

#9 楼 @haoshanshan 呃,改现有 class 例如 Fixnum, Integer 不用找那个文件的,直接在你的代码中写就行 (虽然大一点工程不推荐,但小脚本随便用没关系).

另外你早点说 big endian, 只用搞 1, 2, 4 字节就好了嘛,很简单:

[num].pack 'C'   # 1 字节
[num].pack 'S>' # 2 字节
[num].pack 'L>' # 4 字节

这些参数怎么来的?命令行 ri pack 可以看到

需要 登录 后方可回复, 如果你还没有账号请 注册新账号