cscanffopenfreadfclose

Reading variables via fread and storing via sscanf


I have a file .txt with values of some variable. I need to read them to declarate my variables in main. What is wrong?

#include <stdio.h>
#define INPUT "input.txt"

int main (void){

    FILE *open_file = fopen(INPUT, "r");

    if (open_file == NULL){
        puts("ERROR.");
    } else {
        puts("SUCCESS.");
    }

    char *buffer;
    int size = ftell(open_file); 
    int read_file = fread(buffer,size,*open_file);
    int Integer1, Integer2;
    while (size != EOF){
        sscanf("%d%d",Integer1, Integer2);
    }


    int close_file = fclose(open_file);
    if (close_file == -1){
        puts("Error in closing file.");
    } else {
        puts("Closing file: SUCCESS.");
    }


    return 0;
}

Whats is wrong? I have to read every line of my file. For example if my file contains:

1
2

My scanf should set:

Integer1 = 1; 
Integer2 = 2;

Solution

  • One problem is that after the line

    char *buffer;

    the variable buffer does not point to any valid memory location. It is a wild pointer.

    You can either create a fixed size array like this:

    char buffer[100];

    or you can create a dynamically sized memory buffer, like this:

    if ( fseek( open_file, 0, SEEK_END ) != 0 )
        DoSomethingToHandleError();
    long size = ftell(open_file);
    if ( fseek( open_file, 0, SEEK_SET ) != 0 )
        DoSomethingToHandleError();
    char *buffer = malloc( size );
    if ( buffer == NULL )
        DoSomethingToHandleError();
    

    Depending on whether you used a fixed-size array or a dynamically allocated buffer, you should change the call to fread to one of the following:

    fread( buffer, sizeof(buffer), open_file ); //for fixed size array

    fread( buffer, size, open_file ); //for dynamically allocated buffer

    Instead of using the function fread, you would probably be better off using the function fgets, especially because sscanf requires a null-terminated string as input, not binary data. The function fread will give you binary data that is not null-terminated, whereas fgets will give you a null-terminated string.

    Also, change the line

    sscanf("%d%d",Integer1, Integer2);

    to

    sscanf( buffer, "%d%d", &Integer1, &Integer2);

    Before using Integer1 and Integer2 afterwards, you should also check the return value of sscanf to make sure that both integers were found in the string.

    However, if you don't want to handle the memory management and reading in of the file yourself, you can simply use fscanf, like this:

    if ( fscanf( open_file, "%d%d", &Integer1, &Integer2 ) != 2 )
        DoSomethingToHandleError();
    

    But this has the disadvantage that fscanf will just give you the first two numbers that it finds in the file, and won't perform much input validation. For example, fscanf won't enforce that the two numbers are on separate lines. See the following link for more information on the disadvantages of using fscanf:

    A beginners' guide away from scanf()

    If you use the function fgets as I suggested, then you will need two calls to fgets to read both lines of the file. This means that sscanf will be unable to find both integers in the same string. Therefore, if you use fgets, then you will have to change your program logic a bit, for example like this:

    #define MAX_INTEGERS 2
    char buffer[100];
    int integers[MAX_INTEGERS];
    int num_found = 0;
    
    while ( fgets( buffer, sizeof(buffer), open_file ) != NULL )
    {
        int i;
    
        if ( strchr( buffer, '\n' ) == NULL && !feof( openfile ) )
        {
            printf( "Error: Line size too long! Aborting.\n" );
            break;
        }
    
        if ( sscanf( buffer, "%d", &i ) == 1 )
        {
            if ( num_found == MAX_INTEGERS )
            {
                printf(
                    "Error: Found too many integers in file! This "
                    "program only has room for storing %d integers. "
                    "Aborting.\n",
                    MAX_INTEGERS
                );
                break;
            }
    
            //add found integer to array and increment num_found
            integers[num_found++] = i;
        }
        else
        {
            printf(
                "Warning: Line without number encountered. This could "
                "simply be a harmless empty line at the end of the "
                "file, but could also indicate an error.\n"
            );
        }
    }
    
    printf( "Found the following integers:\n" );
    
    for ( int i = 0; i < num_found; i++ )
    {
        printf( "%d\n", integers[i] );
    }
    

    Instead of using sscanf, you may want to use strtol, as that function allows you to perform stricter input validation.

    If you don't want to use the function fgets, which reads input line by line, but really want to read the whole file at once using fread, you can do that, but you would have to add the terminating null character manually.


    EDIT: Since you stated in the comments that you didn't want a loop and wanted to store the individual lines each into their own named variable, then you could use the following code instead:

    //This function will assume that one number is stored per line, and
    //write it to the memory location that "output" points to. Note that
    //the return value does not specify the number found, but rather
    //whether an error occurred or not. A return value of 0 means no error,
    //nonzero means an error occurred.
    
    int get_number( FILE *fp, int *output )
    {
        char buffer[100];
        int i;
    
        //read the next line into buffer and check for error
        if ( fgets( buffer, sizeof(buffer), fp ) == NULL )
            return -1;
    
        //make sure line was not too long to fit into buffer
        if ( strchr( buffer, '\n' ) == NULL && !feof( fp ) )
            return -1;
    
        //parse line and make sure that a number was found
        if ( sscanf( buffer, "%d", &i ) != 1 )
            return -1;
    
        *output = i
        return 0;
    }
    

    Now, in your function main, you can simply use the following code:

    int error_occurred = 0;
    
    if ( get_number( &Integer1 ) != 0 )
    {
        printf( "Error occured when reading the first integer.\n" );
        error_occurred = 1;
    }
    
    if ( get_number( &Integer2 ) != 0 )
    {
        printf( "Error occured when reading the second integer.\n" );
        error_occurred = 1;
    }
    
    if ( !error_occurred )
    {
        printf( "The value of Integer1 is: %d\n", Integer1 );
        printf( "The value of Integer2 is: %d\n", Integer2 );
    }