0

I've been struggling with this program for a long time. The goal is to read a simple .csv file without using libcsv. After researching and programming I've come up with this implementation. It's almost there but it fails just at the end.

I suspect the error is in the line with str2uint64_t(ptr, &int64_converted, &error); but I can't figure out why.

In case it might help, I've adapted this implementation from the one I've found in this web page: https://cboard.cprogramming.com/c-programming/47105-how-read-csv-file.html

By the way, the program can be compiled and called as: gcc -o q q.c && ./q file.csv

Where file.csv might be something like:

0,10,20,300,905
55,18,8,253,65
0,18,265,293,98
23,18,28,6675,86
677,20,28,293,100

The implementation:

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>


void str2uint64_t(const char *str, uint64_t *intConverted, int *error) 
{
    // Converts string to uint64_t    

    *intConverted = 0;
    *error = 0;

    const char *s = str;

    int sign = *s;

    char *end;
    errno = 0;
    const uint64_t sl = strtoull(str, &end, 10);

    if (end == str) 
    {
        //fprintf(stderr, "%s: not a decimal number\n", str);
        *error = 1;
    } 

    else if ('\0' != *end) 
    {
        //fprintf(stderr, "%s: extra characters at end of input: %s\n", str, end);
        *error = 1;
    } 

    else if (ERANGE == errno) 
    {
        //fprintf(stderr, "%s out of range of type uint64_t\n", str);
        *error = 1;
    } 

    else if (sign == '-') 
    {
        //fprintf(stderr, "%s negative\n", 0);
        //errno = ERANGE;
        *error = 1;
    }

    //return sl;
    *intConverted = sl;
}


void *newMatrix(size_t rows, size_t cols)
{ 
    return malloc (sizeof(uint64_t[rows][cols]));
}


void importMatrix(char CSVFilePath[], size_t rows, size_t cols, uint64_t matrix[rows][cols])
{
    size_t i, j;

    uint64_t int64_converted;
    int error = 0;


    FILE *CSVfile = fopen(CSVFilePath, "r");

    if (CSVfile == NULL)
    {
        perror("Error");
        exit(EXIT_FAILURE);
    }   

    char buffer[BUFSIZ], *ptr;

    for (i = 0; fgets(buffer, sizeof buffer, CSVfile); ++i)
    {
       for (j = 0, ptr = buffer; j < rows; ++j, ++ptr)
       {
            str2uint64_t(ptr, &int64_converted, &error);

            if (error == 0)
            {
                // From https://cboard.cprogramming.com/c-programming/47105-how-read-csv-file.html >> array[i][j] = (int)strtol(ptr, &ptr, 10);
                matrix[i][j] = int64_converted;
            }

            else
            {
                printf("Failed to import matrix\n");
                exit(0);
            }
         }
      }
      fclose(CSVfile);

      putchar('\n');
}


int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        fprintf(stderr, "Usage: ./<program> <file.csv>\n");
        exit(EXIT_FAILURE);
    }

    size_t rows = 5;
    size_t cols = rows;

    uint64_t (*matrix)[rows] = newMatrix(rows, cols);

    importMatrix(argv[1], rows, cols, matrix[rows][cols]);

    //////////////////////////////

    return 0;
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
CSLearner
  • 1
  • 1
  • 2
    How does it fail? What does it do wrong? – Sami Kuhmonen Jul 02 '19 at 15:32
  • It triggers one of the `if` conditions in that function, but I've been removing the `%` in the commented lines and none of the messages appear. I know it has to enter in one of those conditions as the `error` variables turns from 0 to 1, causing the program to stop. – CSLearner Jul 02 '19 at 15:39
  • 2
    Have you run it in a debugger? That would be the easiest way to know what’s happening – Sami Kuhmonen Jul 02 '19 at 15:43
  • No I haven't. I'm programming in Geany and using the command line (this is one of my first C programs and haven't used a debugger ever) – CSLearner Jul 02 '19 at 15:44

1 Answers1

1

the posted code, using gcc via geany results in:

gcc    -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11  -c "untitled.c" 

untitled.c: In function ‘main’:

untitled.c:114:39: warning: passing argument 4 of ‘importMatrix’ makes pointer from integer without a cast [-Wint-conversion]
     importMatrix(argv[1], rows, cols, matrix[rows][cols]);
                                       ^~~~~~

untitled.c:58:6: note: expected ‘uint64_t (*)[(sizetype)(cols)]’ but argument is of type ‘uint64_t {aka long unsigned int}’
 void importMatrix(char CSVFilePath[], size_t rows, size_t cols, uint64_t matrix[rows][cols])
      ^~~~~~~~~~~~

Compilation finished successfully

Both of the warnings are serious. The result is the compiler generates the wrong code. please Fix those warnings and edit your question.

This statement, in function: main():

importMatrix(argv[1], rows, cols, matrix[rows][cols]);

results in passing (for the 4th parameter) the contents of memory, 1 past the end of the array: matrix[][] The statement should be:

importMatrix( argv[1], rows, cols, matrix );

OT: regarding this statement:

fprintf(stderr, "Usage: ./<program> <file.csv>\n");

The program can be run under any name. Therefore suggest the statement be:

fprintf(stderr, "Usage: %s <file.csv>\n", argv[0]);

Then the actual execution name will be displayed

OT: regarding this statement:

return malloc (sizeof(uint64_t[rows][cols]));

strongly suggest using:

return malloc( sizeof( uint64_t ) * rows * cols );

Then in function: importMatrix() after calling: newMatrix()

this statement:

uint64_t (*matrix)[rows] = newMatrix(rows, cols);

is not correct, because the call to newMatrix() is NOT returning an array of pointers, so the statement should be similar to:

typedef matrixType uint64_t matrix[rows][cols]; 
matrixType * matrix = newMatrix(rows, cols);
user3629249
  • 16,402
  • 1
  • 16
  • 17