将C二进制数据读取函数转换为Python

时间:2011-08-21 10:07:46

标签: python c binary-data

(为了清楚起见,我编辑了这个,并根据EOL的答案改变了实际问题) 我正在尝试将C中的以下函数转换为Python但是失败了(参见下面的C代码)。据我所知,从from指向的内存位置开始需要四个1字节的字符,将它们视为无符号长整数,以便为每个字节提供4个字节的空间,并进行一些位移以排列它们作为big-endian的32位整数。然后将其用于检查文件有效性的算法中。 (来自Treaty of Babel

static int32 read_alan_int(unsigned char *from)
{
 return ((unsigned long int) from[3])| ((unsigned long int)from[2] << 8) |
       ((unsigned long int) from[1]<<16)| ((unsigned long int)from[0] << 24);
}
/*
  The claim algorithm for Alan files is:
   * For Alan 3, check for the magic word
   * load the file length in blocks
   * check that the file length is correct
   * For alan 2, each word between byte address 24 and 81 is a
      word address within the file, so check that they're all within
      the file
   * Locate the checksum and verify that it is correct
*/
static int32 claim_story_file(void *story_file, int32 extent)
{
 unsigned char *sf = (unsigned char *) story_file;
 int32 bf, i, crc=0;
 if (extent < 160) return INVALID_STORY_FILE_RV;
 if (memcmp(sf,"ALAN",4))
 { /* Identify Alan 2.x */
 bf=read_alan_int(sf+4);
 if (bf > extent/4) return INVALID_STORY_FILE_RV;
 for (i=24;i<81;i+=4)
 if (read_alan_int(sf+i) > extent/4) return INVALID_STORY_FILE_RV;
 for (i=160;i<(bf*4);i++)
 crc+=sf[i];
 if (crc!=read_alan_int(sf+152)) return INVALID_STORY_FILE_RV;
 return VALID_STORY_FILE_RV;
 }
 else
 { /* Identify Alan 3 */
   bf=read_alan_int(sf+12);
   if (bf > (extent/4)) return INVALID_STORY_FILE_RV;
   for (i=184;i<(bf*4);i++)
    crc+=sf[i];
 if (crc!=read_alan_int(sf+176)) return INVALID_STORY_FILE_RV;

 }
 return INVALID_STORY_FILE_RV;
}

我正试图在Python中重新实现它。为了实现read_alan_int函数,我认为导入struct和执行struct.unpack_from('>L', data, offset)会起作用。但是,对于有效文件,对于值bf,此值始终返回24,这意味着将跳过for循环。

def read_alan_int(file_buffer, i):
    i0 = ord(file_buffer[i]) * (2 ** 24)
    i1 = ord(file_buffer[i + 1]) * (2 ** 16)
    i2 = ord(file_buffer[i + 2]) * (2 ** 8)
    i3 = ord(file_buffer[i + 3])
    return i0 + i1 + i2 + i3

def is_a(file_buffer):
    crc = 0
    if len(file_buffer) < 160:
        return False
    if file_buffer[0:4] == 'ALAN':
        # Identify Alan 2.x
        bf = read_alan_int(file_buffer, 4)
        if bf > len(file_buffer)/4:
            return False
        for i in range(24, 81, 4):
            if read_alan_int(file_buffer, i) > len(file_buffer)/4:
                return False
        for i in range(160, bf * 4):
            crc += ord(file_buffer[i])
        if crc != read_alan_int(file_buffer, 152):
            return False
        return True
    else:
        # Identify Alan 3.x
        #bf = read_long(file_buffer, 12, '>')
        bf = read_alan_int(file_buffer, 12)
        print bf
        if bf > len(file_buffer)/4:
            return False
        for i in range(184, bf * 4):
            crc += ord(file_buffer[i])
        if crc != read_alan_int(file_buffer, 176):
            return False
        return True
    return False


if __name__ == '__main__':
    import sys, struct
    data = open(sys.argv[1], 'rb').read()
    print is_a(data)

...但该死的东西仍然会返回24.不幸的是,我的C技能不存在,所以我无法让原始程序打印一些调试输出,所以我可以知道bf应该是什么。 / p>

我做错了什么?


好的,所以我显然正确地做了read_alan_int。但是,对我来说失败的是检查前4个字符是“ALAN”。我的所有测试文件都未通过此测试。我已经更改了代码以删除这个if / else语句,而只是利用早期返回,现在我的所有单元测试都通过了。所以,在实际层面上,我已经完成了。但是,我会继续提出问题来解决这个新问题:我怎么可能纠缠这些位以从前4个字符中获取“ALAN”?

def is_a(file_buffer):
    crc = 0
    if len(file_buffer) < 160:
        return False
    #if file_buffer.startswith('ALAN'):
        # Identify Alan 2.x
    bf = read_long(file_buffer, 4)
    if bf > len(file_buffer)/4:
        return False
    for i in range(24, 81, 4):
        if read_long(file_buffer, i) > len(file_buffer)/4:
            return False
    for i in range(160, bf * 4):
        crc += ord(file_buffer[i])
    if crc == read_long(file_buffer, 152):
        return True
    # Identify Alan 3.x
    crc = 0
    bf = read_long(file_buffer, 12)
    if bf > len(file_buffer)/4:
        return False
    for i in range(184, bf * 4):
        crc += ord(file_buffer[i])
    if crc == read_long(file_buffer, 176):
        return True
    return False

3 个答案:

答案 0 :(得分:1)

啊,我想我已经明白了。请注意,描述说

/*
  The claim algorithm for Alan files is:
   * For Alan 3, check for the magic word
   * load the file length in blocks
   * check that the file length is correct
   * For alan 2, each word between byte address 24 and 81 is a
      word address within the file, so check that they're all within
      the file
   * Locate the checksum and verify that it is correct
*/

我读到这里说Alan 3中有一个神奇的词,但Alan 2中没有。但是,你的代码是另一种方式,即使C代码只假定Alan 3文件存在ALAN。

为什么呢?因为你不会说C,所以你猜对了 - 自然就够了! - memcmp将返回(相当于Python)如果sf和“ALAN”的前四个字符相等,则为True ..但它不会。 memcmp returns 0如果内容相等,则如果它们不同则为非零。

这似乎是它的工作方式:

>>> import urllib2
>>> 
>>> alan2 = urllib2.urlopen("http://ifarchive.plover.net/if-archive/games/competition2001/alan/chasing/chasing.acd").read(4)
>>> alan3 = urllib2.urlopen("http://mirror.ifarchive.org/if-archive/games/competition2006/alan/enterthedark/EnterTheDark.a3c").read(4)
>>> 
>>> alan2
'\x02\x08\x01\x00'
>>> alan3
'ALAN'

答案 1 :(得分:0)

假设1:您正在Windows上运行,而您尚未以二进制模式打开文件。

答案 2 :(得分:0)

你的Python版本对我来说很好。

PS :我错过了DSM找到的“memcmp() catch”,因此if memcmp(…)…的Python代码实际应该是`if file_buffer [0:4]!= 'ALAN'。

从C代码和您在原始问题的注释中提供的示例文件中我可以看到,示例文件确实无效;这是值:

read_alan_int(sf+12) == 24  # 0, 0, 0, 24 in file sf, big endian
crc = 0
read_alan_int(sf+176) = 46  # 0, 0, 0, 46 in file sf, big endian

所以,crc != read_alan_int(sf+176),确实。

您确定示例文件是有效文件吗?或者是原始帖子遗失的crc计算的一部分?