Как мне перечислить все имена файлов ZIP-архива в C ++ с нуля? - PullRequest
0 голосов
/ 20 апреля 2020

Это код, который у меня есть до сих пор

#include <iostream>
#include <fstream>

void printNextFileName(std::ifstream &fs)
{
    fs.ignore(6);
    char buf[2];
    fs.read(buf,2);
    int flags=buf[0]|buf[1]<<8;
    bool hasEncryptionHeader=1&flags;
    bool hasDataDescriptor=(1<<3)&flags;

    fs.ignore(10);
    char buf2[4];
    fs.read(buf2,4);
    long size=buf2[0]|buf2[1]<<8|buf2[2]<<16|buf2[3]<<24;

    fs.ignore(4);
    char buf3[2];
    fs.read(buf3,2);
    int fileNameLength=buf3[0]|buf3[1]<<8;

    char buf4[2];
    fs.read(buf4,2);
    int extraFieldLength=buf4[0]|buf4[1]<<8;

    char buf5[fileNameLength];
    fs.read(buf5,fileNameLength);

    for(int i=0;i<fileNameLength;i++)
        std::cout<<buf5[i];

    std::cout<<" "<<hasEncryptionHeader<<" "<<hasDataDescriptor; //testing

    fs.ignore(extraFieldLength+(hasEncryptionHeader?8:0)+size+(hasDataDescriptor?18:0)); //some of these values are for testing
}

int main()
{
    std::ifstream fs("zip file to read");

    for(int i=0;i<100;i++) //this is for testing; i will check for the end of the zip file later
    {
        printNextFileName(fs);
        std::cout<<std::endl;
    }

    fs.close();
}

Это работает для простого zip-файла, который я создал, чтобы проверить его, но он не работает для файла jar, который я нашел где-то на моем p c (файл jar - это функциональная программа, поэтому она должна работать). Я считаю, что проблема в том, что код не учитывает что-либо до или после данных файла. Мне нужно знать, чего мне не хватает. Я пытался исправить это весь день и всю прошлую ночь.

Кстати, это как мой второй пост переполнения стека, так что извините, если что-то пропустил; Я буду рад добавить его.

1 Ответ

0 голосов
/ 20 апреля 2020

Проблема 1: Файл открыт в текстовом режиме

Когда вы читаете файл в тексте, возможны все виды полезных преобразований, которые наиболее бесполезны при работе с двоичным файлом. Наиболее распространенным является Windows возврат каретки и перевод строки в конец строки (обычно 0x0D0A), переводимый в новую строку (обычно 0x0A) или наоборот.

Решение 1. Откройте файл в двоичном режиме

std::ifstream fs("zip file to read", std::ios::binary);

Проблема 2: Расширение знака на символах.

Эта простая программа

#include <iostream>
#include <iomanip>

int main() {
    char buf2[4] = {(char)0x80, 0x55, 0x55, 0x55};
    long size=buf2[0]|buf2[1]<<8|buf2[2]<<16|buf2[3]<<24;
    std::cout << std::hex << size;
    return 0;
}

должна распечатать 55555580, верно?

Нет. Он может выводить ffffff80.

Как?

При использовании в арифметике 0x80 c buf2[0] преобразуется в int (или что-то большее, если необходимо), а если char подписано, знак будет сохранен. 0x80 расширяется до 0xFFFFFF80. И все эти лишние 1 бит разбивают havo c на двоичные операции, когда int собирается из байтов.

Примечание: смотрите, как мне пришлось привести 0x80, чтобы он соответствовал? Первый признак того, что что-то идет не так, go часто является актером.

Решение 2

Используйте unsigned char

int main() {
    unsigned char buf2[4] = {0x80, 0x55, 0x55, 0x55};
    // note that the cast isn't needed any more
    long size=buf2[0]|buf2[1]<<8|buf2[2]<<16|buf2[3]<<24;
    std::cout << std::hex << size;
    return 0;
}

или замаскируйте расширенные биты.

int main() {
    char buf2[4] = {(char)0x80, 0x55, 0x55, 0x55};
    long size=buf2[0]     & 0x000000FF | 
              buf2[1]<<8  & 0x0000FF00 |
              buf2[2]<<16 & 0x00FF0000 |
              buf2[3]<<24 & 0xFF000000; 
    std::cout << std::hex << size;
    return 0;
}

примечание: long может не быть 32 бит, и вы все еще можете войти в мир боли. Используйте Fixed Width Integer из cstdint, чтобы убедиться, что вы всегда работаете с данными правильного размера, когда это важно. Здесь это имеет значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...