不兼容的指针不允许将 csv 放置到二维数组中

Incompatible pointers not allowing for csv placement into 2D array

我正在尝试逐行读取 CSV 文件,然后通过使用逗号分隔符分隔行,将这些行拆分为从 CSV 文件中读取的值。一旦成功,目标是将此二维数组读入 C 中的复杂模型作为输入。为此,我使用 getline()strtok().

我是 C 的新手,我花了数周时间才达到这一点,所以除非绝对必要,否则请不要为此建议不同的函数。我将 post 到目前为止我所拥有的并插入我收到的警告以及如果有人可以帮助我弄清楚为什么这段代码不会生成数组的地方。我认为这可能只是一个指针问题,但我一直在尽我所能,但它没有用。

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

#define ARRAYLENGTH 9
#define ARRAYWIDTH 7

float data[ARRAYLENGTH][ARRAYWIDTH];

int main(void) {

  char *line = NULL;
  size_t len = 0;
  ssize_t read;

  FILE *fp;
  fp=fopen("airvariablesSend.csv", "r");

  if(fp == NULL){
    printf("cannot open file\n\n");
    return -1;
  }

  int k , l;
  char **token; //for parsing through line using strtok()

  char comma = ',';
  const char *SEARCH = &comma; //delimiter for csv 
  char *todata; 

  for (l=0; l< ARRAYLENGTH +1; l++){ 
    while ((read = getline(&line, &len, fp)) != -1) {

      //Getline() automatically resizes when you set pointer to NULL and 
      //len to 0 before you loop
      //Here, the delimiting character is the newline character in this 
      //form. Newline character included in csv file

      //printf("Retrieved line of length %zu :\n", read);
      printf("%s", line);

      //The first line prints fine here. Now once I try to parse through 
      //I start getting warnings:

      for(k = 0; k < ARRAYWIDTH; k++) { //repeats for max number of columns

        token = &line;
        while (strtok(token, SEARCH)){

          //I'm getting this warning for the while loop line:
          //warning: passing argument 1 of `strtok' from incompatible pointer type

          fscanf(&todata, "%f", token);

          //I'm getting these warnings for fscanf. I'm doing this because
          //my final values in the array to be floats to put in the  
          //model      

          //warning: passing argument 1 of `fscanf' from incompatible pointer type
          //warning: format `%f' expects type `float *', but argument 3 has type 
          // 'char **'  

          todata = &data[l][k];

          //And finally, I'm getting this warning telling me everything is 
          //incompatible.
          //warning: assignment from incompatible pointer type. 

          printf("%f", data[l][k]);
        }

      }

    }
  }       

  free(line);
  //Free memory allocated by getline()
  fclose(fp);
  //Close the file.
  exit(EXIT_SUCCESS);
  return 0;
}

这个example shows how to use the function strok() : call it once on the line pch = strtok (line," ,"); then call it on NULL in the while loop : pch = strtok (NULL," ,");. The function sscanf() may be used to parse the string. It is similar to fscanf() for files, but be careful if you need to call it many times on the same string (or here)。

for 循环的索引也需要更改:它必须是 for (l=0; l< ARRAYLENGTH; l++)

我建议您使用以下代码,从您的代码开始:

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

#define ARRAYLENGTH 9
#define ARRAYWIDTH 7

float data[ARRAYLENGTH][ARRAYWIDTH];

int main(void) {

    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    FILE *fp;
    fp=fopen("bla.csv", "r");

    if(fp == NULL){
        printf("cannot open file\n\n");
        return -1;
    }

    int k , l;
    //char **token; //for parsing through line using strtok()

    for (l=0; l< ARRAYLENGTH ; l++){
        while ((read = getline(&line, &len, fp)) != -1) {
            printf("%s", line);
            for(k = 0; k < ARRAYWIDTH; k++) {
                char *pch = strtok (line,",");
                while (pch != NULL)
                {
                    if(sscanf(pch, "%f",&data[l][k])!=1){printf("bad file\n");exit(1);}
                    printf("%f\n", data[l][k]);
                    pch = strtok (NULL, ","); //delimiter for csv
                }


            }

        }
    }
    if (line){
        free(line);
    }
    fclose(fp);
    return 0;
}

使用getline

虽然strtok很好,但在使用strtof, strtol, ..等将值直接转换为数字时就没有必要了。除非您将这些值用作字符串值,否则您无论如何都必须调用转换例程(并进行适当的错误检查)。转换例程已经为您设置了可用于解析输入的 end pointer。关键是,为什么要使用两个函数来完成一个函数一开始打算做的事情?以下使用 getlinestrtof

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define ARRAYLENGTH 9
#define ARRAYWIDTH 7

