【问题标题】:C Write / Read Data From Binary FileC 从二进制文件写入/读取数据
【发布时间】:2021-07-02 11:56:13
【问题描述】:

更新

IBM HC-486 1995 11 12 228 Иванов IBM HC-476 1990 1 42 218 Васильев

所以我有点尝试阅读两条记录。第一个很适合。第二个看起来很糟糕。 我有点固定建议,非常感谢它有助于向前发展。所以现在我坚持输出两条记录。 结果是 ->

mark = IBM HC-486 year = 1995 month = 11 day = 12 numroom = 228 lastname = Ивановmark =  IBM HC-47 year = 6 month = 1990
 day = 1 numroom = 42 lastname = 218mark =  Васи� year = 6 month = 1990 day = 1 numroom = 42 lastname = �ьев

用结构制作二进制文件,尝试打印出所有包含的内容..

仅 scanf/printf/FILE/struct

这是一个代码...

实验室.h

#pragma once
    void input();
    void find();
    int getdays(int year, int month);
    void correction();
    void print();

实验室.cpp

#include "Lab.h"
#include <stdio.h>      //FILE
#include <iostream>
#include <conio.h>      //getch
#include <windows.h>
#include <io.h>
struct Computer
{
    wchar_t mark[11];
    int year;
    int month;
    int day;
    unsigned char numroom;
    wchar_t lastname[20];
};
void input()
{
    FILE *inputFile, *outputFile;
    fopen_s(&outputFile, "output.dat", "wb");
    fopen_s(&inputFile, "input.txt", "r");
    Computer c;
    while (fgetws(c.mark, 11, inputFile))
    {
        fscanf_s(inputFile, "%d", &c.year);
        fscanf_s(inputFile, "%i", &c.month);
        fscanf_s(inputFile, "%i", &c.day);
        fscanf_s(inputFile, "%hhu", &c.numroom);
        fwscanf_s(inputFile, L"%s", c.lastname, _countof(c.lastname));
        fwrite(&c, sizeof(struct Computer), 1, outputFile);
    }
    _fcloseall();
    return;
}
void find()
{
    FILE *outputFile;
    fopen_s(&outputFile, "output.dat", "rb+");
    Computer c;
    while (fread(&c, sizeof(struct Computer), 1, outputFile))
    {
        if (c.year == 1995 && wcscmp(L"IBM HC-486", c.mark) == 0)
        {
            wprintf_s(L"\nmark = %s year = %i month = %i day = %i numroom = %i lastname = %s", 
                c.mark, c.year, c.month, c.day, c.numroom, c.lastname);
            _getch();
            _fcloseall();
            return;
        }
    }
    _getch();
    return;
}

int getdays(int year, int month)
{
    int days = 0;
    if (month == 4 || month == 6 || month == 9 || month == 11)
        days = 30;
    else if (month == 2)
    {
        bool leapyear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
        if (leapyear == 0)
            days = 28;
        else
            days = 29;
    }
    else
        days = 31;
    return days;
}

void correction()
{
    FILE* outputFile;
    fopen_s(&outputFile, "output.dat", "rb+");
    fseek(outputFile, 0, 0);
    Computer c;
    long item = 0;
    while (fread(&c, sizeof(struct Computer), 1, outputFile))
    {

        while (c.month < 1 || c.month > 12)
        {
            wprintf_s(L"mark = %s year = %i month = %i day = %i numroom = %i lastname = %s",
                c.mark, c.year, c.month, c.day, c.numroom, c.lastname);
            wprintf_s(L"%s%i", L"Некорректный номер месяца \nПожалуйста введите другой номер месяца:", c.month);
            scanf_s("%i", &c.month);
            fseek(outputFile, item * sizeof(struct Computer), 0);
            fwrite(&c, sizeof(struct Computer), 1, outputFile);
        }
        while (c.day < 1 || c.day > getdays(c.year, c.month))
        {
            wprintf_s(L"mark = %s year = %i month = %i day = %i numroom = %i lastname = %s",
                c.mark, c.year, c.month, c.day, c.numroom, c.lastname);
            wprintf_s(L"%s%i", L"Некорректный номер дня\nПожалуйста введите другой номер дня:", c.day);
            scanf_s("%i", &c.day);
            fseek(outputFile, item * sizeof(struct Computer), 0);
            fwrite(&c, sizeof(struct Computer), 1, outputFile);
        }
        item += 1;
    }
    _getch();
    _fcloseall();
    return;
}
void print()
{
    FILE* outputFile;
    fopen_s(&outputFile, "output.dat", "rb+");
    fseek(outputFile, 0, SEEK_SET);
    Computer c;
    while (fread(&c, sizeof(struct Computer), 1, outputFile))
    {
        wprintf_s(L"mark = %s year = %d month = %i day = %i numroom = %i lastname = %s",
            c.mark, c.year, c.month, c.day, c.numroom, c.lastname);
    }
    _getch();
    _fcloseall();
    return;
}

