【问题标题】:Why do these two methods of counting words differ significantly?为什么这两种计算单词的方法有显着差异?
【发布时间】:2015-03-27 19:42:21
【问题描述】:

我编写了一个程序,允许用户在任何文本文件中查找单词或单词集合的实例数。用户可以在命令行中输入如下内容:

$ ./wordCount Mars TripToMars.txt

在“火星之旅”一书中搜索“火星”一词的实例数,或

$ ./wordCount -f collectionOfSearchWords.txt TripToMars.txt

在collectionOfSearchWords.txt 的各个行中搜索多个单词的实例数。

为确保程序正确,我使用了 grep 命令:

$ grep -o 'Mars' TripToMars.txt | wc -w

$ grep -o -w 'Mars' TripToMars.txt | wc -w

第一个命令查找单词在任何地方的实例数,其中包括“Marsa”、“Marseen”、“Marses”等术语,而第二个命令仅查找“Mars”作为独立的实例词,其中包括尾随标点符号,例如“Mars.”、“Mars!”、“Mars?”等。

两个 grep 命令都返回 49 作为书中“Mars”实例的数量。

当我在下面的 while 循环中使用代码时(为简单起见,我只包含相关代码),程序返回 49。太棒了!

FILE *textToSearch;
char *readMode = "r";

int count;
char nextWord[100];
char d;

textToSearch = fopen(argVector[argCount-1], readMode);
if (textToSearch == NULL) {
    fprintf(stderr, "Cannot open %s to be searched\n", argVector[argCount-1]);
    return 1;
} else {
    while (fscanf(textToSearch, "%*[^a-zA-Z]"), fscanf(textToSearch, "%80[a-zA-Z]", nextWord) > 0) { 

        // increment the counter if the word is a match
        if (strcmp(nextWord, argVector[word]) == 0) {
            count++;
        }
    }
}

但是当我用这个 while 循环代替前一个循环时,程序返回 17。

while(1) {
    d = fscanf(textToSearch, "%s", nextWord);
    if (d == EOF) break;

    // increment the counter if the word is a match
    if (strcmp(nextWord, argVector[word]) == 0) {
        count++;
    }
}     

那么,两者最大的区别是什么

while (fscanf(textToSearch, "%*[^a-zA-Z]"), fscanf(textToSearch, "%80[a-zA-Z]", nextWord) > 0) {}

while(1) {
    d = fscanf(textToSearch, "%s", nextWord);
    if (d == EOF) break;
}

?

编辑

我添加了这段代码:

if (strcmp(nextWordDict, nextWord) == 0 ||
     strcmp(nextWordDict, strcat(nextWord, ".")) == 0 ||
     strcmp(nextWordDict, strcat(nextWord, "?")) == 0 ||
     strcmp(nextWordDict, strcat(nextWord, "!")) == 0 ||
     strcmp(nextWordDict, strcat(nextWord, ",")) == 0) {
        count++;
 }

生成 17 的代码让 Mars 尝试解释带有尾随标点符号的情况,但没有任何变化。还是 17 岁。

EDIT2

正如 John Bollinger 在下面正确指出的那样,这段代码什么都不做,因为缓冲到 nextWord 中的字符串已经有尾随标点符号,并且代码只会添加更多。这是我的错误想法。

【问题讨论】:

  • 不知道,但很容易找到。在每个fscanf 之后只需printf("%s\n",nextWord)。将输出重定向到几个文件并比较这些文件。
  • 另外,在工作代码中,您可以使用此格式字符串"%*[^a-zA-Z]%80[a-zA-Z]" 将两个fscanf 合并为一个。在我写这篇文章时,我突然想到你可能有缓冲区溢出。非工作代码中的格式字符串应为"%80s",以将字长限制为80。
  • 感谢您的回复。对“%80s”的更改产生相同的结果
  • 不,您不能安全地将两个 scanf() 调用合二为一。当第一个编辑描述符与输入项不匹配时,结果行为会有所不同。
  • @JohnBollinger 好点,谢谢。

标签: c regex text scanf text-parsing


【解决方案1】:

当你说命令时你是不正确的......

$ grep -o -w 'Mars' TripToMars.txt | wc -w

...“仅发现 'Mars' 作为独立词的实例”,或者至少该陈述在上下文中具有误导性。该命令查找不属于较大单词的“Mars”实例,其中“单词”定义为由字母、数字和/或下划线组成的连续字符串。特别是,它会匹配“Mars”,它后面跟着一个标点符号,这与您似乎声称的内容相冲突。

但是您的两种扫描方法有什么区别?嗯,这个……

while (fscanf(textToSearch, "%*[^a-zA-Z]"),
        fscanf(textToSearch, "%80[a-zA-Z]", nextWord) > 0) { /* ... */ }

... 扫描零个或多个非拉丁字母字符,忽略是否匹配以及是否发生任何输入错误,然后扫描最多 80 个拉丁字母的连续序列,将该序列记录在 nextWord 缓冲区中.

另一方面,这...

while(1) {
    d = fscanf(textToSearch, "%s", nextWord);
    if (d == EOF) break;
}

... 忽略前导空格,然后将下一个连续的非空格字符串扫描到nextWord

两者在处理既不是拉丁字母也不是空格的字符方面有很大不同:前者忽略它们,而后者将它们包含在nextWord 中。然后当您将nextWord 与字符串"Mars" 进行比较时,后者会丢失

去火星。

“火星”这个名字

火星上有水吗?

因为相邻的标点符号包含在比较中。您的文本很可能有许多与这些类似的结构,而您的 grep 命令没有另外说明。

【讨论】:

  • 哎呀,我对 grep 的 -w 标志有错误的想法,感谢您为我更正。我继续添加代码以包含尾随标点符号(请参阅上面的编辑),我得到了与 17 相同的结果。
  • 你的逻辑倒退了。我假设在许多情况下nextWord已经在一侧或两侧都有标点符号。添加更多不会改善您的匹配。此外,strcat() 修改了它的左参数,所以你最终会附加一整串标点符号。你还需要担心左边的标点符号。为什么不直接使用你的第一种方法,它有效,并且很好地表达了你真正想要的东西?
  • 谢谢。我没有清楚地考虑该代码在做什么。现在很有意义。我使用我的第一种方法,但我只是想了解为什么第二种方法不起作用。感谢您清理它:)
  • 两个while循环都不是万无一失的。建议使用状态处理按字符输入文件字符。当找到“M”时,更改为第二个状态,寻找“a”等,并且在每次失败时,使用当前字符退回到第一个状态。重复直到EOF。如果达到找到整个“火星”字符串的状态,则碰撞计数器。
  • @user3629249,除了 80 个字符的字长限制之外,您发现第一个循环有什么问题?
猜你喜欢
  • 2014-01-10
  • 1970-01-01
  • 1970-01-01
  • 2015-09-11
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 2014-11-18
  • 1970-01-01
相关资源
最近更新 更多