如果符号之间没有space,C计数字程序就可以工作,为什么?

C counting word program works if there is no space between symbols, why?

这是我正在使用的代码,它可以完美运行,除非:

  1. 文本文件以整数开头。
  2. 如果符号之间没有 space,例如 [hi!] 可以完美运行,但 [hi !] 打印 �
  3. 如果我将多个符号放在一起,例如 [??????????],字数将不正确。

澄清一下,如果代码中有整数是可以的,但如果它们在开头则不行。

为什么会这样?我真的很好奇如何解决这个问题。

编辑: 有人向我指出 fscanf 不支持正则表达式,为什么它会正确过滤掉字母?

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

/* counts words in the file */
int main(void) 
{
    FILE *fp;
    int r,n,i; /* a variable for result of a function, returning int */
     /* the words counter */
    const char *filename = "test2.txt"; /* a file name opening for read */
    char word[10]; /* an array for the check if a non-empty word was read */

    if ((fp = fopen(filename, "r")) == NULL) {
        fprintf(stderr, "error: file" "\n");
        return 1;
    }  
    /* if can't open the file for read
       then print an error message and return false to the environment */
    int arraylen = sizeof(word)/sizeof(word[0]); /*write the length of array word to arraylen*/ 
    n = 0; /* turn the counter of words to zero */
    word[0] = '[=11=]'; /* turn the word array to an empty state */
   while ((r = fscanf(fp, "\n%10[^A-Za-z]%*c", word)) == 1) {
        printf("firstoutput\n");
        for(i=0;i<arraylen;i++)

                printf("%c",word[i]);


    if((r = fscanf(fp, "\n%[A-Za-z]%*c", word)) == 0) { /*in case next character is not a 
        letter do nothing, this is in place to prevent the program from getting stuck*/

         printf("secondoutput\n");
        for(i=0;i<arraylen;i++)
            if(word[i] != (' '))
                printf("%c",word[i]);
        }

        if (word[0] != '[=11=]')
            n++;
        /* if the word array got something,
           then it was a word, count it */ 

        word[0] = '[=11=]'; /* turn the word back into an empty state */
    }  
    /* skipping words delimeted by ' ' or '\n' or ','
       while file fp can be read, continue skipping
       and count every skip */


    if (ferror(fp) != 0) { /* check the file for read error if EOF occured */
        fprintf(stderr, "error: read file" "\n");
        fclose(fp);
        return 1;
    }
    /* if there was an error while reading the file
       then print error, close the file (because it was opened though)
       and return false to the environment */

    if (n == 1) /* control "to be" and endings for word or words */
        printf("\nthere is %d word" "\n", n);
    else
        printf("\nthere are %d words" "\n", n);

    fclose(fp); /* close the file */

    return 0; /* return success to the environment */
}

EDIT2:我发布了一个 101% 有效的完整解决方案,我结合了我发现并创建的所有各种技巧和东西,我相信这个解决方案非常紧凑和高效,如果我错了请纠正我!

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

#define WORD "A-Za-z"


int countWords(FILE *f,int* now){
   char ch[100][100];
   int x;
   *now=0;
   int count = 0;
       while (fscanf(f, "%[^" WORD "]",ch[count]) != EOF
        && fscanf(f, "%10[" WORD "]",ch[count]) != EOF) {

        if(count>0) {

        for(x=0;x<count;x++) {

          if((strcasecmp(ch[count],ch[x]))==0) { /* Compare if the two words match, 
          case insensitive*/
            if(fscanf(f, "%[^" WORD "]",ch[count]) != EOF
            && fscanf(f, "%10[" WORD "]",ch[count]) != EOF) /* Since the words match,
            now we have to overwrite the double word by scanning the next set of characters.*/
              printf("String is equal\n");

              (*now)++;
          }
        }
      }
        puts(ch[count]);
        count++;
        (*now)++;
    }
    puts(ch[13]);
    printf("%d\n",*now );

   return count;
}

int main(void){

   int uniquewordCount=0,wordCount=0;
   FILE *rFile = fopen("test2.txt", "r");
   uniquewordCount += countWords(rFile,&wordCount);
   printf("%d\n",(a+b));
   printf("Amount of unique words: %d\n", uniquewordCount);
   printf("Amount of words: %d\n", wordCount);
   return 0;
}

您的第一个扫描命令是

fscanf(fp, "\n%10[^A-Za-z]%*c", word)

这将跳过任何白色-space ("\n"),然后读入最多 10 个不是字母的字符 ("%10[^A-Za-z]") 的缓冲区,最后读取字符在那之后("*c")。

当文件以字母开头时不要进入 while 循环,因为 fscanf 将 return 0,因为它无法扫描非空字符串非字母。如果之前连white space都跳过了,while循环只会在第一个非space字符不是字母的时候才进入,这种可能性不大。

因为您在 fscanf 末尾读了一个额外的字符,所以您的单词会漏读第一个字母。

通过将单词限制为 10 个字母,您可以分块阅读较长的单词,这应该会通过将较长的单词计算为两三个单词来减少您的单词计数。字符缓冲区 word 也应该至少有 11 个字符。

打印字符串的正确方法是printf("%s", word)puts(word)。你的循环基本上没问题,但打印所有十个字母,即使这个词可能有更少的字母。

我不认为使用 fscanf 是计算单词的好方法,但如果你想使用它,你可以这样做:

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

#define WORD "A-Za-z0-9'"

int main(void)
{
    FILE *fp = stdin;
    int n = 0;

    while (fscanf(fp, "%*[^" WORD "]") != EOF
        && fscanf(fp, "%*[" WORD "]") != EOF) {
        n++;
    }

    printf("%d words\n", n);

    return 0;
}

在这里,我们 fscanf 交替处理非单词和单词,并在其中一个 fscanf 发出文件结束信号时结束 lopp。请注意,我们不关心实际内容,因为我们通过不使用 * 星号转换任何内容来跳过它们。这意味着 fscanf 的唯一结果可以是 0 和 EOF.

计算单词的常用方法是读取字符并检测 "context" 何时从非单词转换为单词:

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

int isword(int c)
{
    if ('A' <= c && c <= 'Z') return 1;
    if ('a' <= c && c <= 'z') return 1;
    if ('0' <= c && c <= '9') return 1;
    if (c == '-') return 1;
    if (c == '\'') return 1;
    return 0;
}

int main(void)
{
    FILE *fp = stdin;       // or use a real file, of course
    int word = 0;           // 0: space cntext, 1: word context
    int n = 0;

    for (;;) {
        int c = fgetc(fp);

        if (c == EOF) break;

        if (isword(c)) {
            if (word == 0) n++;
            word = 1;
        } else {
            word = 0;
        }
    }

    printf("%d words\n", n);

    return 0;
}

函数isword在这里定义了什么算作一个词。请注意如何不需要保持两个 fscanf 格式同步。一个字母是一个词的一部分,或者不是。, 这与 else 子句是明确的。