How to create a program to output a certain length of word

I am working on a program where it is supposed to output a certain length of a word whether it is minimum length or maximum length. The one thing I'm currently stuck on right now is how to open a text file using a function in Object Oriented Programming? The text file contains the complete list of words that will be used for my program. I need that to be able to progress to finishing my program.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
   #include <iostream>
   #include <cstdlib>
   #include <fstream>
   #include <ctime>
   #include <string>
   #include <cstring>
  
   using namespace std;

 class Words
  {
      private:
          int minlen;                   //Min length of word
          int maxlen;                  // Max length of word
          int count;
          string * choices;           // Dynamic Array will be used to dispose the additional word choices
          int count_candidates();    // Function will be used to open txt file of words list
          // count_candidates is also used to count all words in file between minlen and maxlen.
          // Returns resulting count.
          void load_words();
 
 
      public:
 
      Words (int mn, int mx)
      {
          minlen = mn;
          maxlen = mx;
          int count = 0;
      }
 
      int count_candidates()
      {
 
      }
 
      void load_words()
      {
 
      }
 
      string get_choices()
      {
         return * choices;
      }
 
 
      ~Words()
      {
          delete [] choices;
      }
 
 
  };
 
  int main()
  {
      srand(time(NULL));  // needs <ctime> included
      int min, max;
 
      cout << "Enter min: ";
      cin >> min;
 
      cout << "Enter max; ";
      cin >> max;
 
      Words words(min, max);
 
      cout << words.pick_word() << endl;
      // The goal is to get the program to output a word that is either min letter count or max letter count.
  }
Last edited on
Hello Battlecode06,

From C++11 on, which you should be using, the more up-to-date way of writing "srand" is: srand(static_cast<unsigned int>(time(nullptr)));.

"<cstring>" is the C++ version of the C header file "<string.h>". There is nothing in your code that I see that would require "<cstring>".

As for the file I would define the file stream in "main" and pass this to the function to open the file.

If you need some help take a look at: https://www.learncpp.com/ chapter 23.6 and 23.7. There is also the book you are working from.

When you have updated your code post it along with a good sample of the file you will be using.

Andy
How do you intend to generate those words?

One way is to store a "dictionary" of words of different lengths. Then you can select a word from the list.

For example, we want to access words by length, so we could store the words in an array, ordered by length:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const char* words_by_length[] = {
    "",
    "a",
    "by",
    "car",
    "done",
    "quiet",
    "hacker",
    "purging"
    // and so on ... you'll need to have enough words
};

// print word of some length i
int i = 4;
printf("word of length %d: %s\n", i, words_by_length[i]); // old
std::cout << "word of length " << i << ": " << words_by_length[i] << std::endl; // newer 
Last edited on
to open a text file


To open a text file, read each line (word?) the basic structure is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <fstream>
#include <string>
#include <iostream>

int main()
{
	std::ifstream fwords("words.txt");

	if (!fwords)
		return (std::cout << "Cannot open file\n"), 1;

	for (std::string word; std::getline(fwords, word); ) {
		// Process read word here
	}
}

Hello Battlecode06,

While working on the program I developed a question.

The functions in the class were these given to use as is? Were they given to use as a starting point. Or are the functionc that you came up with that can be changed?

I noticed:
1
2
3
4
int count_candidates(ifstream& inFile, const std::string& inFileName);    // Function will be used to open txt file of words list
// count_candidates is also used to count all words in file between minlen and maxlen.
// Returns resulting count.
void load_words();

These are in the private section. Not sure if they will work there because they are usually put in the public section.

Speaking of private if you did not know or fully understand when you define a class:
1
2
3
class Words
{
}

What is between the {}s is private by default. So starting with "private:" is not necessary, but OK if you wnat to leave it.

Your function int count_candidates() would work better as int open_file() and save int count_candidates() to actually read the file and do its work.

When you figure out what you need to do I can show you a way to make better use of int open_file().

This would work best if you concentrate on opening the file first then reading the file and leave the rest until these parts are working correctly. Also write your code compile often to catch errors and test as often as needed.

Andy
@KBW, I intend on generating these words from the text file I have where it has a large list of them. So I will need to open and read the file in order to use those words for the rest of my program.
Hello Handy Andy, the function

int count_candidates(ifstream& inFile, const std::string& inFileName);

