0

I'm trying to read the data from .txt file into struct array and then writing some of this data into another .txt file using said array.

My code:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX 5 // max array size
//C:\\Users\\PATOX\\Desktop\\uczniowie.txt
 struct para
{
    char name[15];
    int grade;
};

int read(FILE* file, struct para tab[], int m,  int *k) {
    FILE* output;
    char path[80];
    printf("Enter input file path: ");
    scanf_s("%s", path, 80);
    file = fopen(path, "r");
    if (file == NULL)
    {
        printf("\nError opening input file\n");
        return 1;
    }
    else
    {
        char path[80];
        printf("Enter output file path: ");
        scanf_s("%s", path, 80);
        output = fopen(path, "w");
        if (output == NULL)
        {
            printf("\nError opening output file\n");
            return 1;
        }
        for (int i = 0; i < m; i++)
        {
            if (!feof(file))
            {
                int index;
                char name[15];
                int grade;
                fscanf(file, "%d %s %d", &index, name, &grade);
                strcpy(tab[i].name, name);
                tab[i].grade = grade;
                fwrite(&(tab[i]), sizeof(struct para), 1, output);
                (*k)++;
            }
        }
    }
    fclose(file);
    fclose(output);
    return 0;
}



void print_array(struct para tab[], int m) {
    for (int i = 0; i < m; i++)
    {
        printf("%s %d\n", tab[i].name, tab[i].grade);
    }

}


int main() {
    int k = 0;                  //data counter
    FILE* file;
    struct para tab[5];
    read(&file, tab, MAX, &k);
    print_array(tab, k);
    return 0;
}

When I check the output .txt file the data is here, however only .name is written as expected, the second item is written as some artifact:

Expected (output.txt)

Drozd 4   Jas 5   Tom 6   Bas 8   

Actual (output.txt) (output.txt)

Drozd ÌÌÌÌÌÌÌÌÌÌ   Jas ÌÌÌÌÌÌÌÌÌÌÌÌ   Tom ÌÌÌÌÌÌÌÌÌÌÌÌ   Bas ÌÌÌÌÌÌÌÌÌÌÌÌ   
Veanty
  • 33
  • 5
  • 1
    Do you know how to use the debugger? – Jonathan Wood Mar 10 '23 at 18:25
  • 4
    `fscanf` implies the in file is a text file. `fwrite` implies the out file is a binary data file. Which format do you want? – 001 Mar 10 '23 at 18:30
  • What @001 mentioned is key. – Ted Lyngmo Mar 10 '23 at 18:33
  • 2
    `if (!feof(file))` here is just as wrong as [this](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong). – n. m. could be an AI Mar 10 '23 at 18:37
  • 1
    Don't use `read` as a function name--it conflicts with a standard function (syscall) name. In `main`, we have `FILE *file;` Passing `&file` to `read` passes a `FILE **` to a parameter that is `FILE *`. This is just wrong (will produce compiler error). No need to pass this at all. Eliminate the argument and have a function scoped `FILE *file;` in `read`. It does `fopen` and then `fclose` so no need for `main` to know anything about this. – Craig Estey Mar 10 '23 at 18:40
  • @001 `fwrite` can be used for text files. It doesn't imply anything. When you need to write a substring for example, using `fwrite` makes perfect sense. Of course, passing it an `int` when you want text does not. – Emanuel P Mar 10 '23 at 18:53
  • tab[i].name[14] for example was never set to any value, so it has a random uninitialized value. Your compiler is filling uninitialized variables with this repeating pattern to make them easier to spot, but generally you're more likely to have random characters or leftover data. – user253751 Mar 10 '23 at 19:01
  • @CraigEstey Thanks for the answer, I've already fixed the issues. The FILE* file parameter is predetermined by the task I'm doing (the parameters of the function have to be said FILE* and the array). – Veanty Mar 11 '23 at 11:19
  • @001 I've wanted a text format and I've fixed it by using fprintf() function. – Veanty Mar 11 '23 at 11:21
  • @JonathanWood That doesn't answer my question and potentially makes fun of me for being a beginner. – Veanty Mar 11 '23 at 11:22
  • @Veanty: Maybe you shouldn't post questions online if you're going to be offended by pointed questions regarding how a more experienced developer would approach the issue. – Jonathan Wood Mar 13 '23 at 23:02

