strtol выдает segfault, когда strtok возвращает NULL

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char* getfield(char* line, int num) {
        const char* tok;
        for (tok = strtok(line, ",");
                tok && *tok;
                tok = strtok(NULL, ",\n"))
        {
            if (!--num)
                return tok;
        }
        return NULL;
    }

    int main()
    {
        FILE* stream = fopen("b.csv", "r");
        char line[1024];
        char *pstr;int num;
         const char* value;

        while (fgets(line, 1024, stream))
        {
            char* tmp = strdup(line);
        //printf("Field 3 would be %s\n", getfield(tmp, 3));    
        value=getfield(tmp, 3);
        num =strtol(value,&pstr,10);
        printf("Field 3 would be %d\n", num);
        // NOTE strtok clobbers tmp
            free(tmp);
        }
    }

/* b.csv

301,36,15
302,88,75

// мой вывод

Field 3 would be 15
Field 3 would be 75

*/

проблема: /* b.csv

301,36,15
 302,88,
 ,,,34

если таблица повреждена, как указано выше, «strtok» возвращает NULL, и поэтому «strtol» дает «segfault».. как это решить?

Здесь основная проблема заключается в том, что если 2-й нет, он обрабатывает 3-й как второй и выдает segfault !! например, в 3-й строке b.csv ",,,34" означает, что присутствует 3-е значение, но оно ведет себя так, как будто "34" - это 1-е значение, а 2-е и третье соответственно NULL !!


person Jatin Bodarya    schedule 30.10.2012    source источник
comment
Итак, вы вызываете strtol по указателю NULL? Это никогда не сработает. Проверяйте наличие NULL перед вызовом чего-либо с помощью этого указателя.   -  person Some programmer dude    schedule 30.10.2012


Ответы (2)


Почему бы вам просто не проверить value, полученное от getfield(tmp, 3);, на NULL и не вызывать strtol, если возвращается NULL? Еще один способ обойти это сделать static char* not_found = ""; в getfield и вернуть ему адрес вместо NULL, тогда strtol не будет segfault.

ОБНОВЛЕНИЕ

Поскольку я обнаружил, что strtok действительно беспомощен в этой ситуации, я попытался написать код, который делает то же самое с strchr:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    char* getfield(char* line, int num) {
        char* tok = line;
        char* result;
        if (line)
        {
            do
            {
                if (!--num)
                {
                    tok = strchr(line, ',');
                    if (tok == NULL)
                    {
                        tok = &line[strlen(line)];
                    }
                    size_t fieldlen = tok - line;
                    if (fieldlen)
                    {
                        result = (char*)malloc(fieldlen+1);
                        result[fieldlen] = '\0';
                        strncpy(result, line, fieldlen);
                        return result;
                    }
                    else
                    {
                        break;
                    }
                }
                tok = strchr(line, ',');
                line = tok + 1;
            } while (tok);
        }
        result = (char*)malloc(2);
        strcpy(result, "0");
        return result;
    }

    int main()
    {
        FILE* stream = fopen("b.csv", "r");
        char line[1024];
        char *pstr;int num;
        char* value;

        while (fgets(line, 1024, stream))
        {
            char* tmp = strdup(line);
            //printf("Field 3 would be %s\n", getfield(tmp, 3));    
            value=getfield(tmp, 3);
            num =strtol(value,&pstr,10);
            free(value);
            printf("Field 3 would be %d\n", num);
            // NOTE strtok clobbers tmp
            free(tmp);
        }
    }

Это работало над входным файлом:

    10,,30
    10,

Код возвращает 0, если ничего не найдено, вы можете изменить это, и результат распределяется динамически. Я надеюсь, что это поможет, урок для меня - избегайте C при разборе строк: D

person Rudolfs Bundulis    schedule 30.10.2012
comment
видите, если какое-либо значение отсутствует, я полагаю, чтобы присвоить ему значение 0, чтобы оно не принимало 3-е место как второе, если второе отсутствует... поэтому я подумал назначить его из вызывающей функции, но не могу правильно добавить.. , у вас есть идеи, как это решить? - person Jatin Bodarya; 30.10.2012
comment
в соответствии со спецификацией POSIX, strtok рассматривает два последовательных разделителя как один, поэтому это немного сложно решить: вы можете использовать strchr в строке, чтобы фактически найти и извлечь материал между ,, либо использовать функции регулярных выражений в С++, использовать строки С++ , ... - person Rudolfs Bundulis; 30.10.2012
comment
Вот почему я спрашивал, возможно ли, что я могу присвоить значение 0 (в виде строки) из самого getfield .. поэтому он может вернуть его, и проблема двух последовательных разделителей может быть решена. И я не могу использовать C++, как я полагаю, только на C. - person Jatin Bodarya; 30.10.2012
comment
Обновил ответ решением, которое использует strchr и, надеюсь, работает. - person Rudolfs Bundulis; 30.10.2012
comment
значение = получить поле (tmp, 1); значение2=getfield(tmp, 2);value3=getfield(tmp, 3); это означает, что я предлагал вам сравнить текущие 3 x getfield() (с участием malloc + разные strchr и т. д.) 3 x strtol() 3 x free() с большим объемом данных. Это очень трудоемко !!! когда я хочу назначить его где-то еще при каждой перезагрузке, это дает мне ошибки .... вы можете помочь улучшить производительность этого кода ?? - person Jatin Bodarya; 02.11.2012
comment
Что ж, передайте буфер памяти для результата в качестве параметра getfield(), который является переменной стека в main(), таким образом вы можете избежать malloc() и free(). - person Rudolfs Bundulis; 02.11.2012

вместо

num =strtol(value,&pstr,10);
printf("Field 3 would be %d\n", num);

использовать это:

if (value!=NULL) {         
    num =strtol(value,&pstr,10);
    printf("Field 3 would be %d\n", num);
}
else {
    printf("Field 3 does not exist\n");
}

вы должны проверить, если value!= NULL перед вызовом функции strtol()

Изменить, чтобы исправить возврат функции getfield().

В цикле for попробуйте изменить: (я не проверял, но это может решить вашу проблему)

 tok = strtok(NULL, ",\n")

by

 tok = strtok(tok+strlen(tok)+1, ",\n")
person MOHAMED    schedule 30.10.2012
comment
с помощью этого метода второй вопрос не может быть решен... например. здесь значение в файле равно 10, 30. Оно рассматривает 30 как второе значение и NULL как 3-е значение... поэтому здесь нет segfault, но значение в конечном итоге неверно - person Jatin Bodarya; 30.10.2012
comment
в этом случае вам нужно обновить getfield(), чтобы вернуть правильное значение - person MOHAMED; 30.10.2012