strtok_r ведет себя не так, как ожидалось - PullRequest
0 голосов
/ 24 июня 2018

У меня проблемы с моей strtok_r() реализацией. Я разбираю текстовый файл так, что если он сталкивается с ";", он обрабатывает его как комментарий и игнорирует его, анализируя токены (все, что разделено пробелом) в файле.

Вот такой файл:

1) ;;
2) ;; Basic 
3) ;;
4) 
5) defun main
6) 5 3 2 * + printnum endl      ;;  (3 * 2) + 5 = 11
7) 3 4 5 rot * + printnum endl  ;;  (3 * 5) + 4 = 19
8) return

Что я делаю, так это то, что как только я fgets() строка, я анализирую строку, используя strtok_r(). Вот полная функция, которая пытается это:

void read_token(token* theToken, char* j_file, char* asm_file)
{
    //Declare and initialize variables
    int len;
    char line[1000];
    char *semi_token = NULL;
    char* parse_tok = NULL;
    char* assign = NULL;

    //Open file to begin parsing
    FILE *IN = fopen(j_file, "r");

    //If file pointer NULL
    if (IN == NULL)
    {
        //Print error message
        printf("error: file does not exist\n");

        //Terminate program
        exit(1);
    }

    //File pointer not NULL
    else
    {
        //Initialize char_token linked list
        parsed_element* head = init_list_head();
        head->token = "start";

        print_list(head);

        //Get characters from .j FILE
        while (!feof(IN))
        {
            //Get each line of .j file
            fgets(line, 1000, IN);

            //Compute length of each line
            len = strlen(line);

            //If length is zero or if there is newline escape sequnce
            if (len > 0 && line[len-1] == '\n')
            {
                //Replace with null
                line[len-1] = '\0';
            }

            //Search for semicolons in .J FILE
            semi_token = strpbrk(line, ";\r\n\t");

            //Replace with null terminator
            if (semi_token) 
            {
                *semi_token = '\0';
            }
            // printf("line is %s\n",line );

            //Copy each line
            assign = line;

            // printf("line is %s\n",line );

            len = strlen(line);

            printf("line length is %d\n",len );

            // parse_tok = strtok(line, "\r ");

            //Parse each token in line
            while((parse_tok = strtok_r(assign, " ", &assign)))
            {
                printf("token is %s\n", parse_tok);

                insert_head(&head, parse_tok);

                print_list(head);       

                //Obtain lentgh of token
                // len = strlen(parse_tok);

                // printf("len is %d \n", len);

            }

        }    

    }
}

Я загружаю каждый токен в односвязный список. Вот структура, которая составляет каждый узел списка:

typedef struct parsed_element
{
    char* token;
    struct parsed_element* next;
} parsed_element;

Аспекты, которые работают как ожидалось

1) Моя функция правильно отграничивает каждую строку от fgets () после удаления всех ";" и разделителей пробелов. Вот вывод в качестве доказательства:

1) line length is 0
2) line length is 0
3) line length is 0
4) line length is 0
5) line length is 10
6) line length is 23
7) line length is 27
8) line length is 6

2) Моя функция правильно маркирует каждую строку. Вот вывод, чтобы подтвердить это:

token is defun
token is main
token is 5
token is 3
token is 2
token is *
token is +
token is printnum
token is endl
token is 3
token is 4
token is 5
token is rot
token is *
token is +
token is printnum
token is endl
token is return

Аспекты НЕ работают должным образом

1) Проблема возникает, когда я пытаюсь вставить каждый токен в односвязный список. После получения каждого токена я передаю токен в функцию, которая вставляет его в заголовок уже инициализированного связанного списка. ожидаемое поведение после каждой итерации в цикле while, содержащем strtok_r(), равно:

1) List is: Start
2) List is defun Start
3) List is main defun Start
4) List is: 5 main defun Start
5) List is: 3 5 main defun Start
6) List is: 2 3 5 main defun Start
7) List is: * 2 3 5 main defun Start
8) List is: + * 2 3 5 main defun Start
9) List is: printnum + * 2 3 5 main defun Start
10) List is: endl printnum + * 2 3 5 main defun Start
11) List is: 3 endl printnum + * 2 3 5 main defun Start
12) List is: 4 3 endl printnum + * 2 3 5 main defun Start
13) List is: 5 4 3 endl printnum + * 2 3 5 main defun Start
14) List is: rot 5 4 3 endl printnum + * 2 3 5 main defun Start
14) List is: * rot 5 4 3 endl printnum + * 2 3 5 main defun Start
16) List is: + * rot 5 4 3 endl printnum + * 2 3 5 main defun Start
17) List is: printnum + * rot 5 4 3 endl printnum + * 2 3 5 main defun Start
18) List is: endl printnum + * rot 5 4 3 endl printnum + * 2 3 5 main defun Start
19) List is: return endl printnum + * rot 5 4 3 endl printnum + * 2 3 5 main defun Start

