C 从二进制文件写入/读取数据
C Write / Read Data From Binary File
更新
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
这是一个代码...
Lab.h
#pragma once
void input();
void find();
int getdays(int year, int month);
void correction();
void print();
Lab.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;
}
这有两个主要问题。第一个已经被 Johnny Mopp 指出,因为你对 fgetws 的调用需要至少 c.mark
的 11 个元素,所以你溢出了它。
关于为什么将 0 读作年份,这是由于此溢出以及您试图手动将 NULL 终止符添加到 c.mark
:
c.mark[wcslen(c.mark) - 1] = '[=10=]';
因为你已经溢出 c.mark
,这恰好进入 c.year
并将其设置为 0(尝试在阅读 c.mark
后立即放置此行,你会看到你读对了年份)。
事实上,这不是必需的,因为 fgetws
已经包含 NULL 终止符(您的调用将只读取 10 个字符并添加字符 11 '\0'。
Event 然后,考虑到你尝试添加 NULL 终止符的尝试注定会失败,因为 wcslen
不起作用,除非已经有一个 NULL 终止符,所以你正在尝试设置一个 NULL 终止符已经有一个的地方。此外,由于 -1
.
,您要删除字符串中的最后一个字符
假设您有一个只有一个字符 L"A" 的字符串。如果您执行该操作,wcslen
将 return 1,如果您减去 1,您将执行 c.str[0] = L'[=22=]'
,从而将字符串转换为 L""。在这种情况下,最好使用 sizeof
而不是 wcslen,因为它会 return 11
而不管内容如何,减去 1 你会得到 c.str[10] = '[=25=]'
这就是你真的想要。
然而,正如我之前所说,这是不必要的,因为 fgetws
已经为您处理了 NULL 终止符(请查看 https://docs.microsoft.com/es-es/cpp/c-runtime-library/reference/fgets-fgetws?view=msvc-160 的备注部分)。
更新
关于什么时候结束阅读的决定,我一般都是读到运行没数据为止,不管文件大小。这意味着使用 while (true)
永远循环 运行,并检查 fgetws
和 fscanf
的输出,正如其他人所建议的那样。如果您查看 fgetws
(我之前写的 link)的文档,您可以在 Return 值部分看到它 return 是指向缓冲区的指针成功(这通常没有用)但它 returns NULL 在错误或文件结束的情况下。如果在读取标记时出现错误,可以使用它来中断循环:
if (fgetws(c.mark, 11, inputFile) == NULL)
break;
类似地,fscanf_s
returns EOF
以防出现错误或文件结束 (https://docs.microsoft.com/es-es/cpp/c-runtime-library/reference/fscanf-s-fscanf-s-l-fwscanf-s-fwscanf-s-l?view=msvc-160),因此您可以随时添加该条件您使用 fscanf_s
读取了一个值。例如:
if (fscanf_s(inputFile, "%d", &c.year) == EOF)
break;
其余的也是如此。或者您可以只使用 fgetws
中的条件,但是如果您有不完整的行(其中 fgetws
成功但一个或多个 fscanf_s
失败),这可能会导致记录损坏。最后,这一切都归结为您想要投入多少工作以及您希望您的代码对无效输入有多大的弹性。
更新
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
这是一个代码...
Lab.h
#pragma once
void input();
void find();
int getdays(int year, int month);
void correction();
void print();
Lab.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;
}
这有两个主要问题。第一个已经被 Johnny Mopp 指出,因为你对 fgetws 的调用需要至少 c.mark
的 11 个元素,所以你溢出了它。
关于为什么将 0 读作年份,这是由于此溢出以及您试图手动将 NULL 终止符添加到 c.mark
:
c.mark[wcslen(c.mark) - 1] = '[=10=]';
因为你已经溢出 c.mark
,这恰好进入 c.year
并将其设置为 0(尝试在阅读 c.mark
后立即放置此行,你会看到你读对了年份)。
事实上,这不是必需的,因为 fgetws
已经包含 NULL 终止符(您的调用将只读取 10 个字符并添加字符 11 '\0'。
Event 然后,考虑到你尝试添加 NULL 终止符的尝试注定会失败,因为 wcslen
不起作用,除非已经有一个 NULL 终止符,所以你正在尝试设置一个 NULL 终止符已经有一个的地方。此外,由于 -1
.
假设您有一个只有一个字符 L"A" 的字符串。如果您执行该操作,wcslen
将 return 1,如果您减去 1,您将执行 c.str[0] = L'[=22=]'
,从而将字符串转换为 L""。在这种情况下,最好使用 sizeof
而不是 wcslen,因为它会 return 11
而不管内容如何,减去 1 你会得到 c.str[10] = '[=25=]'
这就是你真的想要。
然而,正如我之前所说,这是不必要的,因为 fgetws
已经为您处理了 NULL 终止符(请查看 https://docs.microsoft.com/es-es/cpp/c-runtime-library/reference/fgets-fgetws?view=msvc-160 的备注部分)。
更新
关于什么时候结束阅读的决定,我一般都是读到运行没数据为止,不管文件大小。这意味着使用 while (true)
永远循环 运行,并检查 fgetws
和 fscanf
的输出,正如其他人所建议的那样。如果您查看 fgetws
(我之前写的 link)的文档,您可以在 Return 值部分看到它 return 是指向缓冲区的指针成功(这通常没有用)但它 returns NULL 在错误或文件结束的情况下。如果在读取标记时出现错误,可以使用它来中断循环:
if (fgetws(c.mark, 11, inputFile) == NULL)
break;
类似地,fscanf_s
returns EOF
以防出现错误或文件结束 (https://docs.microsoft.com/es-es/cpp/c-runtime-library/reference/fscanf-s-fscanf-s-l-fwscanf-s-fwscanf-s-l?view=msvc-160),因此您可以随时添加该条件您使用 fscanf_s
读取了一个值。例如:
if (fscanf_s(inputFile, "%d", &c.year) == EOF)
break;
其余的也是如此。或者您可以只使用 fgetws
中的条件,但是如果您有不完整的行(其中 fgetws
成功但一个或多个 fscanf_s
失败),这可能会导致记录损坏。最后,这一切都归结为您想要投入多少工作以及您希望您的代码对无效输入有多大的弹性。