0

I would like to get the numeric values from a .csv file and store them in an array. My attempt can be seen here:

FILE *ifp;

ifp = fopen(DIREC,"r");
if(ifp == NULL){
        fprintf(stderr,"Input file not read.\n");
        exit(1);
    }

int rows = 0;
int columns = 0;

rows = readParts(ifp);
fseek(ifp, 0, SEEK_SET);
columns = readConfigs(ifp);
fseek(ifp, 0, SEEK_SET);
char arr[rows][columns];
arr[rows][columns] = malloc(sizeof(int)*rows*columns);

for (int i=0; i < rows; ++i){
        while(!feof(ifp)){
            int count = 0;
            int c = fgetc(ifp);

            if(c == '\n'){
                break;
            }
            if(isdigit(c)){
                arr[i][count] = c;
                count++;
            }
        }
    }
printf("First entry: %d", arr[0][0]);

fclose(ifp);

However, I am having issues with using fgetc() to read the integer values. I know it's grabbing the numbers as doing printf("%c",c); returns the correct results. However, when assigning c to arr, I am not assigning the correct value as fgetc() does not return the actual character value. I tried casting (int) c, but it did not change anything. I have looked into fnscanf(), but I am not sure how this would work since my .csv file also contains non-numerics. Is there a proper way to assign integer values to an array using fgetc()?

hkj447
  • 677
  • 7
  • 21
  • 4
    You will want to look at [**Why is while ( !feof (file) ) always wrong?**](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong) – David C. Rankin Jun 27 '19 at 21:21
  • Most systems nowadays use [ASCII encoding (link)](https://en.wikipedia.org/wiki/ASCII#Printable_characters) or [UTF-8](https://en.wikipedia.org/wiki/UTF-8) (which is a superset of ASCII). So, for example, what looks like a zero in your file is actually the number 48 decimal. To convert digits from ASCII to the numeric value, use `arr[i][count] = c - '0';` – user3386109 Jun 27 '19 at 21:34
  • 1
    This is wrong for several reasons: `arr[rows][columns] = malloc(sizeof(int)*rows*columns);`. – Barmar Jun 27 '19 at 21:37
  • 1
    You don't need to call `malloc()`, the declaration `char arr[rows][columns]` allocates space for the array locally. – Barmar Jun 27 '19 at 21:38
  • Yuor code doesn't parse numbers with multiple digits. It's putting each digit into a different element of the array. Is that really what you want? – Barmar Jun 27 '19 at 21:40
  • @Barmar Yes, that was an issue I realized and planned on working out a fix for it later. Would `fgets` be more appropriate in that case? – hkj447 Jun 27 '19 at 21:40
  • 2
    Use `fgets()` to read a whole line, then use `sscanf()` to parse the numbers. – Barmar Jun 27 '19 at 21:43
  • @Barmar, suggest using something similar to: `char *token = strtok( inputLine, "," ); if( token ) { arr[ r ] [ c ] = (int)strtol( token, NULL, 10 );` – user3629249 Jun 30 '19 at 05:31
  • since you want to use `fgetc()` each input needs to be read into an temp array, char by char, then NUL terminated, then use a function like: `strtol()` (so you can check if a actual integer was read.) to convert the array into an integer, – user3629249 Jun 30 '19 at 05:36
  • note that a *.csv file contains comma separated values. Therefore, after reading in a value (and stopping when a '\n' or ',' is encountered. Then, after processing the value, need to step past the ',' and/or '\n' before again reading in a value – user3629249 Jun 30 '19 at 05:39

1 Answers1

1

You have a number of problems. To begin with:

rows = readParts(ifp);
fseek(ifp, 0, SEEK_SET);        /* rewinds to beginning */
columns = readConfigs(ifp);     /* re-reads rows values as columns */
fseek(ifp, 0, SEEK_SET);        /* rewinds to beginning -- again */

Get rid of the fseek(ifp, 0, SEEK_SET); calls, leaving just:

rows = readParts(ifp);
columns = readConfigs(ifp);

Next:

char arr[rows][columns];

declares a variable length array, storage is fully reserved, and no allocation is needed. If you want to dynamically allocate, you will have to declare rows number of pointers, and then columns number of int per-pointer. (you can also declare a pointer to a VLA of int (*)[columns], but since columns is not a compile-time constant, that wouldn't be recommended). Remove arr[rows][columns] = malloc(sizeof(int)*rows*columns);

Next to read the file into your array, simply keep proper r (row) and c (column) counters and then read each integer in the file with fscanf (ifp, "%d", &arr[r][c]) (or use fgets() to read a line int a buffer and then parse the values from the line with sscanf)

For example:

int r = 0, c = 0;

while (fscanf (ifp, "%d", &arr[r][c++]) == 1) {
    /* check if row full */
    if (c == columns) {
        r++;            /* advance row count  */
        if (r == rows)  /* protect arr bounds */
            break;
        c = 0;          /* reset column count zero */
    }
}

(note: you mention .csv but have not provided any example input from the file. If the file is actually comma-separated, you will need to advance the read pointer past the comma (or until the '\n' or EOF is found) following each integer read within the loop)

At this point you have filled your array with each integer value in the file and you have done so ensuring that each of your stored row/column values remain within the bounds of your arr.

Let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • regarding: `fscanf (ifp, "%d", &arr[r][c])` 1) the array should be defined as: `int arr[ rows ][ columns ]; so there is enough room in each entry for an 'int' value 2) This fails to consume the comma between each integer entry, so will fail after the first call. – user3629249 Jun 30 '19 at 05:25
  • @user3629249 Yes, yes, that was understood. No sample data was provided. That is why there is a **note:** about that, 3rd paragraph from the bottom. It would matter whether the delimiters were a comma-only or comma preceded by whitespace as was shown a couple of days ago. – David C. Rankin Jun 30 '19 at 06:58