Show words from a file with a minimum given number

2019-08-21 10:01发布

问题:

I'm trying to solve a problem and i can't figure out how to do it. The problem says that given an input file which has on the first line a number k and on the next lines some words, K is the minimum number of vowels that a word needs to have to be printed in the console.

Example:
Input: test.in
cars
are...great and awesome

Output:
If k is 2 the result should be:
are
great
awesome
And also it should not take into account spaces or other characters like .,!,? and so on.

What i have so far:

 int main(){

    ifstream fin("dataTest.in");
    char s[250];
    int k;
    fin>>k;
    int nrVowels=0;

    while(fin>>s) {

         int n = strlen(s);

            for(int i=0; i < n; i++)

            {
                    if (s[i] == 'a' ||  s[i] == 'e' ||  s[i] == 'o' ||  s[i] == 'i' || s[i] == 'u'){
                        nrVowels++;
                        if(nrVowels == k){
                            cout<<"Here i guess i should print the word?";
                        }
                    }
            }
    }

}

As you can see from the code my problem is that i'm not really sure how i should print the word and how would i know where the word is ending because i don't want to print only a part of the word. Do you guys have any ideas on how i should do this?

回答1:

std::ifstream file("Read.txt");
int number;
file>>number;
std::string str; 
auto vowels = [](char c){return c == 'a' ||  c == 'e' ||  c  == 'o' ||  c  == 'i' || c  == 'u';};
while (std::getline(file, str))
{
    if(std::count_if(str.cbegin(), str.cend(), vowels) >= number){
        std::cout<<str<<'\n';
    }
}


回答2:

Thanks for answering! I've modified my code with this based on your help:

int main(){

    ifstream fin("dateTest.in");
    char s[250];
    int k;
    fin>>k;
    int nrVowels=0;

    while(fin>>s) {

         int n = strlen(s);
            nrVowels=0;
            for(int i=0; i < n; i++)

            {
                    if (s[i] == 'a' ||  s[i] == 'e' ||  s[i] == 'o' ||  s[i] == 'i' || s[i] == 'u'){
                        nrVowels++;

                        if(nrVowels >= k){
                            cout<<s<<" ";
                            break;
                        }

                    }

            }
    }

}

It kind of works now but i need also to separate words that have.,? and so on. How should that be done?



回答3:

Your requirements state that "K is the minimum number of vowels that a word needs to have to be printed in the console", which would translate to the greater or equal condition

if (nrVowels >= k) { /* print word */ }

However, your code prints the word as soon as k vowels are found, so you can keep your condition as is. But you should break the inner loop once the required count was reached.

Also, as it was commented already, you need to reset the nrVowels counter for each word that you inspect. Better yet: make nrVowels a local scope variable within the body of your while-loop.

Some hints regarding your code that are not strictly necessary to solve the problem:

  • use std::string instead of char arrays.
  • use a single variable that contains all your vowels
  • don't count vowels, just ensure there are at least two in the word

code:

ifstream fin("dataTest.in");
std::string s;
const char* vowels = "aeiou";
int k;
fin>>k;

while(fin>>s)
{
    // If the string contains at least two vowels, the find results will differ
    if (s.find_first_of(vowels) != s.find_last_of(vowels))
    {
        std::cout << s << std::endl;
    }
}


回答4:

First fix obvious problems

 int main(){
   ifstream fin("dataTest.in");
   char s[250];
   int k;
   fin>>k;

  while(fin>>s) {
    int nrVowels=0; // moved
    int n = strlen(s);

    for(int i=0; i < n; i++) {
      if (s[i] == 'a' ||  s[i] == 'e' ||  s[i] == 'o' ||  s[i] == 'i' || s[i] == 'u') {
        nrVowels++;

        if(nrVowels == k) {
          cout<< s;;
          break; // inner for
        }
      }
    }
  }    
}

You have

char s[250];

in combination with

fin>>s

is a potential buffer overrun. changing s to

std::string s;

now you get

    int n = strlen(s);

for free.

if (s[i] == 'a' ||  s[i] == 'e' ||  s[i] == 'o' ||  s[i] == 'i' || s[i] == 'u')

is rather long it would be better to say

if (IsVowel(s[i]) {

by making (as Kaldrr proposed)

auto IsVowel = [](const char c){return c == 'a' ||  c == 'e' ||  c  == 'o' ||  c  == 'i' || c  == 'u';};

This gives

int main(){
  ifstream fin("dataTest.in");
  std::string s;
  int k;
  fin>>k;

  auto IsVowel = [](const char c){return c == 'a' ||  c == 'e' ||  c  == 'o' ||  c  == 'i' || c  == 'u';};

  while(fin>>s) {
    int nrVowels=0; // moved

    for(int i=0; i < n; i++) {
      if (IsVowel(s[i]) {
        nrVowels++;

        if(nrVowels == k) {
          cout<< s;;
          break; // inner for
        }
      }
    }
  }    
}

But there is still the problem of each word is tested instead of each string.

int main(){
  ifstream fin("dataTest.in");
  std::string s;
  int k;
  fin>>k;

  auto IsVowel = [](const char c) { return c == 'a' ||  c == 'e' ||  c  == 'o' ||  c  == 'i' || c  == 'u'; };
  auto IsSplit = [](const char c) { return isspace(s[i]) || ispunct(s[i]); };

  while(fin>>s) {
    int nrVowels=0; // moved

    for(int i=0; i < s.length; i++) {
      if (IsVowel(s[i]) {
        nrVowels++;

        if(nrVowels == k) {
          cout << s; // wrong writes all the string and not the word
          break; // inner for
        }
      } else {
        if (IsSplit(s[i]) {
          nrVowels = 0; // restart at each word.
        }
      }
    }
  }    
}

What we want is

while(fin>>s) {
  auto split = Split(s);

  for (const auto& str : split) {
    if (HasEnougVowels(str, k)) {
      cout << s;
    }
  }
}