strtok () возвращает NULL при последующих вызовах, даже если есть другие токены для чтения - PullRequest
2 голосов
/ 28 мая 2020

У меня проблема в моей программе, когда вызов strtok (NULL, "\ r \ n"); возвращает NULL после того, как я вызвал функцию, даже если в потоке все еще есть токены. Я просмотрел это некоторое время и не могу понять, что именно в этом вызове функции, который меняет поведение последующих вызовов strtok().

Буду очень благодарен всем, кто может помочь. Ура.

Основная функция:

int main() 
{ 
    char raw[] = "0 4 96 30\r\n3 4 64 60\r\n3 5 64 20\r\n3 2 32 40\r\n5 1 100 20\r\n20 3 4 30\r\n"; 

    char* line = strtok(raw, "\r\n");            //line == "0 4 96 30" OK
    line = strtok(NULL, "\r\n");                 //line == "3 4 64 60" OK
    line = strtok(NULL, "\r\n");                 //line == "3 5 64 20" OK
    struct Process current = parseProcess(line); //Now strtok calls after this will return NULL...
    line = strtok(NULL, "\r\n");                 //line == NULL (supposed to be "3 2 32 40")
    line = strtok(NULL, "\r\n");                 //line == NULL

    return 0; 
}   

Используемые структура и функция:

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

struct Process {
    long timeArrived;
    long processId;
    long memorySizeReq;
    long jobTime;
    long remainingTime;
};

//Rips values from the input and puts it into a struct
struct Process parseProcess(char* input){
    struct Process output;

    //Makes a back up as to not mutate input
    char* temp = malloc(strlen(input) * sizeof(char));
    strcpy(temp, input);
    char* token = strtok(temp, " ");

    //Fills out the fields of the parsed output
    for(int i=0; i<4; i++){
        switch(i){
            case 0:
                output.timeArrived = atoi(token);
                token = strtok(NULL, " ");
                break;
            case 1:
                output.processId = atoi(token);
                token = strtok(NULL, " ");
                break;
            case 2:
                output.memorySizeReq = atoi(token);
                token = strtok(NULL, " ");
                break;
            case 3:
                output.jobTime = atoi(token);
                output.remainingTime = atoi(token);
                token = strtok(NULL, " ");
                break;
        }
    }
    return output;
}

1 Ответ

1 голос
/ 28 мая 2020

strtok не является реентерабельным и используется внутри parseProcess, поэтому следующие вызовы в main используют другой контекст. Кроме того, strtok(raw, "\r\n"); рассматривает как '\r' и '\n' как разделители, так и любую последовательность этих символов, в результате чего strtok() не будет возвращать пустые токены для пустых строк в исходной строке.

Что касается POSIX strtok_r, следуя комментариям, он не работает в windows, однако у вас есть strtok_s. Есть и другие способы сделать это, но, учитывая, что вы используете strtok(), вот пример этого может быть реализован:

Живая демонстрация

struct Process parseProcess(char* input){
    struct Process output; 
    char* temp = malloc(strlen(input) + 1); //notes 1, 2, 3
    strcpy(temp, input);   
    char* strmax; //for **stmax parameter
    char* token = strtok_r(temp, " ", &strmax);
    for(int i = 0; i < 4; i++){
        switch(i){
            case 0:
                output.timeArrived = atoi(token);
                token = strtok_r(NULL, " ", &strmax);
                break;
            case 1:
                output.processId = atoi(token);
                token = strtok_r(NULL, " ", &strmax);
                break;
            case 2:
                output.memorySizeReq = atoi(token);
                token = strtok_r(NULL, " ", &strmax);
                break;
            case 3:
                output.jobTime = atoi(token);
                output.remainingTime = atoi(token);
                token = strtok_r(NULL, " ", &strmax);
                break;
        }
    }
    free(temp);   //free the allocated memory
    return output;    
}

Примечания:

  1. Зарезервировать место для нулевого терминатора, когда вы выделяете с помощью strlen.
  2. Выделение памяти стоит дорого, если вы можете, избегайте этого, в этом случае вы можете используйте char temp[strlen(input) + 1];.

    MSV C quibles с этим, потому что это массивы переменной длины и говорит, что это ошибка, это не так, VLA действительны в C, вы можете использовать g cc или clang компиляторы, если вы хотите их использовать.

  3. sizeof(char) не требуется, char составляет 1 байт на разных платформах.
//...
#ifdef _MSC_VER  //improved portability
    #define strtok_r strtok_s
#endif 

int main() 
{   
    char raw[] = "0 4 96 30\r\n3 4 64 60\r\n3 5 64 20\r\n3 2 32 40\r\n5 1 100 20\r\n20 3 4 30\r\n"; 
    char* strmax;
    char* line = strtok_r(raw, "\r\n", &strmax);            //line == "0 4 96 30" OK                                 
    line = strtok_r(NULL, "\r\n", &strmax);                 //line == "3 4 64 60" OK
    line = strtok_r(NULL, "\r\n", &strmax);                 //line == "3 5 64 20" OK                                   
    struct Process current = parseProcess(line); 
    line = strtok_r(NULL, "\r\n", &strmax);                 //line == "3 2 32 40" OK                                  
    line = strtok_r(NULL, "\r\n", &strmax);                 ///line == "5 1 100 20" OK;

    return 0; 
}
...