Ruby';s包装和解包说明

Ruby';s包装和解包说明,ruby,pack,Ruby,Pack,即使在阅读了标准文档之后,我仍然无法理解Ruby是如何工作的。下面是给我带来最大麻烦的一个例子: irb(main):001:0> chars = ["61","62","63"] => ["61", "62", "63"] irb(main):002:0> chars.pack("H*") => "a" irb(main):003:0> chars.pack("HHH") => "```" 我希望这两个操作返回相同的输出:“abc”。它们中的每一个都以不同

即使在阅读了标准文档之后,我仍然无法理解Ruby是如何工作的。下面是给我带来最大麻烦的一个例子:

irb(main):001:0> chars = ["61","62","63"]
=> ["61", "62", "63"]
irb(main):002:0> chars.pack("H*")
=> "a"
irb(main):003:0> chars.pack("HHH")
=> "```"
我希望这两个操作返回相同的输出:“abc”。它们中的每一个都以不同的方式“失败”(并非真正的失败,因为我可能预期的是错误的事情)。所以有两个问题:

  • 这些输出背后的逻辑是什么
  • 如何实现我想要的效果,即将十六进制数字序列转换为相应的字符串。更妙的是,给定一个整数n,如何将其转换为一个与文本文件相同的字符串,当它被视为一个数字(例如,在十六进制编辑器中)等于n时
  • Array#pack
    方法非常神秘。在回答问题(2)时,我能够通过以下方式让您的示例发挥作用:

    > ["61", "62", "63"].pack("H2H2H2")
    => "abc" 
    
    有关类似示例,请参见。以下是一种更通用的方法:

    ["61", "62", "63"].map {|s| [s].pack("H2") }.join
    
    这可能不是解决问题的最有效方法;我怀疑有更好的方法,但了解你开始时的投入会有所帮助


    #pack
    方法对于其他语言(如Perl)是通用的。如果Ruby的文档没有帮助,您可以在其他地方查阅类似的文档。

    今天早上我们正在处理一个类似的问题。如果阵列大小未知,则可以使用:

    ary = ["61", "62", "63"]
    ary.pack('H2' * ary.size)
    => "abc"
    
    您可以使用以下方法将其反转:

    str = "abc"
    str.unpack('H2' * str.size)
    => ["61", "62", "63"]
    
    我希望这两个操作返回相同的输出:“abc”

    要理解你的方法为什么不起作用,最简单的方法就是从你期望的开始:

    "abc".unpack("H*")
    # => ["616263"]
    
    ["616263"].pack("H*")
    # => "abc"
    
    因此,Ruby似乎希望将十六进制字节放在一个长字符串中,而不是数组中的单独元素中。因此,你最初的问题最简单的答案是:

    chars = ["61", "62", "63"]
    [chars.join].pack("H*")
    # => "abc"
    
    对于较大的输入,这种方法似乎也表现得相当好:

    require 'benchmark'
    
    chars = ["61", "62", "63"] * 100000
    
    Benchmark.bmbm do |bm|
      bm.report("join pack") do [chars.join].pack("H*") end
      bm.report("big pack") do chars.pack("H2" * chars.size) end
      bm.report("map pack") do chars.map{ |s| [s].pack("H2") }.join end
    end
    
    #                 user     system      total        real
    # join pack   0.030000   0.000000   0.030000 (  0.025558)
    # big pack    0.030000   0.000000   0.030000 (  0.027773)
    # map pack    0.230000   0.010000   0.240000 (  0.241117)
    

    用于
    Array#pack
    'H'
    字符串指令指出,数组内容应解释为十六进制字符串的半字节

    在您提供的第一个示例中:

    irb(main):002:0> chars.pack("H*")
    => "a"
    
    您要求将数组的第一个元素打包为一个十六进制字符串的半字节序列:
    0x61
    ,在本例中,它对应于
    'a'
    ASCII字符

    在第二个例子中:

    irb(main):003:0> chars.pack("HHH")
    => "```"
    
    您告诉我们要将数组的3个元素打包为半字节(在本例中为高位):
    0x60
    对应于
    “`
    ASCII字符。由于缺少“aTemplateString”的“2”或“*”修饰符,低位或第二个半字节(
    0x01
    )“丢失”

    您需要的是:

    chars.pack('H*' * chars.size)
    
    以便将数组中所有元素的所有半字节都打包为十六进制字符串

    只有当数组元素仅表示1字节的十六进制字符串时,
    'H2'*char.size
    的大小写才能正常工作

    这意味着像
    chars=[“6161”、“6262”、“6363”]
    这样的东西将是不完整的:

    2.1.5 :047 > chars = ["6161", "6262", "6363"]
     => ["6161", "6262", "6363"] 
    2.1.5 :048 > chars.pack('H2' * chars.size)
     => "abc" 
    
    而:

    2.1.5 :049 > chars.pack('H*' * chars.size)
     => "aabbcc"
    

    这对大投入是否有效?应该非常有效。我看到的唯一增加的成本是创建临时格式字符串,而“H*”无论如何都必须这样做。我们研究了几种不同的方法,以使其以与其他格式字符一致的方式工作,但事实并非如此,因此我们根据需要使用大小来扩展
    H2
    字符串。H并不太特殊,所有“字符串”都使用格式码(aahhzbbumpp)将长度后缀解释为它们从当前字符串元素中消耗的输入元素数量,而不是从数组中消耗的元素数量。@theTinMan啊,我现在明白了。这是一个文档错误。Ruby的
    pack
    是以Perl为模型的,perldoc明确指出,计数值对于AAZHHBPP具有不同的含义。对于
    'H'
    格式,
    *
    没有按照文档中的预期方式工作。其他格式字符的行为似乎正确,所以我怀疑这是Ruby使用
    'H*.
    的一个缺陷。