我正在尝试计算传递到数组中的字符串的每个单词中的所有音节。
一个音节算作两个彼此相邻的元音(a、e、i、o、u、y)。例如,“peel”中的“ee”算作 1 个音节。但是,“juked”中的“u”和“e”算作2个音节。单词末尾的“e”不算作音节。此外,即使前面的规则不适用,每个单词至少有 1 个音节。
我有一个文件,其中包含我传递到数组中的大字符串(单词、空格和换行符)。 我有代码通过计算每个单词和换行符之间的空格来计算每个单词。见下文:
for (i = 0; i < lengthOfFile; i++)
{
if (charArray[i] == ' ' || charArray[i] == '\n')
{
wordCount++;
}
}
在哪里charArray
是传递到数组中的文件 (freads)
和 lengthOfFile
是由 (fseek)
计算的文件中的总字节数和 wordCount
是统计的总字数。
从这里开始,我需要以某种方式计算数组中每个单词的音节数,但不知道从哪里开始。
最佳答案
如果您仍然遇到问题,那只是因为您对问题想得太多了。每当您计数、确定频率等时,您通常可以通过使用“状态循环”来简化事情。状态循环只不过是一个循环,您可以在其中遍历每个字符(或其他任何字符)并处理您发现自己所处的任何状态,例如:
- 我读过任何字符吗? (如果不是,处理该状态);
- 当前字符是空格吗? (如果是这样,为简单起见,假设没有多个空格,则您已到达单词的末尾,处理该状态);
- 当前字符是非空格非元音字符吗? (如果是这样,如果我的最后一个字符是元音,增加我的音节数);和
- 不管当前字符的分类如何,我需要做什么? (输出它,设置 last = current,等等。)
基本上就是这样,可以将其转换为一个循环,其中包含多个测试来处理每个状态。您还可以添加检查以确保像 "my"
和 "he"
这样的词被计为单个音节,方法是在您到达词尾。
总而言之,您可以编写如下基本实现:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main (void) {
char c, last = 0; /* current & last char */
const char *vowels = "AEIOUYaeiouy"; /* vowels (plus Yy) */
size_t syllcnt = 0, totalcnt = 0; /* word syllable cnt & total */
while ((c = getchar()) != EOF) { /* read each character */
if (!last) { /* if 1st char (no last) */
putchar (c); /* just output it */
last = c; /* set last */
continue; /* go get next */
}
if (isspace (c)) { /* if space, end of word */
if (!syllcnt) /* if no syll, it's 1 (he, my) */
syllcnt = 1;
printf (" - %zu\n", syllcnt); /* output syll cnt and '\n' */
totalcnt += syllcnt; /* add to total */
syllcnt = 0; /* reset syllcnt to zero */
} /* otherwise */
else if (!strchr (vowels, c)) /* if not vowel */
if (strchr (vowels, last)) /* and last was vowel */
syllcnt++; /* increment syllcnt */
if (!isspace (c)) /* if not space */
putchar (c); /* output it */
last = c; /* set last = c */
}
printf ("\n total syllables: %zu\n", totalcnt);
}
(注意:如上所述,这个简单的示例实现不考虑单词之间的多个空格——您可以通过检查 !isspace (last)
。你能找出应该添加该检查的位置吗,提示:它已添加到带有 &&
的现有检查中——微调留给你)
示例使用/输出
$ echo "my dog eats banannas he peels while getting juked" | ./syllablecnt
my - 1
dog - 1
eats - 1
banannas - 3
he - 1
peels - 1
while - 1
getting - 2
juked - 2
total syllables: 13
如果您需要从文件中读取单词,只需将文件作为输入重定向到 stdin
上的程序,例如
./syllablecnt < inputfile
编辑 - 从文件读取到动态分配的缓冲区
根据有关要从文件(或 stdin
)读取到动态大小的缓冲区然后遍历缓冲区以输出每个单词的音节数和总音节数的评论,您可以这样做类似于下面的内容,它只是将文件中的所有字符读入最初分配的包含 8 个字符的缓冲区,并根据需要重新分配(每次需要 realloc
时将分配大小加倍)。这是一个相当标准和合理有效的缓冲增长策略。您可以根据自己的喜好随意增加它的大小,但要避免许多小的 rabbit-pellet 重新分配,因为从计算的角度来看,内存分配相对昂贵。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define NCHAR 8 /* initial characters to allocate */
int main (int argc, char **argv) {
char c, last = 0, *buffer; /* current, last & pointer */
const char *vowels = "AEIOUYaeiouy"; /* vowels */
size_t syllcnt = 0, totalcnt = 0, /* word syllable cnt & total */
n = 0, size = NCHAR;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("fopen-file");
return 1;
}
/* allocate/validate initial NCHAR buffer size */
if (!(buffer = malloc (size))) {
perror ("malloc-buffer");
return 1;
}
while ((c = fgetc(fp)) != EOF) { /* read each character */
buffer[n++] = c; /* store, increment count */
if (n == size) { /* reallocate as required */
void *tmp = realloc (buffer, 2 * size);
if (!tmp) { /* validate realloc */
perror ("realloc-tmp");
break; /* still n good chars in buffer */
}
buffer = tmp; /* assign reallocated block to buffer */
size *= 2; /* update allocated size */
}
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < n; i++) { /* loop over all characters */
c = buffer[i]; /* set to c to reuse code */
if (!last) { /* if 1st char (no last) */
putchar (c); /* just output it */
last = c; /* set last */
continue; /* go get next */
}
if (isspace(c) && !isspace(last)) { /* if space, end of word */
if (!syllcnt) /* if no syll, it's 1 (he, my) */
syllcnt = 1;
printf (" - %zu\n", syllcnt); /* output syll cnt and '\n' */
totalcnt += syllcnt; /* add to total */
syllcnt = 0; /* reset syllcnt to zero */
} /* otherwise */
else if (!strchr (vowels, c)) /* if not vowel */
if (strchr (vowels, last)) /* and last was vowel */
syllcnt++; /* increment syllcnt */
if (!isspace (c)) /* if not space */
putchar (c); /* output it */
last = c; /* set last = c */
}
free (buffer); /* don't forget to free what you allocate */
printf ("\n total syllables: %zu\n", totalcnt);
}
(您可以使用 fgets
或使用 POSIX getline
执行相同的操作,或者使用您的 fseek/ftell
或 stat
然后 fread
在一次调用中将整个文件放入缓冲区 - 由您决定)
内存使用/错误检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向起始地址的指针内存块,因此,(2) 它可以在不再需要时被释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出您分配的 block 的边界,尝试读取或基于未初始化的值进行条件跳转,最后, 以确认您释放了所有已分配的内存。
对于 Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
$ valgrind ./bin/syllablecnt_array dat/syllables.txt
==19517== Memcheck, a memory error detector
==19517== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==19517== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==19517== Command: ./bin/syllablecnt_array dat/syllables.txt
==19517==
my - 1
dog - 1
eats - 1
banannas - 3
he - 1
peels - 1
while - 1
getting - 2
juked - 2
total syllables: 13
==19517==
==19517== HEAP SUMMARY:
==19517== in use at exit: 0 bytes in 0 blocks
==19517== total heap usage: 5 allocs, 5 frees, 672 bytes allocated
==19517==
==19517== All heap blocks were freed -- no leaks are possible
==19517==
==19517== For counts of detected and suppressed errors, rerun with: -v
==19517== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。
关于计算数组中的音节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54624166/