c - 读取 .bin 文件的某些部分(例如从 11 日到 23 日): hex into int, 字符串。 C

标签 c file pointers

我想阅读例如。十六进制 .bin 文件中的第 11 个和第 23 个数字如下所示:https://imgur.com/b4RzPjw将某些部分打印为整数或将其他部分打印为名称(字符串)。 (最好不使用任何[],仅对指针进行操作)

我的示例 .bin 文件包含:前 4 个十六进制数字(蓝色突出显示)是名称的长度,然后 2 个数字是 ASCII 形式的名称。接下来的4个数字(蓝色下划线)是姓氏的长度(红色下划线),最后一个数字是索引。

我的尝试:
将整个 .bin 文件加载到缓冲区后,与此处所示的完全一样:http://www.cplusplus.com/reference/cstdio/fread/ ,我痛苦地尝试了多种方法将此缓冲区的部分分配给变量(或结构),然后使用格式 printf 它,只是为了看看分配了什么。

 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”。来 self 的 .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...

非常感谢您的帮助和指导。

最佳答案

考虑到我将您的确切二进制数据保存在名为 data.bin 的文件中,下面是一个示例:

代码.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;
}

注释:

  • After loading entire .bin file to buffer exactly like presented here

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

  • 看来您这里有一个简单的协议(protocol):

    1. 4 个字节名称长度 - 这是一个 uint32_t
    2. name 的名称长度给出的可变字节数 - 这是一个 char *,因为它的长度在编译时未知(您可以有一个像这样的数组:char[SOME_MAX_NAME_LENGTH],您可以确定在前一个字段中永远不会有大于SOME_MAX_NAME_LENGTH的值,但我更喜欢这种方法)<
    3. #1.相同的内容应用于姓氏长度
    4. #2.相同的内容应用于姓氏


    这映射到Record结构(是的,成员顺序并不重要,只有初始化顺序)。事情还可以更进一步,因为姓氏的数据与姓名的数据重复,因此可能存在一个包含姓名的内部结构em> data 和 Record 仅包含一个包含该结构的 2 个元素的数组。
    但即使这样事情会更简单(而且函数中的代码也会更短 - 没有重复),我也没有这样做,因为它可能不太明显

  • printRecord - 以用户友好的方式显示Record数据(当printfing时,您可以注意到这里的指针逻辑单独的字符)

  • clearRecord - 释放 char * 成员占用的内存并将所有内容初始化为0

  • readRecord - 从文件读取数据并填充记录

    • 没有有任何错误处理,因为代码已经相当长了。但您应该始终检查并处理错误(函数返回码:例如fread)
    • 从各个字节重建(整数)值时要小心,因为字节顺序可能会导致意外结果。检查[SO]: Python struct.pack() behavior (@CristiFati's answer) (当然,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]

关于c - 读取 .bin 文件的某些部分(例如从 11 日到 23 日): hex into int, 字符串。 C,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52085090/

相关文章:

C语言中使用Unix概念的目录监视器代码

c++ - 使用 rtsp、visual studio OpenCv 2.4.5 访问 IP 摄像机?

c++ - 创建一个 char 类型的大数组并复制文件字符

performance - RAMdisk 比磁盘慢?

c++ - Arduino:如何向任何文本框或记事本显示 Serial.print 值

无符号整数递增会导致未定义的行为吗?

javascript - 在不知道文件名的情况下获取文件扩展名

c++ - 我怎么知道我是否需要删除 C++ 中的某些内容?

c - 知道初始地址的指针的地址

c++ - for_each 指针 vector 中的指针