Lab2.cpp

#include <windows.h>
#include "Lab.h"
int main()
{
    SetConsoleCP(65001);
    SetConsoleOutputCP(65001);

    input();
    print();

    //find();
    //correction();
    return 0;
}

【问题讨论】:

  • fgetws(c.mark, 11, inputFile);c.mark 只能容纳 9 个字符 + '\0'
  • 你显示的不像"二进制文件",你希望文本格式的提取能够正确读取这些值。
  • 你没有检查 fscanf_s 的返回值。
  • 您似乎正在使用 C 函数而不是 size -= sizeof(struct Computer); 位读取格式化输入,这将失败,因为文件的大小几乎可以保证与记录数不匹配 sizeof(struct Computer) .
  • @JohnnyMopp 谢谢

标签: c++ c visual-c++


【解决方案1】:

这样做有两个主要问题。 Johnny Mopp 已经指出了第一个问题,因为您对 fgetws 的调用需要 11 个元素的最小大小为 c.mark,因此您将其溢出。

关于为什么您将 0 读作年份,这是由于此溢出以及您尝试手动将 NULL 终止符添加到 c.mark 的事实:

c.mark[wcslen(c.mark) - 1] = '\0';

由于您已经溢出 c.mark,这恰好进入 c.year 并将其设置为 0(尝试在阅读 c.mark 之后立即放置此行,您会看到您阅读了正确的年份)。

事实上,这不是必需的,因为fgetws 已经包含了 NULL 终止符(您的调用将只读取 10 个字符并添加 '\0' 作为字符 11。

然后,请考虑到您尝试添加 NULL 终止符肯定会失败,因为 wcslen 除非已经存在 NULL 终止符,否则您将尝试在已经存在的地方设置 NULL 终止符一。此外,由于-1,您正在删除字符串中的最后一个字符。

假设您有一个只有一个字符 L"A" 的字符串。如果您进行该操作,wcslen 将返回 1,如果您减去 1,您正在执行c.str[0] = L'\0',从而将字符串转换为 L""。在这种情况下,最好使用 sizeof 而不是 wcslen,因为无论内容如何,​​它都会返回 11,并且减去 1 你会得到 c.str[10] = '\0',这就是你真正想要的。

不过,正如我之前所说,这是不必要的,因为 fgetws 已经为您处理了 NULL 终止符(请查看 https://docs.microsoft.com/es-es/cpp/c-runtime-library/reference/fgets-fgetws?view=msvc-160 的备注部分)。

更新

关于何时结束阅读的决定,我通常会阅读直到我用完数据,而不管文件大小。这意味着使用while (true) 使循环永远运行,并检查fgetwsfscanf 的输出,正如其他人所建议的那样。如果您查看fgetws 的文档(我之前写的链接),您可以在返回值部分看到它在成功时返回指向缓冲区的指针(这通常没有用)但它返回 NULL 以防万一错误或文件结尾。如果在读取标记时出现错误,您可以使用它来中断循环:

if (fgetws(c.mark, 11, inputFile) == NULL)
    break;

同样,fscanf_s 在发生错误或文件结束 (https://docs.microsoft.com/es-es/cpp/c-runtime-library/reference/fscanf-s-fscanf-s-l-fwscanf-s-fwscanf-s-l?view=msvc-160) 的情况下返回 EOF,因此您可以在使用 fscanf_s 读取值时添加该条件。例如:

if (fscanf_s(inputFile, "%d", &c.year) == EOF)
    break;

其余的也是如此。或者您可以只使用fgetws 中的条件,但是如果您的行不完整(其中fgetws 成功但一个或多个fscanf_s 失败),这可能会导致记录损坏。最后,这一切都归结为您想要投入多少工作,以及您希望您的代码对无效输入有多大的弹性。

【讨论】:

  • 我更新了答案以包含一些关于它的建议
猜你喜欢
  • 1970-01-01
  • 2015-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多