我尝试创建一个代码来读取csv文件并通过获取行和列来更改一个值。在第一次我读取文件以检查那里有多少行和cols,而不是我创建一个动态2D数组 - 每行都是文件中的行。实际上使文件在2D数组中。而且我将更改所选行和col的值,并将整个数组写回文件。有人知道为什么它会崩溃吗?它在第一行 -
中崩溃了bigArr [i] [j] =(char)的CH;
功能:
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);
}
答案 0 :(得分:2)
崩溃的循环应该更像:
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
字段值的线性列表,这很好。您可以通过n
到bigArr[n * c]
(从第0行开始计算)访问字段bigArr[n * c + c - 1]
来选择行l
的字段。
对于c
和rows
等重要变量,我使用较长的名称,例如lines
(或cols
)和int changeValue(int line, int col, char str[], const char* path)
。还不长,但更有意义。应使用单字符名称,范围有限。
请注意,此代码忽略了CSV格式的细微之处,例如双引号内的逗号字段,更不用说双引号字段中的换行符了。它也忽略了行中不同数量字段的可能性。如果代码跟踪行号,则可以处理太多字段(忽略额外字段)和太少字段(为缺少字段创建空条目)。如果预扫描文件的代码更加清晰,则可以记录每行的最小和最大列数以及行数。然后也可以诊断出问题。
使用更复杂的内存管理方案,也可以只扫描一次文件,如果文件实际上是终端或管道而不是磁盘文件,这有利。它还可以处理任意长的字段值,而不是将它们限制为10个字节,包括终端空字节。
代码应该检查文件是否可以打开,并在完成后关闭它。当前的功能界面是:
library(foreign)
chap12 <- read.dta(file = "<your path>/ch12.dta")
但显示的代码忽略了前三个值。这可能是因为最终代码将更改其中一个读取的值,然后重写该文件。据推测,如果要求更改不存在的列或行,它将报告错误。这些相对较小的不足之处可能是由于最小化使代码类似于MCVE(How to create a Minimal, Complete, and Verifiable Example?)。
答案 1 :(得分:0)
如果您的目标是读取数据并将其存储在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;
}
请注意:
您可以计算行和列以预先分配目标,但您可以在一个循环中执行此操作,因为您一次只能读取一个字符。
您不需要将char **
数组中的每个指针预分配到固定大小,因为这没有多大意义,您可以使用固定大小来像这样预先分配
char (*bigArr)[10] = malloc(sizeof(*bigArr));
相反,在第二个循环中,只需使用一个足够大的缓冲区来保存字符,直到找到','
或'\n'
(如果它们不适合,忽略字符,就像你一样无论如何都会在你的代码中),然后分配指针并将数据复制到其中。
@ JonathanLeffler Here的答案中指出的实际问题是你没有正确地重置j
索引,因此在数组的边界之后写入。
答案 2 :(得分:-1)
摆脱arr数组,因为它没有被使用。
你不会检查你是否分配了bigArr内存,也没有分配较小的数组。
另外你要为bigArr [i] [j] tp指定一个字符的值,这是不正确的 - 你想将你在bigArr [i] [j]中分配的数组设置为它(并且必须处理将字符附加到该数组中,因为您正逐字逐句地阅读