我正在尝试逐行读取 CSV 文件,然后通过用逗号分隔符分隔行,将这些行拆分为从 CSV 文件中读取的值。一旦成功,目标是将此二维数组读入 C 中的复杂模型作为输入。为此,我使用了 getline()
和 strtok()
。
我是 C 的新手,我花了数周的时间才达到这一点,所以除非绝对必要,否则请不要为此建议不同的函数。我将发布到目前为止的内容并插入我收到的警告以及如果有人可以帮助我弄清楚为什么此代码不会生成数组的位置。我认为这可能只是一个指针问题,但我一直在尽我所能,但它没有用。
#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 = , //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;
}
最佳答案
使用getline
:
虽然 strtok
很好,但是当使用 strtof、strtol、..
等将值直接转换为数字时,就没有必要了。除非您将这些值用作字符串值,否则您无论如何都必须调用转换例程(并进行适当的错误检查)。转换例程已经为您设置了一个结束指针
,可用于解析输入。关键是,为什么要使用两个函数来完成一个函数一开始打算做的事情?以下使用 getline
和 strtof
:
#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_SOURCE
和 string.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
远不如使用getline
或fgets
灵活。它依赖于与数据完全匹配的输入格式字符串来防止匹配失败。虽然这在某些需要灵 active 的情况下很好,但使用 fgets
的 getline
一次读取一行是更好的选择。 (只需要一个杂散字符即可破坏 fscanf
转换)
关于c - 不兼容的指针不允许将 csv 放置到二维数组中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29219477/