int main (void) {

    char *line = NULL;      /* initialize ALL variables */
    size_t len = 0;
    ssize_t read = 0;
    float data[ARRAYLENGTH][ARRAYWIDTH] = {{0},{0}};
    size_t al = 0;          /* array length counter     */
    size_t aw = 0;          /* array width counter      */
    FILE *fp = NULL;

    if (!(fp = fopen ("airvariablesSend.csv", "r"))) {
        fprintf (stderr, "error: file open failed.\n");
        return 1;  /* do not return -1 to the shell */
    }

    while ((read = getline (&line, &len, fp)) != -1)
    {
        char *p = line;     /* pointer to line      */
        char *ep = NULL;    /* end pointer (strtof) */

        /* strip trailing '\n' or '\r' 
         * not req'd here, but good habit 
         */
        while (read > 0 && (line[read-1] == '\n' || line[read-1] == '\r'))
            line[--read] = 0;

        errno = 0;
        aw = 0;
        while (errno == 0)
        {
            /* parse/convert each number in line    */
            data[al][aw] = strtof (p, &ep);

            /* note: overflow/underflow checks omitted */
            /* if valid conversion to number */
            if (errno == 0 && p != ep)
            {
                aw++;                   /* increment index      */
                if (aw == ARRAYWIDTH)   /* check for full row   */
                    break;
                if (!ep) break;         /* check for end of str */
            }

            /* skip delimiters/move pointer to next (-) or digit   */
            while (*ep && *ep != '-' && (*ep <= '0' || *ep >= '9')) ep++;
            if (*ep)
                p = ep;
            else
                break;
        }

        al++;
        if (al == ARRAYLENGTH)          /* check length full    */
            break;
    }   

    if (line) free(line);
    if (fp) fclose(fp);

    printf ("\nArray Contents:\n\n");
    for (al = 0; al < ARRAYLENGTH; al++) {
        for (aw = 0; aw < ARRAYWIDTH; aw++)
            printf (" %8.2f", data[al][aw]);
        printf ("\n");
    }

    printf ("\n");

    exit(EXIT_SUCCESS);
}

注意: _GNU_SOURCEstring.h 对于此代码是不必要的,但已保留以备您的代码的其余部分需要它们。

输入

$ cat airvariablesSend.csv

-1.21,2.30,3.41,4.52,5.63,6.74,7.85
1.21,-2.30,3.41,4.52,5.63,6.74,7.85
1.21,2.30,-3.41,4.52,5.63,6.74,7.85
1.21,2.30,3.41,-4.52,5.63,6.74,7.85
1.21,2.30,3.41,4.52,-5.63,6.74,7.85
1.21,2.30,3.41,4.52,5.63,-6.74,7.85
1.21,2.30,3.41,4.52,5.63,6.74,-7.85
1.21,2.30,3.41,4.52,5.63,-6.74,7.85
1.21,2.30,3.41,4.52,-5.63,6.74,7.85

输出

$ ./bin/getlinefloatcsv

Array Contents:

    -1.21     2.30     3.41     4.52     5.63     6.74     7.85
     1.21    -2.30     3.41     4.52     5.63     6.74     7.85
     1.21     2.30    -3.41     4.52     5.63     6.74     7.85
     1.21     2.30     3.41    -4.52     5.63     6.74     7.85
     1.21     2.30     3.41     4.52    -5.63     6.74     7.85
     1.21     2.30     3.41     4.52     5.63    -6.74     7.85
     1.21     2.30     3.41     4.52     5.63     6.74    -7.85
     1.21     2.30     3.41     4.52     5.63    -6.74     7.85
     1.21     2.30     3.41     4.52    -5.63     6.74     7.85

仅使用 fscanf

当然,如果您打算使用 fscanf 并取消 getline,那么您的输入例程将减少为:

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

#define ARRAYLENGTH 9
#define ARRAYWIDTH 7

int main (void) {

    float data[ARRAYLENGTH][ARRAYWIDTH] = {{0},{0}};
    size_t al = 0;          /* array length counter     */
    size_t aw = 0;          /* array width counter      */
    FILE *fp = NULL;

    if (!(fp = fopen ("airvariablesSend.csv", "r"))) {
        fprintf (stderr, "error: file open failed.\n");
        return 1;  /* do not return -1 to the shell */
    }

    for (al =0; al < ARRAYLENGTH; al++)
        fscanf (fp, "%f,%f,%f,%f,%f,%f,%f", &data[al][0], &data[al][1], 
                &data[al][2], &data[al][3], &data[al][4], &data[al][5], &data[al][6]);

    if (fp) fclose(fp);

    printf ("\nArray Contents:\n\n");
    for (al = 0; al < ARRAYLENGTH; al++) {
        for (aw = 0; aw < ARRAYWIDTH; aw++)
            printf (" %8.2f", data[al][aw]);
        printf ("\n");
    }

    printf ("\n");

    exit(EXIT_SUCCESS);
}

但是,注意: 使用 fscanf 远不如使用 getlinefgets 灵活。它依赖于与数据完全匹配的输入格式字符串来防止匹配失败。虽然这在某些需要灵活性的情况下很好,但使用 getline of fgets 一次读取一行是更好的选择。 (只需要一个杂散字符就可以破坏 fscanf 转换)