简体   繁体   中英

C Write / Read Data From Binary File

UPDATE

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

So i kinda try to read two records. First one fits out well. Second looks bad. I kinda fixed suggestions thanks a lot it helped to move forward. So for now i stuck on outputing two records. Result is ->

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 = �ьев

Making a binary file out of structs, attempting to print out all cointaining..

ONLY scanf/printf/FILE/struct

Here's a code...

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;
}

There are two main problems with that. The first one has already been pointed out by Johnny Mopp, in that your call to fgetws requires a minimum size of c.mark of 11 elements, so you are overflowing it.

And regarding why you read 0 as the year, it's due to this overflow and the fact that you are trying to manually add the NULL terminator to c.mark :

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

As you are already overflowing c.mark , this happens to go right into c.year and sets it to 0 (try putting this line right after reading c.mark and you will see that you read the correct year).

In fact, this is not necessary because fgetws already includes the NULL terminator (your call will only read 10 characters and add as character 11 the '\0'.

Event then, take into account that your attempt to add the NULL terminator is bound to fail, because wcslen does not work unless there is already a NULL terminator, so you are trying to set a NULL terminator where there is already one. Besides, you are removing the last character in the string due to the -1 .

Imagine that you have a string with only one character L"A". If you make that operation, wcslen will return 1 and if you substract 1, you are doing c.str[0] = L'\0' , thus converting the string to L"". In this case, it would be better using sizeof instead of wcslen, because it would return 11 regardless of the content, and substracting 1 you would get c.str[10] = '\0' which is what you really want.

Nevertheless, as I said before, it's unnecessary because fgetws already takes care of the NULL terminator for you (take a look at the Remarks section of https://docs.microsoft.com/es-es/cpp/c-runtime-library/reference/fgets-fgetws?view=msvc-160 ).

UPDATE

Regarding the decission on when to end reading, I usually read until I run out of data, regardless of the file size. That would mean making the loop run forever with while (true) , and checking the output of the fgetws and fscanf , as others have suggested. If you take a look at the documentation of fgetws (the link I wrote before) you can see in the Return value section that it returns a pointer to the buffer on success (this is not useful normally) but it returns NULL in case of an error or end-of-file. You can use this to break the loop if there is an error when you read mark by doing:

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

Similarly, fscanf_s returns EOF in case of an error or end-of-file ( https://docs.microsoft.com/es-es/cpp/c-runtime-library/reference/fscanf-s-fscanf-sl-fwscanf-s-fwscanf-sl?view=msvc-160 ), so you could possibly add that condition whenever you read a value using fscanf_s . For instance:

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

And so with the rest. Or you could go just with the condition in fgetws , but that could lead to corrupt records if you have incomplete lines (where the fgetws succeeds but one or more of the fscanf_s fails). In the end it all boils down to how much work you want to put and how resilient do you want your code to be against invalid inputs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM