【问题标题】:Storing tokens in struct在结构中存储令牌
【发布时间】:2018-02-07 16:03:55
【问题描述】:

对于重复的问题,我很抱歉,但我对 C 编程非常陌生,无法理解如何在我自己的代码中实现相同顶部的先前答案。

我要从磁盘上的文件或标准输入中读取文本,对单词进行排序,然后向用户显示单词出现的列表(出现次数最多的单词在顶部,然后按降序排列)。

我目前坚持将我的标记化单词存储为一种合适的方式,以便以后能够对它们进行计数和排序。我决定使用结构。

我编写了一个测试文件,在其中使用来自 stdin 的 fgets 为其提供数据。

这是代码:

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

int main(int argc, char const *argv[])
{
    struct words
    {
        char word[500];
        unsigned int count;
    };

    int size = 500;
    char *buffer;
    char token;
    struct words w;

    #ifdef DEBUG
    printf("--!DEBUG INFO!-- \n Right before the 4-loop now\n--!DEBUG INFO!--\n");
    #endif
    for (int i = 0; i < 10; ++i)
    {
        printf("Please enter word\n");
        fgets(buffer, size, stdin);
        #ifdef DEBUG
        printf("--!DEBUG INFO!-- \n %c\n--!DEBUG INFO!--\n", buffer);
        #endif
        token = strtok(buffer[i], "\n");
        strcpy(w.word[i], token);
        #ifdef DEBUG
        printf("--!DEBUG INFO!-- \n %c\n--!DEBUG INFO!--\n", w.word[i]);
        #endif
    }

    for (int i = 0; i < 10; ++i)
    {
        printf("%c\n", w.word[i]);
    }
    return 0;
}

在编译时,我收到一大堆警告消息,其中大多数都表示类似以下内容:

incompatible pointer to integer conversion assigning to 'char' from
      'char *'; dereference with * [-Wint-conversion]
                token = strtok(buffer[i], "\n");

该程序确实会编译并运行,直到我给它数据并按回车键。之后,它会因分段错误而崩溃:11 条消息

./tok_struct 
--!DEBUG INFO!-- 
 Right before the 4-loop now
--!DEBUG INFO!--
Please enter word
Test 
Segmentation fault: 11

我非常感谢能得到任何帮助!

【问题讨论】:

  • 把这一切#ifdef DEBUG 废话,学习使用调试器。
  • 我很乐意这样做,但该模块基于学习非常基本的方法,即在我的情况下终端和崇高文本。
  • 好吧,为了课程,按照你的方式去做,为了找到一份好工作,按照我的方式去做。也就是说,双向进行。
  • @Bathsheba 上三个月在 Eclipse 中编写 Java 之后,我非常热衷于使用调试器,并且将来会这样做。但是,我也确实看到了不这样做以了解后台实际发生的事情的意义。 :)
  • 这很奇怪——我一直认为调试器会准确地告诉你后台发生了什么。

标签: c struct token


【解决方案1】:

一方面缓冲区需要分配一个大小,它似乎只是 代码中未初始化的指针。

一旦你做 fgets(buffer,...) 你进入未定义的行为领域,如果 buffer 没有指向可以存储输入的地方。

所以首先将缓冲区声明为数组

char buffer[512]; // or whatever size you deem is appropriate

然后将行读入缓冲区(而不是 for 循环使用 while,如果用户没有输入任何内容,您可以检查行长并退出循环)

while (fgets(buffer,sizeof(line),stdin) != NULL)
{
  char* token = strtok(buffer, "\n"); 
  if (token != NULL)
  {
   // in order to get a pointer to the rest of the words you 
   // need to call strtok multiple times and with another 
   // separator since one can assume that there is space between
   // the words e.g.  char* token = strtok(buffer, " \n"); 
   // and to process all words in the line:
   // for (char* token = strtok(buffer, " \n";
   //       token != NULL; 
   //       token = strtok(NULL, " \n"))
   // {
   //  .. here you store your tokens
   // }
  }
}

要存储令牌,你不能拥有你所拥有的结构 char word[500] 只是一个字符数组,因此在其中进行索引 数组并将其作为 strcpy 的目标是没有意义的。

相反,您需要有一个结构数组。

struct words w[200]; // or how many words you are expected to handle

现在对于您发现的每个单词,您需要查看数组,如果它 已经存在,如果是,则递增计数器,否则复制到 word 中并设置 计数器为 1。您应该初始化数组以确保将其设置为 0。跟踪数组中有多少个单词,例如wordsFound

int wordsFound = 0;
for (char* token = strtok(buffer, " \n"; token != NULL; token = strtok(NULL, " \n"))
{
  ...
}

最后一点:strtok 修改了传递给它的参数,因此您不能存储返回的指针。要么你需要像上面那样复制它,要么你需要分配空间然后复制到它。

通常不会有一个单词数组,而是例如一个单词的链接列表,每当找到一个新单词时就会增长,当然这个例子可以扩展以获得更好的查找等,但我想这不是你的暂时的目标。

【讨论】:

    【解决方案2】:

    strcpy(w.word[i], token);正在传递 char 而不是 char*。您可以在结构本身中有一个 2d 数组,同样的方法可以使 count 包含每个单词的频率。

    struct words
    {
        char word[MAXWORDS][MAXLETTERINWORD];
        unsigned int count[MAXWORDS];
    };
    

    然后打印每个单词将是printf("%s\n", w.word[i]);。你还需要strtok吗?因为毕竟您在每次循环迭代中都使用fgets 获取输入。 strtok 将运行一次。您可以将缓冲区本身复制到word[index]

    缓冲区不指向任何内存。以这种方式使用缓冲区是UB。将buffer 声明为能够容纳一行的char 数组。char buffer[MAXLEN];。然后获取输入将是

    if(fgets(buffer,MAXLEN,stdin)==NULL){
        fprintf(stderr,"Error in input\n");
        exit(EXIT_FAILURE);
    }
    

    【讨论】:

      猜你喜欢
      • 2019-03-30
      • 1970-01-01
      • 2021-04-12
      • 2011-12-02
      • 2021-10-07
      • 1970-01-01
      • 2019-02-01
      • 2016-02-18
      • 2021-01-14
      相关资源
      最近更新 更多