【问题标题】:How can I read and obtain separated data from a file using 'fread' in C?如何使用 C 中的“fread”从文件中读取和获取分离的数据?
【发布时间】:2016-11-19 10:25:54
【问题描述】:

我在一个文件中(使用'fwrite()')写了以下内容:

TUS�ABQ���������������(A����������(A��B������(A��B���A��(A��B���A������B���A������0����A������0�ABQ�������0�ABQ�����LAS����������������A�����������A��&B�������A��&B��B���A��&B��B������&B��
B����153���B����153�LAS�����153�LAS�����LAX���������������:A����������:AUUB������:AUUB��B��:
AUUB��B����UUB��B����������B��������LAX���������LAX�����MDW���������������A����������A��(�������A��(����A��A��(����A������(����A����A�89���A����A�89MDW�����A�89MDW�����OAK���������
����������������������@�����������@�����������@�����������@�������������������������OAK���������OAK�����SAN���������������LA����������LA��P@������LA��P@��@A��LA��P@��@A������P@��@A����������@A��������SAN���������SAN�����TPA�ABQ����������������B�����������B��@�����...(continues)

翻译成这样:

TUSLWD2.103.47.775.1904.06.40.03AMBRFD4.63.228.935.0043.09.113.0ASDGHU5.226.47.78.3.26...(The same structure)

而 hexdump 将是:

00000000  54 55 53 00 41 42 51 00  00 00 00 00 00 00 00 00  |TUS.ABQ.........|
00000010  00 00 00 00 00 00 28 41  00 00 0e 42 00 00 f8 41  |......(A...B...A|
00000020  00 00 00 00 4c 41 53 00  00 00 00 00 00 00 00 00  |....LAS.........|
00000030  00 00 00 00 00 00 88 41  00 00 26 42 9a 99 11 42  |.......A..&B...B|
(Continues...)

结构总是 2 个单词,每个单词 3 个字符(即 TUS 和 LWD),后跟 7 个浮点数,然后在文件结束时再次重复。

关键是:我只想读取分隔的每个字段,例如“TUS”、“LWD”、“2.10”、“3.4”、“7.77”...

而我只能使用 'fread()' 来实现这一点!现在,我正在尝试这个:

aux2 = 0;
fseek(fp, SEEK_SET, 0);
fileSize = 0;
while (!feof(fp) && aux<=2) {
    fread(buffer, sizeof(char)*4, 1, fp);
    printf("%s", buffer);
    fread(buffer, sizeof(char)*4, 1, fp);
    printf("%s", buffer);
    for(i=0; i<7; i++){
        fread(&delay, sizeof(float), 1, fp);
        printf("%f", delay);
    }
    printf("\n");
    aux++;
    fseek(fp,sizeof(char)*7+sizeof(float)*7,SEEK_SET);
    aux2+=36;
}

我得到了这个结果:

TUSABQ0.0000000.0000000.00000010.5000000.0000000.00000010.500000
AB0.0000000.000000-10384675421112248092159136000638976.0000000.0000000.000000-10384675421112248092159136000638976.0000000.000000
AB0.0000000.000000-10384675421112248092159136000638976.0000000.0000000.000000-10384675421112248092159136000638976.0000000.000000

但它不能正常工作......

*注意:忘记最后一个'fseek()'的参数,因为我一直在尝试太多无意义的事情! 要将单词(即 TUS)写入文件,我使用以下命令:

fwrite(x->data->key, 4, sizeof(char), fp);

要写浮点数,这个:

for (i = 0; i < 7; i++) {
    fwrite(&current->data->retrasos[i], sizeof(float), sizeof(float), fp);
}

【问题讨论】:

  • 三个字符的单词”:为什么...*4 ...。它应该是fread(buffer, sizeof(char)*3 ... 甚至“更好”的fread(buffer, sizeof(char), 3, ...
  • 因为当我将它们存储在文件中时,每个都有一个结尾 '\0' 字符,所以我正在尝试将其读回@alk
  • 从您显示的转储 (TUSABQ...) 看来,尾部的 \0' 似乎没有写入文件。也许向我们展示由适当的转储工具(如hexdump)生成的文件的clean hex-dump。
  • 好的,所以您可能还想展示用于编写文件的代码。
  • 为了调试起见,将此printf("%s", buffer); 基于行:printf("%s\n", buffer); 或指示每个块读取如下printf("'%s'", buffer);

标签: c binaryfiles fread fseek


【解决方案1】:

我建议使用一个结构来保存每个数据单元:

typedef struct {
    float  value[7];
    char   word1[5];  /* 4 + '\0' */
    char   word2[5];  /* 4 + '\0' */
} unit;

为了使文件格式具有可移植性,您需要一个函数将上述结构打包和解包到 36 字节数组。在 Intel 和 AMD 架构上,float 对应于 IEEE-754-2008 binary32 格式,采用 little-endian 字节顺序。例如,

#define STORAGE_UNIT (4+4+7*4)

#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)

size_t unit_pack(char *target, const size_t target_len, const unit *source)
{
    size_t i;

    if (!target || target_len < STORAGE_UNIT || !source) {
        errno = EINVAL;
        return 0;
    }

    memcpy(target + 0, source->word1, 4);
    memcpy(target + 4, source->word2, 4);

    for (i = 0; i < 7; i++)
        memcpy(target + 8 + 4*i, &(source->value[i]), 4);

    return STORAGE_UNIT;
}

size_t unit_unpack(unit *target, const char *source, const size_t source_len)
{
    size_t i;

    if (!target || !source || source_len < STORAGE_UNIT) {
        errno = EINVAL;
        return 0;
    }

    memcpy(target->word1, source, 4);
    target->word1[4] = '\0';

    memcpy(target->word2, source + 4, 4);
    target->word2[4] = '\0';

    for (i = 0; i < 7; i++)
        memcpy(&(target->value[i]), source + 8 + i*4, 4);

    return STORAGE_UNIT;
}

#else
#error Unsupported architecture!
#endif

以上仅适用于 Intel 和 AMD 机器,但如果需要,当然很容易扩展到其他架构。 (目前几乎所有机器都使用 IEEE 754-2008 binary32 来表示 float,只是字节顺序有所不同。那些不使用的机器通常具有 C 扩展,可以在其内部格式之间进行转换。)

使用以上内容,您可以——应该!必须! -- 记录您的文件格式,例如:

Words are 4 bytes encoded in UTF-8
Floats are IEEE 754-2008 binary32 values in little-endian byte order

A file contains one or more units. Each unit comprises of

    Name    Description
    word1   First word
    word2   Second word
    value0  First float
    value1  Second float
    value2  Third float
    value3  Fourth float
    value4  Fifth float
    value5  Sixth float
    value6  Second float

There is no padding.

要写入一个单元,请使用大小为STORAGE_UNITchar 数组作为缓存,然后将其写入。所以,如果你有unit *one,你可以使用

将它写到FILE *out
    char  buffer[STORAGE_UNIT];

    if (unit_pack(buffer, sizeof buffer, one)) {
        /* Error! Abort program! */
    }
    if (fwrite(buffer, STORAGE_UNIT, 1, out) != 1) {
        /* Write error! Abort program! */
    }

相应地,从FILE *in 读取将是

    char buffer[STORAGE_UNIT];

    if (fread(buffer, STORAGE_UNIT, 1, in) != 1) {
        /* End of file, or read error.
           Check feof(in) or/and ferror(in). */
    }
    if (unit_unpack(one, buffer, STORAGE_UNIT)) {
        /* Error! Abort program! */
    }

如果oneunits 的数组,并且您正在写入或读取one[k],请使用&amp;(one[k])(或等效的one + k)而不是one

【讨论】:

  • 是的,它成功了!我花了一段时间才理解它,但我终于明白了!非常感谢!!
  • @wj127:很高兴听到!是的,我……过于冗长了;文字墙等等。顺便说一句,为什么使用这样的缓冲区是个好主意还有另一个原因,那就是对齐要求。根据硬件架构,某些类型,如float 不能只驻留在any 地址,而是要求该地址是某个小数的倍数,以便处理器可以正确访问它。 (或者,未对齐的访问可能非常慢。)使用这样的缓冲区可以让您安全地以文件格式打包数据,而不必担心这种对齐或填充。
  • 不不,没关系!事实上,我的这个问题是关于我正在为“操作系统”主题做的一个实践,所以很高兴了解更多关于它的免费信息,而且看起来你对这个问题了解很多!再次感谢! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-24
  • 2021-12-24
  • 1970-01-01
  • 2016-04-20
  • 1970-01-01
  • 2016-10-10
  • 1970-01-01
相关资源
最近更新 更多