1 Answers1

1

In trying out your program experimenting with various methods of pushing the structure data to a text output file, I could not successfully produce an output data file file with just text data. My experience with outputting structures to a file, the file is usually defined as a binary file rather than a text file.

With that in mind, I did a bit of refactoring of your program to output the data as text data. Following is a listing of the refactored code.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX 20 // max array size
//C:\\Users\\PATOX\\Desktop\\uczniowie.txt
struct para
{
    char name[15];
    int grade;
};

int readx(FILE* filex, struct para tab[], int m,  int *k)
{
    FILE* output;
    char path[80];
    int check = 0;
    printf("Enter input file path: ");
    check = scanf("%s", path);
    filex = fopen(path, "r");
    if (filex == NULL)
    {
        printf("\nError opening input file\n");
        return 1;
    }
    else
    {
        char path[80];
        printf("Enter output file path: ");
        check = scanf("%s", path);
        output = fopen(path, "w");
        if (output == NULL)
        {
            printf("\nError opening output file\n");
            return 1;
        }
        for (int i = 0; i < m; i++)
        {
            if (!feof(filex))
            {
                int index;
                char name[15];
                int grade;
                check = fscanf(filex, "%d %s %d", &index, name, &grade);

                if (check < 0)  /* Exit loop if no more data is found */
                {
                    break;
                }

                strcpy(tab[i].name, name);
                tab[i].grade = grade;
                /* fwrite(&(tab[i]), sizeof(struct para), 1, output); */
                /* Use fprintf instead for text type output */
                fprintf(output, "%s %d\n", name, grade);
                (*k)++;
            }
        }
    }
    fclose(filex);
    fclose(output);
    return 0;
}

void print_array(struct para tab[], int m)
{
    for (int i = 0; i < m; i++)
    {
        printf("%s %d\n", tab[i].name, tab[i].grade);
    }
}

int main()
{
    int k = 0;                  //data counter
    FILE* filex = NULL;
    struct para tab[MAX];       /* Sized for maximum allowable structure size */
    readx(filex, tab, MAX, &k);
    print_array(tab, k);
    return 0;
}

Following are some items to note.

  • Rather than utilizing the "fwrite" function to output data to a file, the "fprintf" function was used instead with associated formatting to produce an output record for each student read.
  • In testing the rest of the program, it was evident that extraneous data was being output to the file as an additional record write was occurring after the last input record had been read, so a test of the "fscanf" function was added to provide a timely break from the read loop.

With that, the following file set was built for testing.

Grade Data

Following was the sample grade data.

1   Craig   88
2   Kelly   92
3   Kurt    78
4   Lisa    94
5   Benjamin    89
6   Troy    91
7   Isaac   97
8   William 78
9   Elsie   85
10  Winston 75

Following is sample terminal output testing this refactored code.

@Vera:~/C_Programs/Console/TextWrite/bin/Release$ ./TextWrite 
Enter input file path: Sample.txt
Enter output file path: Grades.txt
Craig 88
Kelly 92
Kurt 78
Lisa 94
Benjamin 89
Troy 91
Isaac 97
William 78
Elsie 85
Winston 75

Following is the text data that was placed into file "Grades.txt"

Craig 88
Kelly 92
Kurt 78
Lisa 94
Benjamin 89
Troy 91
Isaac 97
William 78
Elsie 85
Winston 75

So, to reiterate, if the output data should be in a textual format, probably the "fprintf" function would be used; otherwise, if the program truly needs to store each structure as a record, the program would want to utilize an output file defined as a binary file.

Hopefully, this refactored code meets the spirit of your project.

NoDakker
  • 3,390
  • 1
  • 10
  • 11
  • A binary write is required, but there are padding issues that render this method non-portable (and at times unusable between different versions of the same compiler). The C-standard does not specify where padding occurs in a struct, only that there will be none before the first member. To actually write binary data out n a portable fashion the data must be serialized (defining how the data is written so it can be read in the same manner). That said, for learning on a single box, writing and reading a struct (or array of them) works just fine. – David C. Rankin Mar 11 '23 at 07:46
  • Thanks for your answer. I've swapped fwrite() with fprintf() and it is now correctly displaying data in the text file. – Veanty Mar 11 '23 at 11:12