C#等价于perl`pack(“v”,value)`,同时将一些值打包成`byte []`

时间:2014-02-27 03:17:30

标签: c# perl byte bytearray bytecode

我试图在我的c#代码中复制perl脚本的行为。当我们将任何值转换为Byte[]时,无论使用何种语言,它都应该看起来相同。 SO

我有这个函数调用,在perl中看起来像这样:

$diag_cmd = pack("V", length($s_part)) . $s_part;

其中$s_par在以下函数中定义。它将.pds文件放在C:\Users\c_desaik\Desktop\DIAG\PwrDB\offtarget\data\get_8084_gpio.pds

位置
$s_part = 

    sub read_pds
    {
       my $bin_s;
       my $input_pds_file = $_[0];
      open(my $fh, '<', $input_pds_file) or die "cannot open file $input_pds_file";
      {
        local $/;
        $bin_s = <$fh>;
      }

  close($fh);
  return $bin_s;

}

我最好的猜测是这个函数正在读取.pds文件并将其转换为字节数组。

现在,我尝试将行为复制到c#代码中,如下所示

static byte[] ConstructPacket()
{
    List<byte> retval = new List<byte>();         
    retval.AddRange(System.IO.File.ReadAllBytes(@"C:\Users\c_desaik\Desktop\DIAG\PwrDB\offtarget\data\get_8084_gpio.pds"));
    return retval.ToArray();
}

但结果字节数组看起来不一样。我是否需要遵循任何特殊机制来复制pack("V", length($s_part)) . $s_part的行为?

2 个答案:

答案 0 :(得分:1)

正如Simon Whitehead所提到的,模板字符V告诉pack将您的值打包成无符号长整数(32位)(以小端序排列)。因此,您需要将字节转换为无符号整数的列表(或数组)。

例如:

static uint[] UnpackUint32(string filename)
{
    var retval = new List<uint>();

    using (var filestream = System.IO.File.Open(filename, System.IO.FileMode.Open))
    {
        using (var binaryStream = new System.IO.BinaryReader(filestream))
        {
            var pos = 0;
            while (pos < binaryStream.BaseStream.Length)
            {
                retval.Add(binaryStream.ReadUInt32());
                pos += 4;
            }
        }
    }

    return retval.ToArray();
}

并调用此函数:

var list = UnpackUint32(@"C:\Users\c_desaik\Desktop\DIAG\PwrDB\offtarget\data\get_8084_gpio.pds");


更新

如果您想阅读一个长度为前缀的字符串或其列表,您可以使用此功能:

private string[] UnpackStrings(string filename)
{
    var retval = new List<string>();

    using (var filestream = System.IO.File.Open(filename, System.IO.FileMode.Open))
    {
        using (var binaryStream = new System.IO.BinaryReader(filestream))
        {
            var pos = 0;
            while ((pos + 4) <= binaryStream.BaseStream.Length)
            {
                // read the length of the string
                var len = binaryStream.ReadUInt32();

                // read the bytes of the string
                var byteArr = binaryStream.ReadBytes((int) len);

                // cast this bytes to a char and append them to a stringbuilder
                var sb = new StringBuilder();
                foreach (var b in byteArr)
                    sb.Append((char)b);

                // add the new string to our collection of strings
                retval.Add(sb.ToString());

                // calculate start position of next value
                pos += 4 + (int) len;
            }
        }
    }

    return retval.ToArray();
}

答案 1 :(得分:1)

pack("V", length($s_part)) . $s_part

也可以写成

pack("V/a*", $s_part)

创建一个长度为前缀的字符串。长度存储为32位无符号小端数。

+----------+----------+----------+----------+-------- ...
|  Length  |  Length  |  Length  |  Length  | Bytes
| ( 7.. 0) | (15.. 8) | (23..16) | (31..24) |
+----------+----------+----------+----------+-------- ...

这是从字节重新创建原始字符串的方法:

  1. 读取4个字节
  2. 如果使用小端机以外的机器,
    1. 将字节重新排列为本机顺序。
  3. 将这些字节转换为32位无符号整数。
  4. 读取等于该数字的字节数。
  5. 将该字节序列转换为字符串。
  6. 某些语言提供的工具可以执行多个步骤。

    我不知道C#,所以我不能为你编写代码,但我可以用其他两种语言给你一个例子。

    在Perl中,这将写成如下:

    sub read_bytes {
       my ($fh, $num_bytes_to_read) = @_;
       my $buf = '';
       while ($num_bytes_to_read) {
          my $num_bytes_read = read($fh, $buf, $num_bytes_to_read, length($buf));
          if (!$num_bytes_read) {
             die "$!\n" if !defined($num_bytes_read);
             die "Premature EOF\n";
          }
    
          $num_bytes_to_read -= $num_bytes_read;
       }
    
       return $buf;
    }
    
    sub read_uint32le { unpack('V', read_bytes($_[0], 4)) }
    sub read_pstr { read_bytes($_[0], read_uint32le($_[0])) }
    
    my $str = read_pstr($fh);
    

    在C中,

    int read_bytes(FILE* fh, void* buf, size_t num_bytes_to_read) {
       while (num_bytes_to_read) {
          size_t num_bytes_read = fread(buf, 1, num_bytes_to_read, fh);
          if (!num_bytes_read)
             return 0;
    
          num_bytes_to_read -= num_bytes_read;
          buf += num_bytes_read;
       }
    
       return 1;
    }
    
    int read_uint32le(FILE* fh, uint32_t* p_i) {
       int ok = read_bytes(fh, p_i, sizeof(*p_i));
       if (!ok)
          return 0;
    
       { /* Rearrange bytes on non-LE machines */
          const char* p = (char*)p_i;
          *p_i = ((((p[3] << 8) | p[2]) << 8) | p[1]) << 8) | p[0];
       }
    
       return 1;
    }
    
    char* read_pstr(FILE* fh) {
       uint32_t len;
       char* buf = NULL;
       int ok;
    
       ok = read_uint32le(fh, &len);
       if (!ok)
          goto ERROR;
    
       buf = malloc(len+1);
       if (!buf)
          goto ERROR;
    
       ok = read_bytes(fh, buf, len);
       if (!ok)
          goto ERROR;
    
       buf[len] = '\0';
       return buf;
    
    ERROR:
       if (p)
          free(p);
    
       return NULL;
    }
    
    
    char* str = read_pstr(fh);