-1

I have this defensive-programming issue that I don't really know how to solve.

I have this function that takes file path and size of table (rows / columns count) as arguments and I'm looking for better way of validating the input file. I assume that arguments of this function are always correct. size represents the "smaller side" of the table that is stored in file:

For example :

1 2 3 4 
5 6 7 8 

size = 2 is correct while

1 2 3 4 5
5 6 7 8 9

size = 2 is incorrect

I'd also like to be able to reject the files like this

1 2 3 4 5 6 7 8

size = 2 (which is accepted through fscanf)

Another type of file I'd like to be able to reject is

1 2 3
4 5 6

size = 2

As for now my only security is checking if the elements of the file are really numbers.

Here is the code I've done so far :

void import(float** table, int size, char* path)
{
    FILE* data = fopen(path, "r");
    assert(data);
    int i,j;
    int st;

    for (i=0; i<size; i++)
    {
        for(j=0; j<(size*2)-1; j++)
        {
            st = fscanf(data, "%f", &table[i][j]);
            if (!st)
            {
                printf("Error while importing the file.\n");
                fclose(data);
                return -1;
            }
        }
    }
    fclose(data);
}

I don't really where and how to start, I'm not really proficient in C and there seems to exist a lot of functions and mechanisms to do what I want but they all seem very complex and some are actually longer than the code I provided.

If anyone can point me in the right direction that'd be great.

Sword22
  • 266
  • 3
  • 15
  • What does *"number = 2"* mean? – LihO Feb 20 '12 at 21:15
  • 1
    I don't think I understand what a correct file looks like. All I can see is that `2` is always right. – cnicutar Feb 20 '12 at 21:15
  • @LihO the input number, it's the size in the code – Sword22 Feb 20 '12 at 21:16
  • @Sword22: I understand you now. Let me rewrite your question. – LihO Feb 20 '12 at 21:19
  • 1
    This is absolutely the wrong way to use assert. assert is NOT for error checking, but for logical consistency. In other words, you could write: data = open( ... ); if( data == NULL ) { ... } assert( data != NULL );, but calling assert after open is not valid error checking. – William Pursell Feb 20 '12 at 21:30
  • @William Pursell : Could you please expand a little bit on that? I've always been told that I should assert my mallocs and file opening. – Sword22 Feb 20 '12 at 21:46
  • 1
    @Sword22 You have been told wrong. You should always check your mallocs and your file openings, but you should not do so with assert. Assert is for stating logical necessity or parameter assumptions. (eg, int foo( int *x ) { assert( x ) ... } documents that foo must not be called with a NULL pointer.) – William Pursell Feb 20 '12 at 21:50

3 Answers3

1

You can't readily detect ends of lines in scanf(), so using that directly is not going to meet your criteria.

You probably need to read entire lines (fgets() or perhaps getline()), and then process each line in turn. The line processing can use sscanf(), and you can use the %n directive too. In outline, this boils down to:

for (line_num = 0; line_num < size; line_num++)
{
    ...read line from file into buffer line, checking for EOF...
    start = line;
    for (i = 0; i < 2 * size; i++)
    {
        if (sscanf(start, "%f%n", &value, &offset) != 1)
            ...ooops - short line or non-numeric data...
        else
        {
            start += offset;
            table[line_num][i] = value;
        }
    }
}
...check that there's no clutter after the last expected line...
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1

Your for loop could look like this:

char line[1000], *token;
for (i = 0; i < size; i++) // for each line
{
    if (fgets(line, 1000, data) != NULL) // read line
    {
        token = strtok (line," ");
        for (j = 0; j < (size * 2) - 1; j++) // for each number from line
        {
            if (sscanf(token, "%f", &table[i][j]) <= 0)
            {
                // there are columns missing:
                printf("Error while importing the file.\n");
                fclose(data);
                return -1;
            }
            token = strtok (NULL," ");
        }
    }
    else
    {
        // there are rows missing:
        printf("Error while importing the file.\n");
        fclose(data);
        return -1;
    }
}

Also note that assert(data); should be replaced with something like this:

if (!data)
{
    printf("Error while openning the file [filePath=\"%s\"].\n", filePath);
    cleanExit();
}
LihO
  • 41,190
  • 11
  • 99
  • 167
  • @Sword22: Sorry, I had an error there. You can't read this input directly from `line`, you have to split it into tokens. Check my answer now. – LihO Feb 20 '12 at 23:16
0

You could also calculate a checksum of the entire file. The question is how serious you are about it. It's easy to create an xor checksum, but it's not really safe against collisions. The best is probably to use something like sha-1 if it's important.

foo
  • 387
  • 2
  • 9