Перевод функции чтения двоичных данных C в Python - PullRequest
4 голосов
/ 21 августа 2011

(я отредактировал это для ясности и немного изменил реальный вопрос, основываясь на ответе EOL) Я пытаюсь перевести следующую функцию в C на Python, но с треском провалился (см. Код C ниже). Насколько я понимаю, требуется четыре 1-байтовых символа, начиная с области памяти, на которую указывает from, обрабатывает их как длинные беззнаковые целые, чтобы дать каждому из них 4 байта пространства, и выполняет сдвиг битов, чтобы расположить их как 32-разрядное целое число с прямым порядком байтов Затем он используется в алгоритме проверки правильности файла. (из Вавилонского договора )

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) будут работать. Однако для допустимых файлов это всегда возвращает 24 для значения bf, что означает, что цикл 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.

Что я делаю не так?


Хорошо, так что я, очевидно, правильно делаю read_alan_int. Однако, что мне не удается, так это проверка того, что первые 4 символа - «АЛАН». Все мои тестовые файлы не проходят этот тест. Я изменил код, чтобы удалить этот оператор if / else и вместо этого просто воспользоваться преимуществами ранних возвратов, и теперь все мои модульные тесты пройдены. Итак, на практическом уровне, я сделал. Тем не менее, я оставлю вопрос открытым для решения новой проблемы: как я могу разбить биты, чтобы получить «ALAN» из первых 4 символов?

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 ]

1 голос
/ 21 августа 2011

Ах, я думаю, что понял.Обратите внимание, что в описании написано

/*
  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 существует для файлов Alan 3.

Почему?Потому что вы не говорите на C, так что вы угадали - вполне естественно!- что memcmp вернет (эквивалент Python) True, если первые четыре символа sf и "ALAN" равны ... но это не так. memcmp возвращает 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'
0 голосов
/ 21 августа 2011

Ваша версия Python выглядит нормально для меня.

PS : я пропустил "memcmp() catch", который обнаружил DSM, поэтому код Python для if memcmp(…)… на самом деле должен быть `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 отсутствует в исходном посте ??

0 голосов
/ 21 августа 2011

Гипотеза 1: вы работаете в Windows, и вы не открыли свой файл в двоичном режиме.

...