Чтение определенной части .bin-файла (например, с 11-го по 23-е): шестнадцатеричное в int, символьная строка. С - PullRequest
0 голосов
/ 29 августа 2018

Я хочу читать, например. между 11-м и 23-м числами в шестнадцатеричном .bin-файле , выглядящем так: https://imgur.com/b4RzPjw, печатать некоторые части как intiger или другие части как имя (строку) . (желательно без использования [ ], только операции с указателями)

Мой пример .bin-файла содержит: сначала 4 шестнадцатеричных числа (синяя подсветка) - длина имени, затем 2 числа - имя в ASCII. Следующие 4 цифры (синее подчеркивание) - это длина фамилии (красное подчеркивание), а последнее - индекс.

Моя попытка:
После загрузки всего .bin-файла в буфер точно так, как показано здесь: http://www.cplusplus.com/reference/cstdio/fread/, я с треском пытался разными способами присвоить части этого буфера переменным (или структуре), а затем распечатать его, используя форматирование, просто чтобы увидеть что было назначено.

 char *name_length = malloc(4);
 char *pEnd;
 for(*buffer=0; *buffer<4; *buffer++) {
     sscanf(buffer, "%s", name_length);
     long int i = strtol (buffer, &pEnd, 16);
     printf("%x", i);
 }

Над (неправильным) кодом печатается 0000 (я думаю, он полностью прогнил от своих корней, хотя я не знаю, почему); в случае, если был элегантный способ загрузить части буфера уже в структуру, вот объявление:

 struct student_t
{
    char name[20];
    char surname[40];
    int index;
};

«Ближайший» результат, который я смог получить, - это другой код, который печатает «2000». из моего .bin файла: «02 00 00 46 2E», что означает «2 0 0 0 / длина / F. / строка /»

  for(int i=0; i<4; i++)
  printf("%d", buffer[i]); //it's supposed to print first 4 hex digits...
  for(int j=5; j<7; j++)
  printf("%s", &buffer[j]); //it's supposed to print from 5th to 7th...

Большое спасибо за помощь и руководство.

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Учитывая, что я сохранил ваши точные двоичные данные в файле с именем data.bin , вот пример:

code.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <errno.h>

#define FILE_NAME "data.bin"


typedef struct Record_ {
    uint32_t nameLen, surnameLen;
    char *name, *surname;
} Record;


void printRecord(Record record) {
    printf("\nPrinting record:\n  Name length: %u\n  Name: [", record.nameLen);
    if ((record.nameLen != 0) && (record.name != NULL)) {
        char *pc;
        for (pc = record.name; pc < record.name + record.nameLen; pc++) {
            printf("%c", *pc);
        }
    }
    printf("]\n  Surname length: %u\n  Surname: [", record.surnameLen);
    if ((record.surnameLen != 0) && (record.surname != NULL)) {
        char *pc;
        for (pc = record.surname; pc < record.surname + record.surnameLen; pc++) {
            printf("%c", *pc);
        }
    }
    printf("]\n");
}


void clearRecord(Record *pRecord) {
    free(pRecord->name);
    free(pRecord->surname);
    memset(pRecord, 0, sizeof(Record));
}


int readRecord(FILE *pFile, Record *pRecord) {
    size_t readBytes = fread(&pRecord->nameLen, sizeof(pRecord->nameLen), 1, pFile);
    if (pRecord->nameLen != 0) {
        pRecord->name = malloc(pRecord->nameLen);
        readBytes= fread(pRecord->name, 1, pRecord->nameLen, pFile);
    }
    readBytes = fread(&pRecord->surnameLen, sizeof(pRecord->surnameLen), 1, pFile);
    if (pRecord->surnameLen != 0) {
        pRecord->surname = malloc(pRecord->surnameLen);
        readBytes = fread(pRecord->surname, 1, pRecord->surnameLen, pFile);
    }
    return 0;
}


