Counting identical words

Hello everybody!
I'm trying to count the number of occurrences of each word in a text file. But program put in the file the first symbol of inputed word only (line 34) and don't enter in the for-statement (line 51). What should I search for in my code in order to find the problem?
Thanks in advance.
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
#include <fstream.h>
#include <iostream.h>
#include <map.h>
#include <string>
#include <iterator>
#include <cstdlib>
#include <iomanip>
#include <conio.h>

typedef std::map<std::string, int> StrIntMap; // map for counting

void countWords(std::istream& in, StrIntMap& words) // func that count words
{
    std::string s;

    while (in >> s)
    {
        ++words[s];
    }
}

using namespace std;

int main ()
{
  char str [80];

  ofstream out("test.dat");
  if(!out) {
		cout << "Cannot open file.\n";
		return 1;
}

  do {
        cout << "Enter a word (BL to quit):\n";
        cin >> str;
        out << str;
        } while(*str != '\n'); // if empty str inputed -> stop do-while
  out.close();

  ifstream in("test.dat");
    if(!in) {
		cout << "Cannot find file.\n";
		return 1;
}


  StrIntMap w;
  countWords(in, w);

    for (StrIntMap::iterator p = w.begin( ); p != w.end( ); ++p)
    {   std::cout << "\nPress any key to close";
        std::cout << setw(30) << left  << p->first  << " occurred "
                  << setw(10)  << right << p->second << " times.\n";
    }

  std::cout << "\nPress any key to close";

getch();
return 0;
}
Last edited on
Use std::string instead of the char array for 'str', just like you do in the countWords.
First symbol of str will never be newline. Just because you cannot enter any whitespace character using formatted input.

