0

I'm trying to read a CSV file where I have just double values separated with a comma. I'm using the char *fgets(char *str, int n, FILE *stream) function to read the rows. In the code, to finish the do-while loop, I'm using the getc() method to read the next char, to find out if I've read the end of the file. The problem is, getc() method read the first character from the next line, so I'm losing data (as the first number in the next line loses one digit). As you can see, apart from the first row, all the first column entries lost their first characters.

What should I use to control my while loop? Or is there another method that I should use to read data from CSV files? Thank you very much

Data from my_file.csv:

3.0000,4.0000,5.0000,6.0000,7.0000
6.0000,5.0000,4.0000,3.0000,2.0000
9.0000,6.0000,3.0000,0.0000,-3.0000
12.0000,7.0000,2.0000,-3.0000,-8.0000
15.0000,8.0000,1.0000,-6.0000,-13.0000
18.0000,9.0000,0.0000,-9.0000,-18.0000

Actual output:

[enter image description here][1]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void getData(char *buff);

int main() {

    char folder_addr[] = "C:\\my_file.csv";

    FILE *fp = fopen(folder_addr, "r");
    do  {
        char buff[1024];
        fgets(buff, 1024, (FILE*)fp);
        printf(buff);
        getData(buff);
    } while ((getc(fp)) != EOF);

    return 0;
};

void getData(char *buff){
    char *token = strtok(buff, ",");
    //printf("First value:  %s\n", token);

    while (token != NULL)   {
        //printf("First value:  %s\n", token);
        token = strtok(NULL, ",");
    }
}


  [1]: https://i.stack.imgur.com/fQzw1.jpg
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Oguz Canbek
  • 61
  • 1
  • 7
  • 1
    Are you familiar with `ungetc()`? – Shawn Mar 31 '19 at 21:37
  • Or better yet, since it fixes multiple issues, just check what `fgets()` returns instead of assuming it works. – Shawn Mar 31 '19 at 21:38
  • Also `printf(buff);` should be `fputs(buff, stdout);` to prevent issues with undefined behavior if `buff` has % characters in it. – Shawn Mar 31 '19 at 21:42
  • `fgets(..., (FILE*)fp)` --- `fp` is a value of `FILE*` type. The explicit conversion is unnecessary, redundant, and confusing. – pmg Mar 31 '19 at 21:44
  • You know how many columns csv file has. Why do you not using fscanf? – user14063792468 Mar 31 '19 at 22:14
  • Do you ever have multi-line character fields in your CSV data? If so, how are newlines handled? Just an extra complication which you probably don't need to worry about for your exercise. – Jonathan Leffler Apr 01 '19 at 04:16
  • @JonathanLeffler In this problem I won't be having any multi-line character fields, every entry will be just double values, but that is an interesting problem! – Oguz Canbek Apr 01 '19 at 07:44

2 Answers2

2

Replace your do {fgets()...} while(getc); with while (fgets()) {...}

pmg
  • 106,608
  • 13
  • 126
  • 198
0

The best thing to do is to just do a while(fgets()) instead of a do-while. I would not say that peeking a single char is the best method here.

For those cases where it is a reasonable option (if such a case exists) to do so, you can do a fseek(fp, -1, SEEK_CUR) to go back one step. It is a tiny bit ugly, but not to bad and it gets the job done. You would need to reorder a little bit like this:

while ((getc(fp)) != EOF) {
    fseek(fp, -1, SEEK_CUR);
    char buff[1024];
    fgets(buff, 1024, (FILE*)fp);
    printf(buff);
    getData(buff);
} 

However, do note that the fseek is not guaranteed to work if the stream is not seekable, such as pipes. It is better to use ungetc. Same principle. You put the character you just read back in the stream.

A similar method is this:

int c;
while ((c = getc(fp)) != EOF) {
    char buff[1024];
    buff[0]=c;
    fgets(&buff[1], 1023, (FILE*)fp);
    printf(buff);
    getData(buff);
} 

You should also change printf(buff) to puts(buff)

klutt
  • 30,332
  • 17
  • 55
  • 95
  • I've checked the `fseek()` method and it works! Also, changing the order of the `do-while` loop to a `while(fgets())` gets to work done! Thank you very much! – Oguz Canbek Mar 31 '19 at 22:11
  • BTW, in the second method that you suggested, why is the type of `c` is `int` and not `char` ? I've tried and both of them works but I'm not sure why... – Oguz Canbek Mar 31 '19 at 22:21
  • `getc` returns an int. The reason for that is that getc may return EOF, which does not fit in a char. – klutt Mar 31 '19 at 22:23
  • Whenever getc does not return EOF, you can safely assign a char variable to the value of `c` – klutt Mar 31 '19 at 22:26
  • 1
    It isn't clear that the `fseek()` method is guaranteed to work with non-seekable devices (such as terminals and pipes). It would be better — guaranteed to work by the C standard — to use `ungetc()` to put back the character that was read (as long as a character was read — as long as you didn't get EOF). – Jonathan Leffler Apr 01 '19 at 04:14