【问题标题】:Count letters from a File in C用 C 计算文件中的字母
【发布时间】:2021-12-12 00:29:16
【问题描述】:

我正在尝试创建一个程序,该程序从文件中读取并计算文件中每个字母字符的出现次数。以下是我到目前为止的内容,但是返回的计数(存储在 counters 数组中)比预期的要高。

  void count_letters(const char *filename, int counters[26]) {
    FILE* in_file = fopen(filename, "r");
    const char ALPHABET[] = "abcdefghijklmnopqrstuvwxyz";
    if(in_file == NULL){
        printf("Error(count_letters): Could not open file %s\n",filename);
        return;
    }
    char line[200];
    while(fgets(line, sizeof(line),in_file) != NULL){ //keep reading lines until there's nothing left to read
        for(int pos = 0; pos < sizeof(line); pos++){//iterate through each character in line...
            if(isalpha(line[pos])){//skip checking and increment position if current char is not alphabetical
                for(int i = 0; i < 26; i++){//...for each character in the alphabet
                    if(tolower(line[pos]) == tolower(ALPHABET[i]))//upper case and lower case are counted as same
                        counters[i]++;    // increment the current element in counters for each match in the line
                }
            }
        }
    }
    fclose(in_file);
    return;
}

【问题讨论】:

  • pos &lt; sizeof(line) 是一个错误的测试。您正在测试 line (200) 中的所有字符,而不是实际读取的内容。更改为pos &lt; strlen(line)
  • 以后在询问有关调试程序的问题时,请提供minimal reproducible example。这意味着其他人无需任何更改即可编译和运行的完整程序——包括它需要的所有#include 语句和main 例程。它还包括样本输入、观察到的输出和所需的输出。
  • 会做 ^ 谢谢你的建议,我是新来的 :)
  • void 函数的末尾不需要return,它会自动完成。
  • @Dan,在这里修改您的问题标题以将其标记为“已解决”是不习惯或不合适的。由于您已经回滚了一次该编辑的回滚,因此我将标记此问题而不是再次回滚。

标签: c file


【解决方案1】:

for(int pos = 0; pos &lt; sizeof(line); ... 中有错误。您假设数组中的所有 200 个位置都是有效字符,但这仅适用于每行包含 200 个字符的文本。您应该只计算字符串初始化部分中的字符。它的长度因行而异:

for(int pos = 0; pos < strlen(line); ...

此外,您不需要最内层循环,因为所有字母字符很可能都有连续的 ASCII 代码:

if(isalpha(line[pos]))
    counters[tolower(line[pos]) - 'a']++;

我假设 counters 之前已用 0 初始化。如果没有,则必须在计数之前初始化此数组。

【讨论】:

  • C 标准不保证所有 C 实现都使用 ASCII。
  • @EricPostpischil 不,它没有。但是 ASCII 用于接近 100% 的情况,不是吗?
  • 教学生依赖常见案例而不是文档规范会导致错误。这对他们不利,对社会不利。
  • @EricPostpischil 有些人disagree。我认为 this 是一种务实而非迂腐的情况。
【解决方案2】:

for(int pos = 0; pos &lt; sizeof(line); pos++) 中,sizeof(line) 计算为整个数组line 的大小,而不是由最近的fgets 调用填充的部分。因此,在长行之后,循环重复计算数组中剩余的字符以读取短行。

修改循环以仅遍历line 中最近由fgets 填充的部分。您可以通过在看到空字符时退出循环来做到这一点。

【讨论】:

    【解决方案3】:

    您不需要使用 fget,因为字符函数的运行速度与文件系统使用自己的缓冲一样快。

    #define NLETTERS    ('z' - 'a' + 1)
    
    int countLetters(FILE *fi, size_t *counter)
    {
        int ch;
        if(fi && counter)
        {
            memset(counter, 0, sizeof(*counter * NLETTERS));
            while((ch = fgetc(fi)) != EOF)
            {
                if(isalpha(ch))
                {
                    counter[tolower(ch) - 'a']++;
                }
            }
            return 0;
        }
        return 1;
    }
    

    【讨论】:

    • 任何评论静默DV-ter?
    【解决方案4】:

    我花了两分钱买一个更简单的解决方案(你有很多循环;))。在大多数情况下,首选逐行读取输入,但由于您只是在这里计算字符,我认为这不是其中之一,最终会增加复杂性。该答案还假设 ASCII 字符编码,如另一个答案的 cmets 中所述,C 标准不保证这一点。您可以根据需要使用您的 char ALPHABET 进行修改以实现终极便携性

    #include <stdio.h>
    #include <ctype.h>
    
    #define NUM_LETTERS 26
    
    int main(void)
    {
        FILE* in_file = fopen("/path/to/my/file.txt", "r");
        if (in_file == NULL) exit(-1);
    
        unsigned charCounts[NUM_LETTERS] = {0};
        int curChar;
        // rather than reading line-by-line, read one character at a time
        while ((curChar = fgetc(in_file)) != EOF)
        {
            // only proceed if it is a letter
            if (isalpha(curChar))
            {
                // this is bad if not using ASCII, instead you'd need another
                // loop to check your ALPHABET, but increment the count here
                (charCounts[tolower(curChar) - 'a'])++;
            }
        }
    
        // print out the results
        for (int i=0; i<NUM_LETTERS; i++)
        {
            // 'A'+i also assumes ASCII encoding
            printf("%c: %u\n", 'A'+i, charCounts[i]);
        }
    }
    

    Demo 使用 stdin 而不是文件。

    【讨论】:

      猜你喜欢
      • 2021-01-25
      • 1970-01-01
      • 2016-04-22
      • 1970-01-01
      • 2020-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      相关资源
      最近更新 更多