0

I have to write a program that requests a file name from the user and then counts all of the words in the file. The hypothetical file has 55 words in it, but my program counts 56 words.

Every change I've tried making to my code has only gotten me farther from the correct answer, either resulting in 0 words or causing it to become an infinite loop. I'm seriously stuck on where the extra word/character is coming from, so I was hoping someone might see an error that I'm missing.

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    char filename[20];

    cout << "Enter a file name: ";
    cin >> filename;
    
    ifstream fin;
    fin.open(filename);
    if (fin.fail())
    {
        exit(1);
    }
    
    char next;
    int word = 0;

    while (fin)
    {
        fin.get(next);
        
        if (next == ' ' || next == '\n')

            word++;
    }
    
    fin.close();

    cout << "The file contains " << word << " words.";

    return 0;
}

  • 3
    Could you please post your input file? My guess is your input file contains extra line breaks at the end which based on your current algorithm will lead to an extra word count. Your current algorithm will give incorrect results in cases of double spaces, multiple line breaks, or no line break before last word. Currently you're counting number of spaces or line breaks, which doesn't always guarantee number of words in your input file. – mani Nov 23 '22 at 01:26
  • There isn't an actual input file, this is for a school assignment so all I know is the amount of words its supposed to have. We aren't given any other information about the input file. – heemi98 Nov 23 '22 at 01:31
  • 1
    `while (fin) {fin.get(next);` is the bug. It tests, then reads and then uses the result of the read regardless of whether it succeeded or not. Failure will be caught on the next iteration of the loop, but by then the 56th, and invalid, data will have been processed. – user4581301 Nov 23 '22 at 01:35
  • 1
    When you don't have input, you need to make your own input to test against. – user4581301 Nov 23 '22 at 01:36
  • you should allow for multiple runs of spaces, new lines etc. at the moment "cat dog" (2 spaces) will be 3 words. – pm100 Nov 23 '22 at 01:38
  • 2
    Side note: A really easy way to read and count words is `std::string word; int count = 0; while (fin >> word) { count++; }`. `>>` into a `string` always ignores leading whitespace and then gathers up to the next whitespace, end of file, or failure for any other reason. Then it returns a reference to the stream which the `while` can immediately test for a valid and readable state. This catches any bad inputs like smacking up against the end of the file before you can accidentally count it. – user4581301 Nov 23 '22 at 01:41
  • Your suggestion to change it to a string worked, thank you so much! @user4581301 – heemi98 Nov 23 '22 at 01:52

2 Answers2

0

I debugged and modified your code. Here is my version:

#include <fstream>
#include <iostream>

using namespace std;

int main() {
  char filename[20];

  cout << "Enter a file name: ";
  cin >> filename;

  ifstream fin;
  fin.open(filename);
  if (fin.fail()) {
    exit(1);
  }

  char next;
  // When the last char in file is not newline or space,
  // we need to count the last word manually
  bool hasWord = false;
  int word = 0;

  while (fin) {
    fin.get(next);

    if (fin.fail()) {
      if (hasWord)
        word++;
      break;
    }
    if (next == ' ' || next == '\n') {
      word++;
      hasWord = false;
    } else
      hasWord = true;
  }

  fin.close();

  cout << "\nThe file contains " << word << " words.";

  return 0;
}

Two tips:

  1. When ifstream read the last char of file, it will count it twice. Because the first time ifstream reads it, eof() will not be true. The second time ifstream reads it, eof() will be true. But the next variable keeps the last value. So it counts twice. Solution is when ifstream read a char, we check it again by fin.fail().
  2. If the last char of file is not a space or newline. We need count the last word manually.

BTW, my code is based on your version. I didn't handle the mixed space or newline.


Docs about eof():

https://en.cppreference.com/w/cpp/io/basic_ios/eof

This function only reports the stream state as set by the most recent I/O operation; it does not examine the associated data source. For example, if the most recent I/O was a get() which returned the last byte of a file, eof() returns false. The next get() fails to read anything and sets the eofbit. Only then does eof() return true.

waltermitty
  • 453
  • 3
  • 12
  • Mandatory reading: [Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?](https://stackoverflow.com/q/5605125/4581301). The `if (fin.fail())` sandpapers over the problems, but there was no need for the problem in the first place. – user4581301 Nov 23 '22 at 01:55
  • Side note: Counts a word and potentially fails if there are two spaces in a row or a mix of spaces and newlines. This is a problem ported from the asker's original code. – user4581301 Nov 23 '22 at 01:57
-1

I changed the while loop to use a string instead of a char as suggested to me in the comments, and it worked. Here's the new code.

#include <iostream>
#include <fstream>
#include <string>

using namespace std;
int main()
{

char filename[20];

cout << "Enter a file name: ";
cin >> filename;

ifstream fin;
fin.open(filename);
if (fin.fail())
{
    exit(1);
}

std::string word;
int count = 0;

while (fin >> word)
{
    count++;
}

fin.close();

cout << "The file contains " << count << " words.";

return 0;

}