can be changed and be used to save and read the file from another function. You can show the way that works better with opening and reading the file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
   
   #include <iostream>
   #include <cstdlib>
   #include <fstream>
   #include <ctime>
   #include <string>
   using namespace std;
  
   class Words
   {
      private:
          int minlen;                   //Min length of word
          int maxlen;                  // Max length of word
          int count;
         string * choices;           // Dynamic Array will be used to dispose the additional word choices
          int count_candidates();    // Function will be used to open txt file of words list
          // count_candidates is also used to count all words in file between minlen and maxlen.
          // Returns resulting count.
          void load_words(); 
 
      public:
 
      Words (int mn, int mx)
      {
          minlen = mn;
          maxlen = mx;
          int count = 0;
      }
 
      string pick_word()
      {
          if (count == 0)
          {
              return string;
          }
      }
 
      string get_choices()
      {
         return * choices;
      }
 
 
      ~Words()
      {
          delete [] choices;
      }
 
 
  };
 
  int main()
  {
      std::ifstream fwords("enable1.txt");
 
      if(!fwords)
      {
          std::cout << "Cannot open file\n";
          return 1;
      }
      for (std:: string word, std:: getline(fwords, word); )
      {
 
      }
 
      srand(static_cast<unsigned int>(time(nullptr)));  // needs <ctime> included
      int min, max;
 
      cout << "Enter min: ";
      cin >> min;
 
      cout << "Enter max; ";
      cin >> max;
 
      Words words(min, max);
 
      cout << words.pick_word() << endl;
     // The goal is to get the program to output a word that is either min letter count or max letter count.
  }

This is the code I have now with the changes I've made to it.


: In member function 'std::__cxx11::string Words::pick_word()':
33:26: error: expected primary-expression before ';' token
             return string;
                          ^
: In function 'int main()':
60:42: error: qualified-id in declaration before '(' token
     for (std:: string word, std:: getline(fwords, word); )
                                          ^
60:42: error: expected ';' before '(' token
     for (std:: string word, std:: getline(fwords, word); )
                                          ^                                         
					  ;
60:49: error: could not convert '((void)0, word)' from 'std::__cxx11::string' {aka 'std::__cxx11::basic_string<char>'} to 'bool'
     for (std:: string word, std:: getline(fwords, word); )

These are the errors I have compiled from the code that I was working on.
The text file is pretty big and probably contains a few thousand words. It's like a dictionary itself. But here it is to give you an idea of what the text file looks like.

      aa
      aah
      aahed
      aahing
      aahs
      aal
      aalii
      aaliis
      aals
     aardvark
     aardvarks
     aardwolf
     aardwolves
     aargh
     aarrgh
     aarrghh
     aas
     aasvogel
     aasvogels
     ab
     aba
     abaca
     abacas
     abaci
     aback
     abacterial
     abacus
     abacuses
     abaft
     abaka
     abakas
     abalone
     abalones
     abamp
     abampere
     abamperes
     abamps
     abandon
     abandoned
     abandoner
     abandoners
     abandoning
     abandonment
     abandonments
     abandons
     abapical
     abas
     abase
     abased
     abasedly
     abasement
     abasements
     abaser
     abasers
     abases
     abash
     abashed
     abashes
     abashing
     abashment
     abashments
     abasia
     abasias
     abasing
     abatable
     abate
     abated
     abatement
     abatements
     abater
     abaters
     abates
     abating
     abatis
     abatises
     abator
     abators
     abattis
     abattises
     abattoir
     abattoirs
     abaxial
     abaxile
     abba
     abbacies
     abbacy
     abbas
     abbatial
     abbe
     abbes
     abbess
     abbesses
     abbey
     abbeys
     abbot
     abbotcies
     abbotcy
     abbots
     abbreviate
     abbreviated
     abbreviates
     abbreviating
     abbreviation
     abbreviations
     abbreviator
     abbreviators
     abdicable
     abdicate
     abdicated
     abdicates
     abdicating
     abdication
     abdications
     abdicator
     abdicators
     abdomen
     abdomens
     abdomina
     abdominal
     abdominally
     abduce
     abduced
     abducens
     abducent
Last edited on
Hello Battlecode06,

