-2

i am new in c. So in my university, i just learn about file in c. and i got a task. If i put an empty file in my project directory, and read it. The output are symbols (i dont know what symbol it is). So here is the code, please help

player dota[100];
FILE *fp;
fp = fopen("soal09.txt", "r");
if(fp == NULL)
{
    printf("Error Opening The File!!\n");
    return 0;
}
else
{
    while(!feof(fp))
    {
        fscanf(fp, "%[^ ] %d %d\n", &dota[idx].name, &dota[idx].score, &dota[idx].num);
        idx++;
    }
}
fclose(fp);

do
{
    enter();
    menu();
    printf("Input your choice [1..5]: ");
    scanf("%d", &choose); fflush(stdin);

    if(choose == 1)
    {
        system("cls");
        enter();
        printf("%-20s %-15s     %s\n", "Player Name", ": Average Score", ": Number of Playing");
        printf("====================================================================\n");
        for(int i = 0; i < idx; i++)
        {
            printf("%-20s %-15d     %d\n", dota[i].name, dota[i].score, dota[i].num);
        }
        printf("\nPress Enter to Continue...");
        getchar();
    }

getchar();
return 0;

}

and the output is ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ -858993460 Thank you ^^

  • 4
    See [why while (!feof(file)) is always wrong](http://stackoverflow.com/q/5431941/62576) – Ken White Dec 18 '16 at 05:58
  • 1
    And the alternative to `while (!feof(file))` is to *check the return values of your function calls*, which in fact you should do for **every** function call whose return value conveys status information about which you care. – John Bollinger Dec 18 '16 at 06:11
  • ╠ is 0xCC in codepage 437, and [MSVC fills 0xCC to uninitialized memory to help debugging](https://stackoverflow.com/q/370195/995714). That means you've accessed uninitialized memory. You can find tons of questions about ╠ and 0xCC here on SO – phuclv Aug 18 '18 at 10:54

1 Answers1

0

The end-of-file indicator that is checked by feof() is only set after a previous file I/O operation has failed. You must attempt an I/O operation to find out if you have reached the end of the file. So, with an empty file, your code attempts to read the file, the end-of-file indicator is set, no values are read into the first struct, but idx is incremented, so it looks like you have added data for a player. But the fields of the first struct are uninitialized, so you are seeing garbage. Also note that dota[idx].name is presumably an array of chars, so it decays to a pointer to char when passed to fscanf(). Using &dota[idx].name, as you have, is wrong, though it might appear to work. It does cause the compiler to emit a warning, and you should have these enabled (I always use at least gcc -Wall -Wextra -pedantic).

You should not use feof() to control file I/O loops. One simple solution is to use the return value of fscanf() to control the loop:

while(fscanf(fp, "%[^ ] %d %d\n",
             dota[idx].name, &dota[idx].score, &dota[idx].num) == 3) {
    idx++;
}

This will only update a player struct if three assignments are made by the call to fscanf(). But, the problem with this simple solution is that it doesn't handle malformed input well. If a line of the data file is missing a field, the struct will be incorrectly filled, and the loop will terminate, even if there are more lines in the file to read. Also, since no field width is specified for the string conversion, a long name could crash your program.

A better solution uses fgets() to read the lines of the file into a buffer, and then uses sscanf() to extract the information from the buffer:

#include <string.h>        // for strcpy()

...

char buffer[1000];
int line = 0;
char temp_name[100];
int temp_score, temp_num;

while(fgets(buffer, sizeof(buffer), fp)) {
    ++line;

    if (sscanf(buffer, "%99[^ ] %d %d\n",
               temp_name, &temp_score, &temp_num) == 3) {
        strcpy(dota[idx].name, temp_name);
        dota[idx].score = temp_score;
        dota[idx].num = temp_num;
        ++idx;
    } else if (buffer[0] != '\n') {
        fprintf(stderr, "Bad input in line %d\n", line);
    }
}

Here, a generous buffer is declared to accept a line of text from the file, and temporary variables are declared to hold the values to be stored in the struct fields. I have chosen a size of 100 for temp_name, but this should match the size of the string array in your actual struct. Note that the string conversion in sscanf() has a field width of 99, so that at most 99 non-space (not non-whitespace, and why aren't you just using %99s here?) characters are matched, leaving space for the '\0' to be added.

fgets() will return NULL when it reaches the end of the file, so the loop will continue until that happens. For each line that is read, a line counter is incremented. Then sscanf() is used to read data into the temporary variables. The value returned from sscanf() is checked to be sure that 3 assignments were made, and if so, then the data is copied into the struct, and idx is incremented. Note that strcpy() is needed to copy the string from temp_name to dota[idx].name.

If the value returned from sscanf() indicates that something other than 3 assignments were made, there is a check to see if buffer holds an empty line. If not, an error message is printed to stderr providing the line number of the bad input in the file.

A couple of further comments. Your do loop appears to be missing the associated while(). And you use fflush(stdin) after the scanf() inside the do loop. fflush()is meant to flush output streams. The behavior of fflush() on input streams is explicitly undefined in the C Standard (ISO/IEC 9899:2011 7.21.5.2/2), though I believe that Microsoft deviates from the Standard here. Nevertheless, you should not use fflush(stdin) in portable C code. Instead, use something like this:

int c;

...

scanf("%d", &choose);
while ((c = getchar()) != '\n' && c != EOF)
    continue;                  // discard extra characters

This code reads characters from the input stream until either a '\n' or EOF is reached, clearing any characters left behind by the previous call to scanf() from the input stream.

Community
  • 1
  • 1
ad absurdum
  • 19,498
  • 5
  • 37
  • 60