int main() {
    FILE *fp = fopen(FILE_NAME, "r+b");
    if (fp == NULL)
    {
        printf("Error opening file: %d\n", errno);
        return 1;
    }
    Record record = {0, 0, NULL, NULL};
    printRecord(record);
    int ret = readRecord(fp, &record);
    if (ret)
    {
        printf("readRecord returned %d\n", ret);
        fclose(fp);
        return 2;
    }
    printRecord(record);
    clearRecord(&record);
    fclose(fp);
    return 0;
}

Примечания :

  • После загрузки всего .bin-файла в буфер, как показано здесь

    Обычно это не очень хорошая идея. Читайте только столько, сколько вам нужно. Представьте, что вы хотите прочитать 10 байтов из файла размером в сотни МиБ s. Это будет полная трата ресурсов, а иногда может даже привести к сбоям

  • Похоже, у вас есть простой протокол:

    1. 4 байтов для длина имени - это uint32_t
    2. Переменное число байтов, заданное длиной имени для name - это char *, так как его длина неизвестна во время компиляции (у вас может быть такой массив: char[SOME_MAX_NAME_LENGTH] где Вы точно знаете, что в предыдущем поле никогда не будет значения, превышающего SOME_MAX_NAME_LENGTH, но мне этот подход нравится больше)
    3. То же самое из # 1. применяется для длина фамилии
    4. То же самое из # 2. применяется для фамилия


    Отображается в структуре Record (да, порядок членов не важен, только порядок инициализации). Можно пойти еще дальше, поскольку данные для фамилия являются дубликатом данных для name , могла существовать внутренняя структура, содержащая данные name и Record содержит только массив с 2 элементами этой структуры.
    Но даже если бы все было проще (а также код в функциях был бы короче - без дублирования), я этого не делал, потому что это, вероятно, было бы менее очевидно

  • printRecord - отображает Record данные в удобной для пользователя форме (здесь вы можете заметить логику указателя при printf индивидуальном вводе символов)

  • clearRecord - освобождает память, занятую элементами char *, и инициализирует все как 0

  • readRecord - считывает данные из файла и заполняет запись

    • У нет обработки ошибок, так как код уже довольно длинный. Но вы должны всегда проверять и обрабатывать ошибки (коды возврата функции: например, fread)
    • Будьте осторожны при восстановлении (целочисленных) значений из отдельных байтов, так как вы можете получить неожиданные результаты из-за порядка байтов . Проверьте [SO]: поведение Python struct.pack () (@ Ответ CristiFati) (или, конечно, Google ) для получения дополнительной информации по этой теме
    • Считать 4 байта для размера, затем (allocate and) прочитать « size » для строки (я могу ошибаться, но я не думаю, что sscanf (семейство функций) должен работать с двоичными данными (кроме строк))

выход

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q052085090]> gcc code.c -o code.exe && ./code.exe

Printing record:
  Name length: 0
  Name: []
  Surname length: 0
  Surname: []

Printing record:
  Name length: 2
  Name: [F.]
  Surname length: 13
  Surname: [MurrayAbraham]
0 голосов
/ 30 августа 2018

sscanf() - неправильный инструмент для обработки двоичных данных, подобных этому.

Вы получите гораздо лучшие результаты, работая с чем-то, похожим на ваш последний раздел кода, где вы индексируете каждый символ в буфере напрямую и обрабатываете его поочередно.

Обратите внимание, что это написано при условии, что буфер - это указатель на символы, а не массив символов.

Что вам нужно сделать, это прочитать четыре символа, чтобы получить длину:

struct student_t result;
int length = 0;
int i;
// Progress backwards down data since it's stored "little endian"
for (i = 3; i >= 0; i--)
{
     length = (length << 8) + (buffer[i] & 255);
}

Мы только что использовали четыре байта, переместите указатель буфера вперед, чтобы пропустить их:

buffer += 4;

У нас есть длина, и наш указатель буфера теперь обращается к первому символу имени. Прочитайте столько символов и сохраните их:

for (i = 0; i < length; i++)
{
    result.name[i] = *buffer++;
}
// Add a NUL byte to terminate the string.
result.name[i] = '\0';

Это прочитает имя, и при этом он переместил указатель буфера для адресации первого байта следующего значения длины. Все, что вам нужно сделать, это сбросить длину до нуля и повторить вышеизложенное, чтобы прочитать фамилию.

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