In your class do this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Words
{
    private:
    int minlen;                   //Min length of word
    int maxlen;                  // Max length of word
    int count;
    string * choices;           // Dynamic Array will be used to dispose the additional word choices

/*
    **** These forward declarations are in the private section. Should be in the public section
         but since all the functions are in the class they are not needed. *****
    int count_candidates(ifstream& inFile, const std::string& inFileName);    // Function will be used to open txt file of words list
    // count_candidates is also used to wordCount all words in file between minlen and maxlen.
    // Returns resulting wordCount.
    void load_words();
*/
    public:

    Words(int mn, int mx)
    {
        minlen = mn;
        maxlen = mx;
        int count = 0;
    }

    //string pick_word()
    //{
    //    if (count == 0)
    //    {
    //        return string;
    //    }
    //}

    //string get_choices()
    //{
    //    return *choices;
    //}


    ~Words()
    {
        delete[] choices;
    }
};

Lines 26 - 37 are not needed yet, so do not worry about them yet.

In line 30 you are trying to return a type not a variable. That is your first error.

Your next 3 errors may be in line 61 where you have a space between "std::" and "getline"

In line 40 consider "*choices". With the (*) next to the variable it is easier to tell that you are trying to dereference the variable to get its value, but either way will work.

Maybe I misunderstood what you first wrote or your interpretation of the original directions is a bit off, but I took it as you wanted a class function, or maybe not a class function, to open the file, so in main I did this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main()
{
    //srand(time(NULL));  // needs <ctime> included
    srand(static_cast<unsigned int>(time(nullptr)));

    int min{}, max{}, response{};

    cout << "Enter min: ";
    cin >> min;

    cout << "Enter max: ";
    cin >> max;

    Words words(min, max);

    std::string inFileName{ "test.txt" };
    std::ifstream inFile;
    
    if (response = words.open_file(inFile, inFileName))
        return response;

and in the class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int open_file(ifstream& inFile, const std::string& inFileName)
{
    inFile.open(inFileName);

    if (!inFile)
    {
        std::cout << "\n     File " << std::quoted(inFileName) << " did not open.\n";
        //std::cout << "\n     File \"" << inFileName << "\" did not open.\n";

        return 1;
    }

    return 0;
}

For what it is worth I found using "inFile" and "outFile" to work better with less confusion. Easier to keep track of than changing the file stream name from 1 program to the next. It is best to be consistent.

After you have opened the file you just pass it to any function that needs it.

Based on your first class I would start with defining the file stream in "main" then opening the file then print out some part of the array you created in void count_candidates(). I changed this to "void" because there is no reason to return anything. The class defines the private variable "count", "wordCount" is more descriptive, so all you have to do is increment this variable that you already have.

I created a simple print function to print the array then went back and add an if statement to use "minlen" and "maxlen" to limit the size of the words printed.

Get this much working before you start into the other functions.

Andy
@OP Seeing that you decided to use a class which is a reasonable thing to do you might like to consider the following which rearranges what you have but make it object oriented. I changed a few names to make it clearer to me at least.

The WordGoup object should do as much of the work as possible so all you do is send it a message (parameters via a function) where the parameters come from the input in main().

Also, you need to decide at what stage you filter the word sizes, before or after the array is built.

The beauty of the class is you isolate the functions instead of having line after line of stuff in main. In your case you have 2 methods in the class to fix up and you are nearly there. Remember that choices = new ...[count];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <iostream>
#include <fstream>
#include <ctime>
#include <string>

class WordsGroup
{
private:
    int minlen{0};
    int maxlen{0};
    std::string file_name;

    std::string* choices = nullptr;
    int count{0};

public:

    WordsGroup (int min, int max, std::string fName)
    {
        minlen = min;
        maxlen = max;
        file_name = fName;

        this->load_words();
    }

    ~WordsGroup()
    {
        delete [] choices;
    }

    void load_words()
    {
        // count up number of words on file
        // create array (new) and load from file

        std::ifstream fwords(file_name);

        if(!fwords)
        {
            std::cout << "Cannot open file\n";
            return;
        }

        std::string temp;

        count = 0;
        while(fwords >> temp)
        {
            std::cout << count << ' ' << temp << '\n';
            count++;
        }

        std::cout << "There are " << count << " words\n";
    }

    std::string pick_word()
    {
        if (count == 0)
        {
            return "Zero words available to select from";
        }
        // generate a random number xxx
        //        srand(static_cast<unsigned int>(time(nullptr)));  // needs <ctime> included
        //        int min, max;
        // return choices[xxx];
        return "Needs fixing";
    }
};

int main()
{
   // USER INPUT
    int min{}, max{};
    std::cout << "      Enter min: ";
    std::cin >> min;

    std::cout << "      Enter max: ";
    std::cin >> max;

    std::string source_file;
    std::cout << "Enter file name: ";
    std::cin >> source_file;

    source_file = "enable.txt"; // AVOIDS ENTERING EACH TIME

    // CONSTRUCT OBJECT
    WordsGroup words(min, max, source_file);

    // OUTPUT RESULTS
    std::cout << words.pick_word() << '\n';
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
      Enter min: 1
      Enter max: 1
Enter file name: 1
0 aa
1 aah
2 aahed
3 aahing
4 aahs
5 aal

...
121 abduced
122 abducens
123 abducent
There are 124 words
Needs fixing
Program ended with exit code: 0
The goal is to get the program to output a word that is either min letter count or max letter count


If this is the requirement, then simply:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <fstream>
#include <string>

int main()
{
	std::ifstream fwords("Words.txt");

	if (!fwords)
		return (std::cout << "Cannot open file\n"), 1;

	size_t minsz {}, maxsz {};

	std::cout << "Enter min: ";
	std::cin >> minsz;

	std::cout << "Enter max; ";
	std::cin >> maxsz;

	std::cout << "Words that are either min letter count or max letter count\n";

	for (std::string word; std::getline(fwords, word); )
		if (word.size() == minsz || word.size() == maxsz)
			std::cout << word << '\n';
}


If it is required to store the appropriate words for use later, then use a std::vector<std::string> and add to the vector instead of simply displaying them.
Yeah I was able to get this code to compile and will need to work on the next functions and get them to work. I'm not sure if the count_candidates() is reading but I put some extra code in there using comments to see if I'll need something like that.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
   
   #include <iostream>
   #include <cstdlib>
   #include <fstream>
   #include <ctime>
   #include <string>
   #include <iomanip>
   using namespace std;
  
   class Words
  {
      private:
          int minlen;                   //Min length of word
          int maxlen;                  // Max length of word
          int Wordcount;
          string * choices;           // Dynamic Array will be used to dispose the additional word choices
       //  void count_candidates();    // Function will be used to read txt file of words list
          // count_candidates is also used to count all words in file between minlen and maxlen.
          // Returns resulting count.
          void load_words();
 
      public:
 
      int open_file(ifstream & inFile, const std::string& inFileName)
      {
          inFile.open(inFileName);
 
      if (!inFile)
      {
          std::cout << "\n File " << std::quoted(inFileName) << " did not open.\n";
          return 1;
      }
          return 0;
      }
 
      Words (int mn, int mx)
      {
          minlen = mn;
          maxlen = mx;
          int count = 0;
      }
 
      void count_candidates()
      {
         Wordcount++;
 
         /* while
          {
              inFileName >> candidates;
              break;
 
          }   */
 
      }
 
      /*
      string pick_word()
      {
          if (count == 0)
          {
              return string;
          }
      }
     string get_choices()
      {
         return * choices;
      }
      */
 
      ~Words()
      {
          delete [] choices;
      }
 
 
  };
 
  int main()
  {
 
      srand(static_cast<unsigned int>(time(nullptr)));  // needs <ctime> included
      int min {}, max {}, response{};
 
      cout << "Enter min: ";
      cin >> min;
 
      cout << "Enter max; ";
      cin >> max;
 
      Words words(min, max);
 
      std::string inFileName{ "enable1.txt" };
      std::ifstream inFile;
      if (response = words.open_file(inFile, inFileName))
      {
          return response;
      }
 
     // cout << words.pick_word() << endl;
     // The goal is to get the program to output a word that is either min letter count or max letter count.
 }
Hello Battlecode06,

You are on the right path, but your function needs a little work.
1
2
3
4
5
6
7
8
9
10
11
void count_candidates()
{
    Wordcount++;

    /* while
     {
         inFileName >> candidates;
         break;

     }   */
}

In line 1 how do expect to use the file stream if you do not pass it to the function? The stream is still defined in "main" and now open, but you are not using it.

In the () of the while loop is where you need to read the file. You also need to define a "std::string" to read into. Since the point is to count the words tha name is not important. I just used "word".

"inFileName" is defined as a string to hold the name of the input file. Here not only is it not defined you can not read from a string.

"WordCount" is at the beginning of the function so it is only incremented once. This should be inside the {}s of the while loop. Do not just write some code think about what the function is for and what the while loop needs to do.

After the while loop is finished the "eof" bit on the file stream will be set. Not only do you need to reset the state bits on the file stream it is a good idea to clear the buffer and you will need to move the file point back to the beginning, ".seekg".

When the private variable "WordCount" will contain the number of lines, or words, read.

Your next function to work on is to read the file and store the words in the dynamic array that it will create.

You could add a "Print" function if you need one or just for testing to print out the array or a portion of it just to make sure everything worked.

Andy

Edit:
Last edited on
My code was able to compile with no errors after implementing some changes to it with the count_candidates() function. I think it is created correctly but I'll post the updated version for you to see if I did get anything wrong.

1
2
3
4
5
6
7
8
9
10
11
12
	void count_candidates(std::string inFileName)
      {
          std::ifstream inFile(inFileName);
          std::string readWord;
          Wordcount = 0;
 
          while(inFile >> readWord)   // >> is for reading data   // << is for writing out data
          {
              Wordcount++;
          }
 
      }
Hello Battlecode06,

You should not be opening a file stream that is already open. You should be passing the file stream as in: void count_candidates(std::ifstream& inFile).

"WordCount" should already be set to (0)zero.

The while loop is good.

But after the while what state is the file stream in?

The while loop runs until the read sets the "eof" bit on the file stream. Do you understand what that means?

As I said earlier there is more to do after the while loop than you have.
After the while loop is finished the "eof" bit on the file stream will be set. Not only do you need to reset the state bits on the file stream it is a good idea to clear the buffer and you will need to move the file point back to the beginning, ".seekg".


Almost half way there, but not finished.

Andy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <iostream>
#include <fstream>
#include <ctime>
#include <string>

class Words
{
private:
    int minlen{0};
    int maxlen{0};
    std::string file_name;

    std::string* choices = nullptr;
    int count{0};

public:

    Words (int min, int max, std::string fName)
    {
        minlen = min;
        maxlen = max;
        file_name = fName;

        this->load_words();
        srand( time(NULL) );
    }

    ~Words()
    {
        delete [] choices;
    }

    int getCount()
    {
        return count;
    }

    void display_list()
    {
        for(int i = 0; i < count; i++)
        {
            std::cout << choices[i] << '\n';
        }
    }

    void load_words()
    {
        std::ifstream fwords(file_name);

        if(!fwords)
        {
            std::cout << "Cannot open file\n";
            return;
        }

        // COUNT WORDS (1st FILE READ)
        std::string temp;
        count = 0;
        while(fwords >> temp)
        {
            // FILTER WORDS
            if(temp.length() == minlen or temp.length()== maxlen)
                count++;
        }

        // SETUP ARRAY
        choices = new std::string[count];
        fwords.close();

        // LOAD ARRAY (2nd FILE READ)
        fwords.open(file_name);
        int index{0};
        while(fwords >> temp)
        {
            if(temp.length() == minlen or temp.length() == maxlen)
            {
                choices[index] = temp;
                index++;
            }
        }
        fwords.close();
    }

    std::string pick_word()
    {
        if (count == 0)
        {
            return "Zero words available to select from";
        }

        return choices[rand() % count];
    }
};

int main()
{
    // INPUT
    int min{}, max{};
    std::cout << "      Enter min: ";
    std::cin >> min;

    std::cout << "      Enter max: ";
    std::cin >> max;

    std::string source_file;
    std::cout << "Enter file name: ";
    std::cin >> source_file;

    // CONSTRUCT OBJECT
    Words sample(min, max, source_file);

    // SOME TESTS ON OBJECT ...
    // HOW MANY WORDS
    std::cout
    << "There are " << sample.getCount() << " words that are either "
    << min << " or " << max << " characters long\n";

    // DISPLAY ALL WORDS IN THE FILTERED LIST
    sample.display_list();

    // PICK SOME WORDS FROM LIST AT RANDOM
    std::cout << "A selection of some words\n";
    for(int i = 0; i < 5; i++)
        std::cout << sample.pick_word() << '\n';
}


      Enter min: 3
      Enter max: 9
Enter file name: enable1.txt
There are 20 words that are either 3 or 9 characters long
aah
aal
aardvarks
aas
aasvogels
aba
abamperes
abandoned
abandoner
abasement
abashment
abatement
abattises
abattoirs
abbotcies
abdicable
abdicated
abdicates
abdicator
abdominal
A selection of some words
aba
abdicator
abattises
abattoirs
abandoner
Program ended with exit code: 0
Topic archived. No new replies allowed.