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

时间:2016-05-13 14:08:46

标签: c dynamic

我尝试创建一个代码来读取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);
}

3 个答案:

答案 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字段值的线性列表,这很好。您可以通过nbigArr[n * c](从第0行开始计算)访问字段bigArr[n * c + c - 1]来选择行l的字段。

对于crows等重要变量,我使用较长的名称,例如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;
}

请注意:

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

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

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

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

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

答案 2 :(得分:-1)

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

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

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