我得到了这个我无法解决的练习,重点是创建一个你输入文本的程序,然后程序分析文本的每个单词并计算每个单词的元音,然后程序返回屏幕显示有3个或更多不同元音的单词数量,不同意思是,如果单词有3个“a”则无关紧要,它只计为1个(单词有元音“a”,它不是不管多少次,例如,单词“above”有3个元音,单词“been”有1个元音,单词“example”有2个元音。元音可以是大写或小写,没关系,这里是棘手的部分:它不能包含我们制作的任何指针或功能。
我所做的是要求用户逐字输入,以便程序分析每个单词,然后在最后返回包含3个或更多元音的单词数,但我觉得必须有一个更简单的方法,用户可以键入完整的段落或文本,然后程序分析每个单词并返回具有3个或更多不同元音的单词数。
无论如何,我的代码如下,任何建议将不胜感激:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
main() {
int vowels, text, words, c, total=0,a=0,e=0,i=0,o=0,u=0;
printf ("How many words does your text has? ");
scanf("%d",&words);
for(c=1;c<=words;c++){
printf("Type your word %d, after that press enter, then press 'control' and 'z' at the same time, and then press enter again: \n", c);
while (EOF != (text=getchar())){
if (text == 'a' || text == 'A'){
a++;
if (a >=2){
a = 1;
}
}
if (text == 'e' || text == 'E'){
e++;
if (e >=2){
e = 1;
}
}
if (text == 'i' || text == 'I'){
i++;
if (i >=2){
i = 1;
}
}
if (text == 'o' || text == 'O'){
o++;
if (o >=2){
o = 1;
}
}
if (text == 'u' || text == 'U'){
u++;
if (u >=2){
u = 1;
}
}
}
vowels = a+e+i+o+u;
if(vowels >=3){
total = total +1;
}
a=0,e=0,i=0,o=0,u=0;
vowels = 0;
}
printf("\n\nThe total of words with 3 or more vowels is: %d", total);
printf("\n");
total=0;
return 0;
}
为了阅读和分析单个单词或段落单词以确定包含至少三个不同元音(任何情况)的单词数量,这是用scanf
读取输入的罕见时间之一(使用'%s'
格式说明者)实际上是一个合理的选择。
回想一下'%s'
格式说明符将读取第一个空格的字符。这为您提供了一种从stdin
一次读取单词的简单方法。要结束输入,用户只需输入EOF
(或Windows上的ctrl+d
)即可生成ctrl+z
。这符合您的段落要求。
对于解析,您可以利用将每个字符转换为小写以简化元音检查。使用5
元素的频率数组提供了一种简单的方法来跟踪每个单词中找到的不同元音的数量。然后,在增加具有三个不同元音的单词的总字数之前,最终测试以查看发现的元音数是否等于所需数字。
一个简单的实现类似于:
#include <stdio.h>
enum { NREQD = 3, NVOWEL = 5, MAXC = 128 }; /* declare constants */
int main (void) {
char word[MAXC] = ""; /* word buffer */
size_t wordcnt = 0; /* words with 3 different vowels */
printf ("enter a word(s) below, [ctrl+d on blank line to end]\n");
for (;;) {
int vowels[NVOWEL] = {0}, /* frequency array */
vowelcnt = 0, /* vowels per-word */
rtn; /* scanf return */
if ((rtn = scanf ("%127s", word)) == EOF) /* chk EOF */
break;
for (int i = 0; word[i]; i++) { /* loop over each char */
if ('A' <= word[i] && word[i] <= 'Z') /* check upper */
word[i] ^= 'a' - 'A'; /* convert to lower */
switch (word[i]) { /* check if vowel */
case 'a': vowels[0] = 1; break;
case 'e': vowels[1] = 1; break;
case 'i': vowels[2] = 1; break;
case 'o': vowels[3] = 1; break;
case 'u': vowels[4] = 1; break;
}
}
for (int i = 0; i < NVOWEL; i++) /* loop over array */
if (vowels[i]) /* check index */
vowelcnt++; /* increment vowelcnt */
if (vowelcnt >= NREQD) /* do we have at least 3 vowels? */
wordcnt++; /* increment wordcnt */
}
printf ("\nThere are %zu words with %d different vowels.\n",
wordcnt, NREQD);
}
示例使用/输出
$ ./bin/vowelcnt
enter a word(s) below, [ctrl+d on blank line to end]
Everyone Understands That The Dictionary Doesn't Track
Words That Contain Vowels Like It Does Etimology.
There are 4 words with 3 different vowels.
仔细看看,如果您有其他问题,请告诉我。
您可以使用fgets
读取整行。我不知道你如何定义段落,你的意思是只是一个长文本或一组行?您可以在控制台中复制和粘贴多行,如果使用fgets
循环,则可以获得所有行。但是允许用户一次输入多行,这更加棘手,因为您应该知道用户将输入多少行。这就是为什么我会说要专注于逐行阅读文本。
您的解决方案按字符读取字符,而忽略非元音。没关系,但你没有发现你应该做的话。 for
循环毫无意义,因为在第一次迭代中,您输入的while
循环只会在没有更多字符从stdin
读取时离开。因此,for
循环的下一次迭代将不会进入while
循环,您将不再阅读任何内容。
你也在重复太多的代码,我知道你的任务就是不要使用你自己的函数,但是这可以通过使用字符作为数组的索引来创建一个字符数组,通过一个简单的查找表来改进。我会在代码中解释一下。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
int main(void)
{
char line[1024];
// initializing look ups with 0
int lookup_vowels[1 << CHAR_BIT] = { 0 };
// using 'a', 'e' as index for the lookup table
// if you want to know if a character is a vowel,
// lookup_vowels[character] will be 1 if character is
// a vowel, 0 otherwise
lookup_vowels['a'] = lookup_vowels['e'] = lookup_vowels['i'] =
lookup_vowels['o'] = lookup_vowels['u'] = 1;
// for parsing word with strtok
const char *delim = " \t\r\n";
int num_of_words = 0;
printf("Enter some text, to end input press ENTER and then CTRL+D\n");
while(1)
{
if(fgets(line, sizeof line, stdin) == NULL)
break;
// parsing words
char *word = strtok(line, delim);
if(word == NULL)
continue; // the line has only delimiters, ignore it
do {
// will be access with the same principle as the lookup
// table, the character is the index
int present[1 << CHAR_BIT] = { 0 };
size_t len = strlen(word);
for(size_t i = 0; i < len; ++i)
{
// I'll explain later the meaning
int c = tolower(word[i]);
if(lookup_vowels[c])
present[c] = 1; // set the present for a vowel to 1
}
int count = present['a'] + present['e'] + present['i'] + present['o']
+ present['u'];
if(count > 2)
{
printf("'%s' has more than three distinct vowels\n", word);
num_of_words++;
}
} while((word = strtok(NULL, delim)));
}
printf("The number of word with three or more distinct vowels: %d\n", num_of_words);
return 0;
}
让我快速解释一下我在这里使用的一些技术:
查找表是一个256的数组,因为char
是8位1值,可以有256个不同的值(范围[0,255])。这个想法是这个数组初始化为0整体(int lookup_vowels[1<<CHAR_BIT] = { 0 };
),然后我只在5个位置设置为1:在元音的位置使用他们的ASCII值作为索引。
因此,如果检查,而不是做重复任务
// where c is a char
if(c == 'a' || c == 'A')
a=1;
}
对于所有元音,我只能这样做
int idx = tolower(c);
if(lookup_vowels[idx])
{
// c is a vowel
}
present
变量函数类似于查找表,这里我使用元音的ASCII代码作为索引,如果word
中存在元音,则将其设置为1。扫描word
中的所有字符后,我将存储在present
中的所有值加起来。如果该值大于2,则该单词具有至少3个或更多不同的元音,并且计数器变量增加。
函数strtok
用于使用定义的分隔符集分割行,在本例中为空字符,制表符,回车符和换行符。要开始解析该行,必须使用源字符串作为第一个参数并将分隔符作为第二个参数来调用strtok
。所有其他后续调用必须将NULL
作为第一个参数传递。该函数返回指向下一个单词的指针,并在找不到更多单词时返回NULL
。
找到单词后,它会计算不同元音的数量并检查此数字是否大于2。
fotenotes
CHAR_BIT
中定义的1limits.h
返回字节的位数。通常一个字节是8位宽,所以我可以写入256。但是有一些“异国情调”的架构,其中一个字节不是8位长,所以通过做1<<CHAR_BIT
,我得到了正确的维度。