Your program should enter infinite loop and you will have to terminate it. In that case complete flushing of out buffer and writing file to disc is not guaranteed.
Last edited on
When I edited code by the following:
1
2
3
int main ()
{
  std::string str [80];


Errors were shown:
[C++ Error] Count.cpp(36): E2015 Ambiguity between '_STL::basic_istream<char,_STL::char_traits<char> >::operator >>(bool &)' and '_STL::basic_istream<char,_STL::char_traits<char> >::operator >>(void * &)'
[C++ Error] Count.cpp(38): E2094 'operator!=' not implemented in type 'string' for arguments of type 'char'

When I edited code by the following:
1
2
3
int main ()
{
  string str;


Errors were shown:
[C++ Error] Count.cpp(38): E2093 'operator*' not implemented in type 'string' for arguments of the same type
Is any way exist to correct it?
string is not pointer so you canot dereference it.

However there is member function .empty() which tells you if string is empty. use it.
This code is really bad.
If you can use a string, why using a char?
You can use my new and commented code:
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
#include <iostream>
#include <fstream>
#include <string>
#include <set>

int main()
{
    std::string input; //We'll get the input with this variable
    while(input.empty()) //While it's empty (with std::getline, while it's a '\n'
    {
        std::cout << "Input a valid phrase: "; //Show request
        std::getline(std::cin, input); //Get the input from std::cin (user)
        std::cout << std::endl; //Newline
    }
    std::set<char> input_chars(input.begin(), input.end());
    std::ofstream words_output(((input + "_phrase") + ".dat").c_str()); //Example: input = "IM HAPPY". file = "IM HAPPY_phrase.dat"
    words_output << input << "\n\n";
    for(auto letter : input_chars) //for each letter in the set
    {
        words_output << letter << " found in:\n";
        for(auto current_letter = input.begin(); current_letter != input.end(); current_letter++) //for each letter in the input; here we start a linear search
        {
            if(*current_letter == letter)
            {
                words_output << std::distance(input.begin(), current_letter) + 1;
                //std::distance for calculating where the char was found. +1 is "human" readability.
                words_output << "\n";
            }
        }
        words_output << "\n";
    }
    return 0;
}
User input like this is a little odd, there's no getting around it. A do-while is a little 'chunky', because you need to check that the string is not "BL" twice:
1
2
3
4
5
6
7
8
9
10
string str;
do
{
  cout << "Enter a word, BL to quit: ";
  cin >> str;
  
  if ( str != "BL" )
    // add str to map

} while ( str != "BL" )


I would do a plain while, preferring to double up on console output:
1
2
3
4
5
6
7
8
string str;
cout << "Enter a word, BL to quit: ";
while ( ( cin >> str ) && str != "BL" )
{
  // add str to map

  cout << "Enter a word, BL to quit: ";
}

I've tested code according your recommendations and it's working! But it binds inputed words:
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
#include <fstream.h>
#include <iostream.h>
#include <map.h>
#include <string>
#include <iterator>
#include <cstdlib>
#include <iomanip>
#include <conio.h>

typedef std::map<std::string, int> StrIntMap;

void countWords(std::istream& in, StrIntMap& words)
{
    std::string s;

    while (in >> s)
    {
        ++words[s];
    }
}

using namespace std;

int main ()
{
  string str;

  ofstream out("test.dat");
  if(!out) {
		cout << "Cannot open file.\n";
		return 1;
}

  while ( ( cin >> str ) && str != "BL" )
  { out << str;
    cout << "Enter a word (BL to quit):\n";
    }
  out.close();

  ifstream in("test.dat");
    if(!in) {
		cout << "Cannot find file.\n";
		return 1;
}


  StrIntMap w;
  countWords(in, w);

    for (StrIntMap::iterator p = w.begin( ); p != w.end( ); ++p)
    {   std::cout << "\nPress any key to close";
        std::cout << setw(20) << left  << p->first  << " occurred "
                  << setw(5)  << right << p->second << " times.\n";
    }

  std::cout << "\nPress any key to close";

getch();
return 0;
}


For instance if input was:
cat
dog
dog
cat
Output is equal to:

catdogdogcat occured 1 times  


Last edited on
out << str;  
first word: "cat"
in file: "cat"
second word: "dog"
in file: "catdog"
...

Add whitespaces to the file.
Thank you so much. This is working code (If anyone know how to input empty string instead of "BL" (blank line) - a sign of completion of input is null string).

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
#include <fstream.h>
#include <iostream.h>
#include <map.h>
#include <string>
#include <iterator>
#include <cstdlib>
#include <iomanip>
#include <conio.h>

typedef std::map<std::string, int> StrIntMap;

void countWords(std::istream& in, StrIntMap& words)
{
    std::string s;

    while (in >> s)
    {
        ++words[s];
    }
}

using namespace std;

int main ()
{
  string str;

  ofstream out("test.dat");
  if(!out) {
		cout << "Cannot open file.\n";
		return 1;
}
  cout << "Enter a word: \n";
  while ( ( cin >> str ) && str != "BL" )
  { out << str << " ";
    cout << "Enter the next word (BL to quit):\n";
    }
  out.close();

  ifstream in("test.dat");
    if(!in) {
		cout << "Cannot find file.\n";
		return 1;
}


  StrIntMap w;
  countWords(in, w);

    for (StrIntMap::iterator p = w.begin( ); p != w.end( ); ++p)
    {   
        std::cout << setw(20) << left  << p->first  << " occurred "
                  << setw(5)  << right << p->second << " times.\n";
    }

  std::cout << "\nPress any key to close";

getch();
return 0;
}
Last edited on
MiiNiPaa wrote:
Add whitespaces to the file.

You are writing your words as continuous stream of letters.
Thank you so much, I've realized it (watch line 35 { out << str << " ";).
Could you explain how to make as a sign of completion of input is null string?
When while ( ( cin >> str ) && str != "\n" ) "\n" is not working.
And when while ( ( cin >> str ) && str != "\n" ) Builder reports that "!=" not implemented in type 'string' for arguments of type 'char'.
'\n' is a newline symbol. It cannot appears inside string when using formatted input (as it counts as a whitespace symbol)

It is not possible (technically it is but it is better to not do that) to get an empty string or string containing whitespace characters using operator>>.

You might want to use std::getline function (but be wary of mixing formatted and unformatted input). In that case you can just use .empty() member function or compare to empty string ""
If anyone know how to input empty string instead of "BL" (blank line)


The >> operator will ignore leading white space, so you can't use it straight away.

Check out std::istream::peek()
http://www.cplusplus.com/reference/istream/istream/peek/

The idea being to use peek() to check for a newline, and otherwise read in the string.
Ugh. peek() will (almost) always return a newline. That which is left after previous input:
1
2
"dog\n"
//   ↑Here it is! 

You will need to ignore previous input first and to use getline straight away might be better choice.
Last edited on
I'm trying .empty() at first. Do you mind this?
1
2
3
4
5
6
7
while (cin >> str)
  { out << str << " ";
    cout << "Enter the next word (Null string to quit):\n";
    if (str.empty()){
    out.close();
    }
  }

Unfortunately this code is not working. Is any way exist to improve it?
Last edited on
Did you read my previous post:
MiiNiPaa wrote:
It is not possible (technically it is but it is better to not do that) to get an empty string or string containing whitespace characters using operator>>.
Use std::getline.
And if you will decide to use a getline and run into "it skips input" problem, here is a solution:
http://stackoverflow.com/questions/21567291/why-does-stdgetline-skip-input-after-a-formatted-extraction
Last edited on
Ugh. peek() will (almost) always return a newline.


peek() will always return the next character in the stream, and has nothing to do with a newline character.
peek() will always return the next character in the stream,
Correct, peek will return next character in stream. Which will be a newline:
entered: "cat\n" ← newline character appended by pressing enter
extracted: "cat"
left in stream: "\n"
Extracted by peek(): '\n' (will not ask user for input)
This is working code with possibility of input closing by pressing "Enter":
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
#include <fstream.h>
#include <iostream.h>
#include <map.h>
#include <string>
#include <iterator>
#include <cstdlib>
#include <iomanip>
#include <conio.h>

typedef std::map<std::string, int> StrIntMap;

void countWords(std::istream& in, StrIntMap& words)
{
    std::string s;

    while (in >> s)
    {
        ++words[s];
    }
}

using namespace std;

int main ()
{
  string str;

  ofstream out("test.dat");
  if(!out) {
		cout << "Cannot open file.\n";
		return 1;
}
  cout << "Enter a words: \n";
  std::getline(cin, str);
  out << str;
  out.close();

  ifstream in("test.dat");
    if(!in) {
		cout << "Cannot find file.\n";
		return 1;
}


  StrIntMap w;
  countWords(in, w);

    for (StrIntMap::iterator p = w.begin( ); p != w.end( ); ++p)
    {   
        std::cout << setw(20) << left  << p->first  << " occurred "
                  << setw(5)  << right << p->second << " times.\n";
    }

  std::cout << "\nPress any key to close";

getch();
return 0;
}

Thank you for your insight!
Topic archived. No new replies allowed.