一些读取.csv文件的代码崩溃了

我尝试创建一个代码来读取csv文件并通过获取行和列来更改一个值。 在第一次我读取文件以检查那里有多少行和cols,而不是我创建一个动态2D数组 – 每行是文件中的行。 实际上使文件在2D数组中。 而且我将更改所选行和col的值,并将整个数组写回文件。 有人知道它为什么会坠毁吗? 它在第一行崩溃了 –

bigArr [i] [j] =(char)的CH;

function:

int changeValue(int line, int col, char str[],const char* path) { FILE* csvFile = fopen(path, "r"); char arr[VERY_BIG_MEMORY]; int l = 0, c = 1; int i = 0,j=0; int ch = 0; if (!csvFile) { printf("Cant read the file\nPlease open a file\n"); return -1; } do { ch = fgetc(csvFile); if (ch == ',') { c++; } } while (ch !='\n'); fseek(csvFile, 0L, SEEK_SET); do { ch = fgetc(csvFile); if (ch == '\n') { l++; } } while (ch!=EOF); char** bigArr = (char**)calloc(l*c,sizeof(char*)); for (i = 0; i < l*c; i++) { bigArr[i] = (char*)calloc(10,sizeof(char)); } fseek(csvFile, 0L, SEEK_SET); do { ch = fgetc(csvFile); if (ch == ',') { j++; } else if (ch == '\n') { i++; } else { bigArr[i][j]=(char)ch; } } while (ch != EOF); } 

崩溃的循环应该更像:

 enum { MAX_FIELD_WIDTH = 10 }; // Including null terminator i = j = 0; while ((ch = getc(csvFile)) != EOF) { if (ch == ',' || ch == '\n') { bigArr[i++][j] = '\0'; j = 0; } else { if (j < MAX_FIELD_WIDTH - 1) bigArr[i][j++] = ch; // else ignore excess characters } 

警告:未经测试的代码!

您的代码只是创建一个l * c字段值的线性列表,这很好。 您可以通过bigArr[n * c + c - 1]访问字段bigArr[n * c]来选择第n行的字段(从第0行开始计算)。

对于像lc这样的重要变量,我使用较长的名称,如rows (或lines )和rows 。 还不长,但更有意义。 应使用范围有限的单字符名称。

请注意,此代码忽略了CSV格式的细微差别,例如双引号内的逗号字段,更不用说双引号字段中的换行符。 它也忽略了行中不同数量字段的可能性。 如果代码跟踪行号,则可以处理太多字段(忽略额外字段)和太少字段(为缺少字段创建空条目)。 如果预扫描文件的代码更加清晰,则可以记录每行的最小和最大列数以及行数。 然后也可以诊断出问题。

使用更复杂的内存管理方案,也可以只扫描一次文件,如果文件实际上是终端或管道而不是磁盘文件,这有利。 它还可以处理任意长的字段值,而不是将它们限制为10个字节,包括终端空字节。


代码应该检查文件是否可以打开,并在完成后关闭它。 当前的function界面是:

 int changeValue(int line, int col, char str[], const char* path) 

但是前三个值被显示的代码忽略。 这可能是因为最终代码将更改其中一个读取的值,然后重写该文件。 据推测,如果要求更改不存在的列或行,它将报告错误。 这些相对较小的不足之处可能是由于最小化使代码类似于MCVE( 如何创建最小,完整和可validation的示例? )。

如果您的目标是读取数据并将其存储在char **指针中,那么这是一种方法

 int changeValue(const char *path) { FILE *file; size_t column_count; size_t row_count; int character; char **result; char *field; char large_buffer[100]; size_t length; size_t index; file = fopen(path, "r"); if (file == NULL) { printf("Cant read the file\nPlease open a file\n"); return -1; } /* Count Rows and Columns */ while ((character = fgetc(file)) != EOF) { switch (character) { case ',': ++column_count; break; case '\n': ++row_count; break; } } rewind(file); result = malloc(row_count * column_count * sizeof(char *)); if (result == NULL) { fclose(file); return -1; /* Do something to inform the caller */ } length = 0; index = 0; while ((character = fgetc(file)) != EOF) { switch (character) { case '\n': case ',': field = malloc(length + 1); if (field != NULL) { memcpy(field, large_buffer, length); field[length] = '\0'; } result[index++] = field; length = 0; break; default: if (length < sizeof(large_buffer)) large_buffer[length++] = character; break; } } /* USE THE DATA NOW AND FREE THE POINTERS */ fclose(file); return 0; } 

注意:

  1. 您可以计算行和列以预先分配目标,但您可以在单个循环中执行此操作,因为您一次只读取一个字符的文件。

  2. 您不需要将char **数组中的每个指针预先分配到固定大小,因为这没有多大意义,您可以使用固定大小来预分配它,就像这样

     char (*bigArr)[10] = malloc(sizeof(*bigArr)); 

    相反,在第二个循环中,只需使用足够大的缓冲区来保存字符,直到找到',''\n'忽略字符,如果它们不适合,就像你在代码中那样 ),然后分配指针并将数据复制到其中。

  3. @ JonathanLeffler 在答案中指出的实际问题是你没有正确地重置j索引,因此在数组的边界之后写入。

因为它没有使用,摆脱arr数组。

你不检查你是否分配了bigArr内存,也没有分配较小的数组。

你也在为bigArr [i] [j] tp分配一个字符的值,这是不正确的 – 你想将你在bigArr [i] [j]中分配的数组设置为它(并且必须处理附加字符因为你正逐字逐句阅读该数组