Проблемы с разбором и перезаписью данных в C с использованием пользовательского strtok

Я читаю файл .csv, который затем мне нужно разобрать на токены. Я пытался использовать strtok(), но, к сожалению, он не может возвращать пустые поля (которыми заполнены мои данные). Поэтому я воспользовался самодельной версией strtok, которую нашел, strtok_single, которая возвращает правильные значения, которые мне нужны.

Данные вводятся в мой массив правильно; но что-то не так, потому что до завершения цикла инициализации данные перезаписываются. Я пробовал печатать заявления и анализировать проблему, но я просто не могу понять, что не так. Любое понимание вообще было бы полезно.

Вот самодельная функция strtok, которую я использую:

char* strtok_single(char* str, char const* delims) {
    static char* src = NULL;

    char* p, *ret = 0;

    if (str != NULL)
        src = str;    
    if (src == NULL)
        return NULL;

    if ((p = strpbrk(src, delims)) != NULL) {
        *p = 0;
        ret = src;
        src = ++p;
    }    
    return ret;
}

Вот мой код:

int main() {
    int numLines = 0;
    int ch, i, j;
    char tmp[1024];
    char* field;
    char line[1024];

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

    // count number of lines in file
    while ((ch = fgetc(fp)) != EOF) {
        if (ch == '\n')
            numLines++;
    }

    fclose(fp);

    // Allocate memory for each line in file
    char*** activity = malloc(numLines * sizeof(char**));

    for (i = 0; i < numLines; i++) {
        activity[i] = malloc(42 * sizeof(char*));

        for (j = 0; j < 42; j++) {
            activity[i][j] = malloc(100 * sizeof(char));
        }
    }

    // read activity file and initilize activity matrix
    FILE* stream = fopen("filename.csv", "r");
    i = 0;
    while (fgets(line, 1024, stream)) {
        j = 0;
        int newlineLoc = strcspn(line, "\n");
        line[newlineLoc] = ',';
        strcpy(tmp, line);

        field = strtok_single(tmp, ",");

        while (field != NULL) {
            for (j = 0; j < 42; j++) {
                activity[i][j] = field;
                field = strtok_single(NULL, ",");
                // when I print activity[i][j] here, the values are correct
            }
            // when I print activity[i][j] here, the values are correct for the
            // first iteration
            // and then get overwritten by partial data from the next line
        }

        i++;

    } // close while
    fclose(stream);

    // by the time I get to here my matrix is full of garbage
    // some more code that prints the array and frees memory
} // close main

person Hopper06    schedule 06.08.2014    source источник
comment
Вы говорите, что ввод CSV; это фиксированный формат или переменное количество столбцов?   -  person John Zwinck    schedule 06.08.2014
comment
Это фиксированный формат из 42 столбцов, но с переменным количеством строк. Поэтому я был ленив и просто жестко запрограммировал 42 вместо того, чтобы создавать переменную для столбцов. Я улучшу это позже.   -  person Hopper06    schedule 06.08.2014


Ответы (2)


activity[i][j] = field;

Когда циклы заканчиваются, каждый activity[i][j] указывает где-то в tmp, который перезаписывается в каждом цикле. Вместо этого, поскольку вы предварительно выделяете место в каждом activity[i][j], вы должны просто скопировать туда содержимое строки:

strcpy(activity[i][j], field);

Будьте осторожны с переполнением буфера (т.е. если field больше 99 символов).

Кроме того, sizeof(char) является лишним, поскольку по определению всегда равно 1.

person Drew McGowen    schedule 06.08.2014
comment
Вау, поле — это движущийся указатель на tmp, и я перезаписываю tmp, это так ясно задним числом, спасибо. Кроме того, хотя я знаю, что sizeof(char) равен 1, я всегда добавляю sizeof(type) в свои malloc просто по привычке (лучше поставить его, когда это не нужно, чем забыть, когда это необходимо). Еще раз спасибо. - person Hopper06; 06.08.2014

Ваша строка «активность [i] [j] = поле;» назад - вы хотите, чтобы указатель был назначен на память malloc.

person Nerf Herder    schedule 06.08.2014