如何逐字计算文件中的元音数量并将结果附加到单词上?

How to count number of vowels from file word by word and attach the results to the words?

我正在尝试用 C 编写代码,从文件中读取 n 个单词并逐字修改内容。

程序将计算每个单词中的元音数量。如果当前单词中的元音数量是偶数,程序将以两个为对交换元音(如果是奇数则不交换),然后它将元音数量附加到单词上。

例如,如果单词是 apple ,修改后的单词将看起来像 eppla_2vow .

我的问题是我不太清楚应该如何逐字修改。

     FILE *f = fopen("input.dat","r");
     int i;
     int bufflen=256;
     char buff[bufflen];

     while(n)
     {
      fscanf(f,"%s",buff);
      n--;
     }

     int vowels=0;

     for(i=0; buff[i]!='[=10=]'; i++)
     {
          if (buff[i] == 'a' || buff[i] == 'e' || buff[i] == 'i' ||
          buff[i] == 'o' || buff[i] == 'u' || buff[i] == 'A' ||
          buff[i] == 'E' || buff[i] == 'I' || buff[i] == 'O' ||
          buff[i] == 'U')
          { vowel++;}

          if (buff[i] == ' ')
          {
           vowels=0;
          }
     }

我什至不确定我是否以正确的方式遍历字符串以便能够进行这些修改。

我再举个例子,假设文件的内容是:

apple juice strawberry can make pineapple

修改后的单词如下所示:

eppla_2vow juice_3vow strewbarry_2vow can_1vow meka_2vow penieppla_4vow

我会重组程序以使事情变得更容易。这是一个大纲:

  • 获取输入文件大小,例如stat()
  • 分配 size+1 个字节并在末尾放置一个空字节
  • 将文件读入分配的缓冲区
  • 定义一个足够 space 的小缓冲区用于一个单词 + _XXvow 字节
  • 使用strtok()
  • 循环扫描分配的缓冲区
  • strtok() return 地址复制到小型本地缓冲区
  • 修改小缓冲区中的字节(复制期间或之后)并输出修改后的字

额外:您可以为所有字符创建一个字节数组,而不是对元音字符进行大量测试,将所有内容初始化为零,然后用替换字母填充元音位置;当任何索引字节非零时,用新元音替换元音并增加元音计数。这种方法通过索引数组而不是许多(测试+分支)来提高效率。

这可以分解成几个子任务:

首先,我们需要一个函数来计算字符串中元音的数量。这很简单。我们可以将其概括为计算一个字符串中任何字符在另一个字符串中出现的次数,而不会出现任何问题。

其次,我们需要一个函数来交换字符串中的元音。两分球技术似乎是实现这一目标的方法。当 front 和 back 指针没有相遇时,向前迭代 front 指针,直到它落在元音上。在这一点上,递减反向指针直到它碰到一个元音并交换它们。继续这样交换,直到指针交叉。如上,很容易通过字符列表换成字符串。

除此之外,其他一切都是样板文件——在空格和 IO 上对字符串进行分块。将结果写回文件而不是字符串,可以更轻松地处理 _Nvow 要求的重新分配;您可以使用 fprintf 将单词粘在一起并计数。

综合起来:

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

int str_count_in_chars(char *start, char *end, char *chars) {
    int count = 0;

    for (; start != end; count += !!strchr(chars, *(start++)));

    return count;
}

void str_swap_in_chars(size_t str_len, char **str, char *chars) {
    for (int front = 0, back = str_len - 1; front < back; front++) {
        if (strchr(chars, (*str)[front])) {
            for (; !strchr(chars, (*str)[back]); back--);

            char tmp = (*str)[front];
            (*str)[front] = (*str)[back];
            (*str)[back--] = tmp;
        }
    }
}

char *file_to_str(FILE *fin) {
    int buf_len = 64;
    char buf[buf_len];
    char *str = malloc(buf_len);
    str[0] = '[=10=]';

    for (int i = 1; fgets(buf, buf_len, fin); i++) {
        if (!(str = realloc(str, i * buf_len))) {
            fprintf(stderr, "%s:%d realloc failed\n", __FILE__, __LINE__);
            exit(1);
        }

        strcat(str, buf);
    }

    return str;
}

int main() {
    char *vowels = "aeiou";
    FILE *fin = fopen("input.dat", "r");
    FILE *fout = fopen("output.dat", "w");

    if (!fin || !fout) {
        fprintf(stderr, "%s:%d fopen failed\n", __FILE__, __LINE__);
        exit(1);
    }

    char *words = file_to_str(fin);
    fclose(fin);
    int words_len = strlen(words);

    for (int i = 0; i < words_len;) {
        if (isspace(words[i])) {
            fputc(words[i++], fout);
            continue;
        }

        int start = i;

        for (; i < words_len && !isspace(words[i]); i++);

        char *word = words + start;
        int word_len = i - start;
        int vowel_count = str_count_in_chars(word, words + i, vowels);

        if (vowel_count % 2 == 0) {
            str_swap_in_chars(word_len, &word, vowels);
        }

        fprintf(fout, "%.*s_%dvow", word_len, word, vowel_count);
    }

    fclose(fout); 
    free(words);
    return 0;
}

执行此操作后,output.dat 包含:

eppla_2vow juice_3vow strewbarry_2vow can_1vow meka_2vow penieppla_4vow