将输入文件标记为链表

时间:2017-10-30 07:27:59

标签: c string pointers linked-list tokenize

我尝试标记化输入文件,并将其单个字词存储在按字数统计的链接列表中。我一直在努力将标记化的字符串存储到节点中,并且很难理解我的tokenizing/inserting进程中的错误。目前,当打印出存储的字符串时,每个字符串的第一个字母被截断,并且看似随机的垃圾和每个字符串的结尾。我尝试了以下方法来解决我的错误:

  1. 在标记化之后将每个字符串终止为空(我已经将其留在了 我的程序似乎是正确的)
  2. 使用strncpy()代替new_word->str = str;
  3. 将指向标记化字符串的指针传递给我的insert函数, 而不只是传递字符串本身。
  4. 以下是我的代码

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <strings.h>
    
    typedef struct word{ 
        int length; 
        char *str; 
        struct word *left;
        struct word *right; 
        struct word *down;
    }word;
    
    
    void print_list(word **head){ 
    
        word *temp_traverse = *head;
        word *temp_down;
    
        for( ; temp_traverse!=NULL; temp_traverse = temp_traverse->right){ 
            temp_down = temp_traverse;
            for( ; temp_down!=NULL; temp_down = temp_down->down){ 
                printf("Count: %d, String: %s\n", temp_down->length, temp_down->str);
            }
        }
    
    }
    
    
    int is_empty(word **head, word **tail){ 
    
        if((*head == NULL)||(*tail == NULL))
            return 1;
    
        return 0;
    }
    
    void insert(word **head, word **tail, word *new_word){ 
    
        if(is_empty(head, tail)){
            (*head) = new_word; 
            (*tail) = new_word;
            return;
        }
    
        if((new_word->length)<((*head)->length)){ 
            new_word->right = (*head);
            (*head)->left = new_word;
            (*head) = new_word;
            return;
        }
    
        word *temp = *head;
    
        while(((temp->right)!=NULL) && ((temp->length)<(new_word->length))) 
            temp = temp->right;
    
        if((temp->length) == (new_word->length)){
            while(temp->down != NULL)
                temp = temp->down;
            temp->down = new_word;
            return;
        }
    
        if(temp->right == NULL){
            word* last = (*tail);
            last->right = new_word;
            new_word->left = last; 
            (*tail) = new_word;
            return;
        }
    
        word* next = temp->right;
        temp->right = new_word;
        next->left = new_word; 
        new_word->left = temp; 
        new_word->right = next;
    
        return;
    }
    
    void create(word **head, word **tail, char **str){ 
    
        word *new_word = (word*)malloc(sizeof(word));
        int length = strlen(*str);
    
        if(new_word == NULL){
                fprintf(stderr, "Error creating a new word node.\n");
                exit(0);
            }
    
        new_word->str = (char*)malloc(sizeof(*str));
        strncpy(new_word->str, *str, length);
        //new_word->str = *str;
        new_word->length = length;
        printf("%s ", new_word->str); //test print
    
        new_word->left = NULL;
        new_word->right = NULL;
        new_word->down = NULL;
    
        insert(head, tail, new_word);
    
        return;
    }
    
    
    void tokenize(word **head, word **tail, char words_buffer[]){ 
    
        char *cur; 
    
        cur = strtok(words_buffer, " .,;()\t\r\v\f\n");
    
        *cur++ = '\0';
        create(head, tail, &cur);
    
        /* tokenize the next string and reset the "duplicate" variable */
        while((cur = strtok(NULL, " .,;()\t\r\v\f\n")) != NULL){
            //cur = strtok(NULL, " .,;()\t\r\v\f\n"); 
            *cur++ = '\0';      
            if(cur){
                create(head, tail, &cur);
            }
        }
    
    }
    
    int main(int argc, char *argv[]){ 
    
        FILE *fp;
        word *head = NULL; 
        word *tail = NULL;
    
        /*if(argc<3){
            printf("Failure: not enough arguments");
            return -1; 
        }*/
    
        fp = fopen(argv[1], "r");
        fseek(fp, 0, SEEK_END);
        char words_buffer[ftell(fp)+1];
        fseek(fp, 0, SEEK_SET);
    
        if(fp==NULL){
            printf("Failure: unreadable file");
            return -1;
        }
    
        while(fgets(words_buffer, sizeof(words_buffer), fp)){
                if(strlen(words_buffer)>1)
                    tokenize(&head, &tail, words_buffer);
        }
    
        //print_list(&head);
    
        fclose(fp);
        return 0;
    } 
    

    我离开了我的测试字符串打印供您参考。您还会注意到我现在没有使用print_list,因为我还没有正确存储字符串。

    由于最后的垃圾,我假设我错误地使用指向字符串的指针,或者malloc()太多空间。至于截断,我不确定,但我认为它与我的*cur++ = '\0';行有关。

    非常感谢任何帮助,感谢您花时间看一看。

1 个答案:

答案 0 :(得分:3)

您没有使用strncpy()复制整个字符串。

事实上,当你获得长度时,你正在复制一个字符太少:

int length = strlen(*str);

strncpy()联机帮助页中所述:

  

警告:如果src的前n个字节中没有空字节,则放在dest中的字符串将不会以空值终止。

因此,当您使用以空终止字符串操作的函数(例如大多数标准库str*()函数)时,请确保使用以下内容对'\0'终结符进行说明:

int length = strlen(*str) + 1;

另外,另外,void *返回的malloc()被隐式转换为任何对象指针类型,因此代替:

word *new_word = (word*)malloc(sizeof(word));

你应该简单地使用:

word *new_word = malloc(sizeof(word));

甚至更好:

word *new_word = malloc(sizeof *new_word);

以避免因更改声明中的指针类型而导致malloc()调用而导致的错误。

sizeof运算符不会计算非可变长度数组表达式,因此这是获取对象大小的更可靠方法。

修改

至于缺少每个字符串的第一个字符,我认为这是由于:

*cur++ = '\0';

因为它只是无用地将cur[0]设置为'\0',然后递增指针;字符串现在从你单词的第二个字母开始。