Вместо этого я наблюдаю:

1) List is: start 
2) List is: defun start 
3) List is: main defun start 
4) List is: 5 * + printnum endl 5 start 
5) List is: 3 5 * + printnum endl 5 start 
6) List is: 2 3 5 * + printnum endl 5 start 
7) List is: * 2 3 5 * 5 start 
8) List is: + * 2 3 5 * 5 start 
9) List is: printnum + * 2 3 5 * 5 start 
10) List is: endl printnum + * 2 3 5 * 5 start 
11) List is: 3 num endl * + printnum endl t * + printnum endl rot * + printnum endl 5 rot * + printnum endl 4 5 rot * + printnum endl 3 rot * + printnum endl 3 start 
12) List is: 4 3 num endl * + printnum endl t * + printnum endl rot * + printnum endl 5 rot * + printnum endl 4 3 rot * + printnum endl 3 start 
13) List is: 5 4 3 num endl * + printnum endl t * + printnum endl rot * + printnum endl 5 4 3 rot * + printnum endl 3 start 
14) List is: rot 5 4 3 num endl * + printnum endl t rot 5 4 3 rot 3 start 
15) List is: * rot 5 4 3 num endl * t rot 5 4 3 rot 3 start 
16) List is: + * rot 5 4 3 num endl * t rot 5 4 3 rot 3 start 
17) List is: printnum + * rot 5 4 3 num * t rot 5 4 3 rot 3 start 
18) List is: endl printnum + * rot 5 4 3 num * t rot 5 4 3 rot 3 start 
19) List is: return endl printnum + *  rn turn return num * t  rn turn return  return start 

После третьей итерации моя функция вставки заголовка завершается ошибкой и не вставляет каждый токен в начало списка. На самом деле, это как-то ломает мои жетоны. Почему это происходит? Я почти уверен, что это не реализация моего связанного списка функций insert_head() и print_list().

Они были тщательно проверены и доказали свою работоспособность для других приложений. Мне кажется, это как-то связано с тем, как я анализирую каждый токен? Или как эти утилиты взаимодействуют?

Я публикую свой код для моих insert_head() print_list() функций для справки:

LIST_STATUS insert_head(struct parsed_element** head, char* token);
void print_list(struct parsed_element* head);

LIST_STATUS insert_head(struct parsed_element** head, char* token)
{
    //Check if parsed_element** head returns NULL
    if (!*head)
    {
        //Return status
        return LIST_HEAD_NULL;
    }

    //Case where head is not NULL
    else
    {
        //Create new node
        parsed_element* new_node;

        //Malloc space for new node
        new_node = (parsed_element*)malloc(sizeof(parsed_element));

        //Case where malloc returns void*
        if (new_node != NULL)
        {
            //Set tokenue of new node
            new_node->token = token;

            //Point new node to address of head
            new_node->next = *head;

            //New node is now head node (CHECK FOR POTENTIAL ERRORS)
            *head = new_node;

            //Return status
            return LIST_OKAY;
        }

        //Case where malloc returns NULL
        else 
        {
            //Print malloc error
            printf("Malloc error: aborting\n");

            exit(0);
        }
    }   
}

void print_list(struct parsed_element* head)
{
    //Create variable to store head pointer
    parsed_element* print_node = head;

    //Print statement
    printf("List is: ");

    //Traverse list
    while (print_node != NULL)
    {
        //Print list element
        printf("%s ",print_node->token);

        //Increment pointer
        print_node = print_node->next;
    }

    //Print newline
    printf("\n");
}

1 Ответ

0 голосов
/ 24 июня 2018

Ваша функция read_token использует локальную переменную line для чтения содержимого файла. При токенизации этой строки с использованием strtok вы будете получать указатели в память, выделенную для этой локальной переменной. Передача таких указателей затем другой функции insert_head, которая просто назначает указатель (но не копирует содержимое), приведет к появлению узлов списка, которые указывают на «недействительную» память, как только закончится read_token (line будет становятся недействительными при выходе из области видимости, т.е. когда read_token заканчивается).

Так что вместо

new_node->token = token;

вам нужно скопировать содержимое токена, т.е. написать

new_node->token = malloc(strlen(token)+1);
strcpy(new_node->token,token);
...