【问题标题】:Reading and writing binary files in C用 C 读写二进制文件
【发布时间】:2020-09-09 10:42:22
【问题描述】:

这是 2 个独立的应用程序。

  • 在第一个中,我尝试将姓名、年龄和薪水等员工详细信息存储在名为 emp.bin 的二进制文件中。
  • 在第二个应用程序中,我尝试查看文件的内容,但在名称的位置,只出现了第一个字符。

我尝试单独打印每个字符,结果发现名称中的每个字母后面都有 3 个空字符 '\n',这就是为什么在第一个字符之后不打印的原因。

“写”应用代码:

//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>

int main()
{
    FILE *fp;
    char another = 'Y';
    struct emp
    {
        char name[40];
        int age;
        float bs;
    };
    struct emp e;
    fp = fopen("emp.bin", "wb");
    if (fp == NULL)
    {
        puts("Cannot open the file.");
        return 1;
    }
    while(another == 'Y')
    {
        printf("Enter the employee name, age and salary: ");
        scanf("%S %d %f", e.name, &e.age, &e.bs);
        while(getchar() != '\n');
        fwrite(&e, sizeof(e), 1, fp);
        printf("Add another record? (Y/N)");

        another = getchar();
    }
    fclose(fp);
    return 0;
}

“读取”应用代码:

//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>

int main()
{
    FILE *fp;
    struct emp
    {
        char name[40];
        int age;
        float bs;
    } e;
    fp = fopen("emp.bin", "rb");
    if (fp == NULL)
    {
        puts("Cannot open the file.");
        return 1;
    }
    while (fread(&e, sizeof(e), 1, fp) == 1)
    {
        printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
    }
    fclose(fp);
}

这是输入和输出:

如何更正此代码以使其打印全名?

【问题讨论】:

  • 干得好,简明扼要地说明问题并显示演示问题的简短代码。第一个问题很好。
  • 我没有在我的回答中提到它,因为它可能是一个错字(因为我在编辑时注意到它)但是当你说_“3个空字符'\n'”_有一个错误: \n 是换行符; nul 字符是 \0 (字符串终止符),它实际上是你得到的(我复制了你的问题)。
  • while(getchar() != '\n'); 会在文件提前结束的情况下导致无限循环。

标签: c pointers scanf binaryfiles file-pointer


【解决方案1】:

正如 Roberto 在他的回答中所指出的,问题在于 %S 转换说明符,这是一个错字,您应该使用 %s

但请注意,还有其他问题可能会造成问题:

  • 您应该告诉scanf() 读取员工姓名的最大字符数,否则如果输入太长,scanf() 可能会超出目标数组的末尾。

  • 如果两个程序在具有不同字节序的不同系统上运行,则接收端的数字将不正确,因为它们的字节将以相反的顺序存储。出于这个原因,应该在二进制文件中明确指定和处理字节顺序。文本格式往往是数据传输的首选。

这是修改后的版本:

//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>

int main() {
    FILE *fp;
    char another = 'Y';
    struct emp {
        char name[40];
        int age;
        float bs;
    } e;
    int c;

    fp = fopen("emp.bin", "wb");
    if (fp == NULL) {
        puts("Cannot open the file.");
        return 1;
    }
    while (another == 'Y') {
        printf("Enter the employee name, age and salary: ");
        if (scanf("%39s %d %f", e.name, &e.age, &e.bs) != 3)
            break;
        while ((c = getchar()) != EOF && c != '\n')
            continue;
        if (fwrite(&e, sizeof(e), 1, fp) != 1)
            break;
        printf("Add another record? (Y/N)");
        another = getchar();
    }
    fclose(fp);
    return 0;
}

“读取”应用代码:

//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>

int main() {
    FILE *fp;
    struct emp {
        char name[40];
        int age;
        float bs;
    } e;
    fp = fopen("emp.bin", "rb");
    if (fp == NULL) {
        puts("Cannot open the file.");
        return 1;
    }
    while (fread(&e, sizeof(e), 1, fp) == 1) {
        printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
    }
    fclose(fp);
    return 0;
}

【讨论】:

    【解决方案2】:

    问题出在 “writer” 应用程序中,甚至在执行实际写入之前。

    当你从用户那里获取数据时

    scanf("%S %d %f", e.name, &e.age, &e.bs);
    

    您使用格式%S(大写字母“S”。格式说明符区分大小写!)。正如我们在printf man page 中看到的那样

    S
    (不是在 C99 中,而是在 SUSv2 中。) ls 的同义词。不要使用。

    这将我们引向%ls 格式说明符,其描述方式如下

    s
    [...]如果存在 l 修饰符: const wchar_t * 参数应该是指向宽字符数组的指针。数组中的宽字符被转换为多字节字符

    谈到Windows source我们有:

    S
    相反大小的字符串,最多为第一个空白字符(空格、制表符或换行符)。 [...]
    scanf 函数一起使用时,表示宽字符数组;当与wscanf 函数一起使用时,表示单字节字符数组 [...]

    所以,基本上,您是从 stdin 读取字符并将它们转换为 宽字符。在这种情况下,每个字符都采用sizeof(wchar_t)。在你的系统中,这个大小可能是 4。


    您需要的只是%s 格式说明符。由于您的 name 数组大小为 40,我建议使用

    scanf("%39s", e.name );
    

    从用户那里获取名称。这样最多可以写入 39 个字符,这是为 string terminator '\0' 保留的第 40 个字符。

    【讨论】:

    • @chux 复制和粘贴错字。谢谢。
    • @RobertoCaboni:分析得很好,除了在这种情况下每个字符占用四个字节wchar_t 的实际大小是实现定义的,它可以是2 为在 Windows 上就是这样。
    • 是否应该在达到第 39 个限制后添加关于消耗字符的部分?
    • @chqrlie 知道吗?在我的 Windows(64 位)机器上测试程序之前,我也是这么想的。
    • @RobertoCaboni:我应该刚刚写了:在旧系统上可能是 2。
    猜你喜欢
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    • 2019-09-18
    • 1970-01-01
    • 2020-09-03
    • 2021-06-22
    相关资源
    最近更新 更多