所以,我有一个程序,该程序从命令行获取初始参数,并尝试查看给定输入中它们出现了多少次。
例如在终端中,您将编写如下内容:
./main cat nap dog
然后它会说要查找3个单词,然后输入单词,并在其后输入一个句点:
Looking for words:
cat
cat
nap
.
然后程序将返回:
Result:
cat: 2
nap: 1
dog: 0
我想实现一种方法,以便您可以在一行上输入多个单词,并且仍然可以对它们进行计数。
我具有以下功能:
int process_stream(WordCountEntry entries[], int entry_count)
{
short line_count = 0;
char buffer[30];
while (fgets(buffer, 30, stdin)) {
buffer[strlen(buffer)-1]='\0';
char* token = strtok(buffer, " \n");
while (token != NULL){
token = strtok(NULL, " \n");
int i = 0;
while (i < entry_count+1) {
if (!strcmp(entries[i].word, token))
entries[i].counter++;
i++;
}
line_count++;
}
}
return line_count;
}
我尝试使用“”的定界符标记每个给定的字符串,然后尝试移至下一行。但是,它只会分隔第一个单词,然后给出分段错误。
答案 0 :(得分:2)
有很多错误的方法:
i < entry_count+1
:假设entry_count
为1;从1 <2开始,您将针对未初始化且可能越界的entries[1]
进行测试。我认为您想写< entry_count
。buffer[strlen(buffer)-1]=
-如果strlen为0,则表示您正在写buffer[-1]
-那里还有另一个段错误。我也不知道这条线的目的是什么。token
是否不为空,然后再将其设置为strtok()
。几乎可以肯定,第二次它将为null,您将针对null执行strcmp
。那里还有另一个段错误。您需要将循环移动到循环的下一个strtok
。fgets
始终在换行符处停止,无论如何都不需要strtok装置。答案 1 :(得分:0)
最有可能的问题是将输入行拆分为令牌的循环。在输入数据上两次调用“ strtok”,只执行一次NULL检查。
如果第二个strtok调用返回NULL,则该代码将在strcmp上失败(崩溃)。考虑将循环体修改为:
while (fgets(buffer, 30, stdin)) {
buffer[strlen(buffer)-1]='\0';
if ( strcmp(buffer, ".") == 0 ) break ;
char* token = strtok(buffer, " \n");
while (token != NULL){
int i = 0;
while (i < entry_count+1) {
if (!strcmp(entries[i].word, token))
entries[i].counter++;
i++;
}
line_count++;
// Place strtok here
token = strtok(NULL, " \n"); }
}
还请注意,发布的代码将解决导致每行上的第一个标记被忽略的问题。
答案 2 :(得分:0)
您的函数乱序处理令牌。在将指针token
与以下各项一起使用之前,您将跳过第一个token
,将其完全覆盖:
char* token = strtok(buffer, " \n");
while (token != NULL){
token = strtok(NULL, " \n");
接下来,鉴于您使用" \n"
的分隔符,以下各行是多余的。您尝试用以下代码覆盖的'\n'
永远不会成为token
的一部分:
buffer[strlen(buffer)-1]='\0';
您正在使用'.'
来标记输入结束,因此您不需要处理最后一行。您可以通过使用简单的goto
语句来中断嵌套循环,例如:
while (fgets (buffer, MAXC, stdin)) {
...
while (token != NULL) {
if (!strcmp (token, ".")) /* compare != '.' */
goto done;
...
}
done:;
return line_count;
}
假设entry_count
是entries
中元素的数量,那么您的+1
会使您读取超出entries
的范围,可能会导致SegFault。虽然无法验证是否给出了问题所缺少的完整代码,但看起来像您想要的那样:
while (i < entry_count) { /* +1 causeses UB */
您对token = strtok(NULL, " \n");
的获取下一个标记的调用应该是循环中的最后一个语句,而不是第一个,否则您将跳过一个标记。
将其完全放在一起,看起来您需要类似以下内容:
#define MAXC 128 /* max number of characters per word/line */
...
int process_stream (WordCountEntry entries[], int entry_count)
{
short line_count = 0;
char buffer[MAXC];
const char *delim = " \n"; /* set delim once */
while (fgets (buffer, MAXC, stdin)) {
char *token = strtok(buffer, delim);
while (token != NULL) {
if (!strcmp (token, ".")) /* compare != '.' */
goto done;
int i = 0;
while (i < entry_count) { /* +1 causeses UB */
if (!strcmp(entries[i].word, token))
entries[i].counter++;
i++;
}
line_count++;
token = strtok(NULL, delim); /* now get next token */
}
}
done:;
return line_count;
}
简短示例
根据您的评论,希望每行能够处理单个单词或多个单词。上面的函数可以做到这一点,但是在您尚未发布的代码中,您可能还会遇到其他问题。为了验证上述功能,编写了一个简短的实现。只要您的代码提供相似的输入,上面的函数就可以满足您的需求。使用的简短示例是:
#include <stdio.h>
#include <string.h>
#define NENT 16 /* max number of entries (no. elements in array) */
#define MAXC 128 /* max number of characters per word / line */
typedef struct {
char word[MAXC];
int counter;
} WordCountEntry;
int process_stream (WordCountEntry entries[], int entry_count)
{
short line_count = 0;
char buffer[MAXC];
const char *delim = " \n"; /* set delim once */
while (fgets (buffer, MAXC, stdin)) {
char *token = strtok(buffer, delim);
while (token != NULL) {
if (!strcmp (token, ".")) /* compare != '.' */
goto done;
int i = 0;
while (i < entry_count) { /* +1 causeses UB */
if (!strcmp(entries[i].word, token))
entries[i].counter++;
i++;
}
line_count++;
token = strtok(NULL, delim); /* now get next token */
}
}
done:;
return line_count;
}
int main (int argc, char **argv) {
WordCountEntry wce[NENT] = { { .word = "" } };
int n = 0;
if (argc < 2) {
fputs ("error: insufficient arguments.\n", stderr);
return 1;
}
for (int i = 1; i < (argc < NENT ? argc : NENT); i++) {
for (int j = 0; j < i; j++) {
if (!strcmp (wce[j].word, argv[i]))
goto next;
}
strcpy (wce[i-1].word, argv[i]);
n++;
next:;
}
puts ("Looking for words:");
if (!process_stream (wce, n))
fputs ("(user canceled input)\n", stderr);
puts ("\nResult:");
for (int i = 0; i < n; i++)
printf ("%s: %d\n", wce[i].word, wce[i].counter);
}
使用/输出示例
$ ./bin/wordcountentry cat nap dog
Looking for words:
cat
cat
nap
.
Result:
cat: 2
nap: 1
dog: 0
或每行包含多个单词:
$ ./bin/wordcountentry cat nap dog
Looking for words:
cat dog dog
cat dog
nap .
Result:
cat: 2
nap: 1
dog: 3
您的代码中可能还有其他问题,由于A Minimal, Complete, and Verifiable Example (MCVE)的缺乏,无法确定是否可以解决所有问题,但是考虑到您的情况,这应该可以解决可识别的问题可能还有其他。
如果您还有其他问题,请告诉我。