繁体   English   中英

读取.bin文件的某些部分(例如,从11月23日开始):十六进制为int,char字符串。 C

[英]Reading certain part of a .bin file (eg. from 11th 23rd): hex into int, char string. C

我想阅读例如。 十六进制.bin文件中第11至23号之间的数字,如下所示: 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 / length / F. / string /”

  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...

非常感谢您提供的所有帮助和指导。

sscanf()不是用于处理此类二进制数据的正确工具。

从代码的最后一部分开始,您将获得更好的结果,您可以在其中直接索引缓冲区中的每个字符,然后逐个字符地对其进行处理。

请注意,这是在假设buffer是指向字符而不是字符数组的指针的情况下编写的。

您需要做的是读取四个字符以获取长度:

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';

这将读取名称,然后将其移动到指向下一个长度值的第一个字节的缓冲区指针。 然后,您要做的只是将长度重置为零,然后重复上述操作以读入姓氏。

考虑到我将精确的二进制数据保存在名为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文件以完全像这里介绍的那样缓冲

    通常,这不是一个好主意。 只阅读所需的内容。 假设您要从数百个MiB大的文件中读取10个字节。 那将完全浪费资源,有时甚至会导致崩溃

  • 似乎您在这里有一个简单的协议:

    1. 名称长度的 4个字节-这是uint32_t
    2. 通过为名称的名称长度给定的字节数量可变的-这是一个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
    • 从单个字节重建(整数)值时要小心,因为endianness可能会导致意外的结果。 检查[SO]:Python struct.pack()行为(@CristiFati的回答) (或者,当然是Google )以获取有关此主题的更多信息
    • 读取4个字节的大小,然后(分配和)读取字符串的“ 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] 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM