One extra line being read in file handling - c++

I am trying to get the number of lines and words from a text file in c++. But one extra line is being read by compiler.
#include <iostream>
#include<fstream>
using namespace std;
int main(void)
{
ifstream f;
string name;
char a;
int line, words;
line = 0;
words = 0;
f.open("file.txt");
while (f) {
f.get(a);
if (a == '\n')
{
line++;
words++;
}
if (a == ' ')
{
words++;
}
}
f.close();
cout << "Number of words in file : " << words << endl;
cout << "Numbers of lines in the file : " << line << endl;
}
OUTPUT:-
Number of words in file : 79
Numbers of lines in the file : 3
file.txt:-
This C++ Program which counts the number of lines in a file. The program creates an input file stream, reads a line on every iteration of a while loop, increments the count variable and the count variable is printed on the screen.
Here is source code of the C++ program which counts the number of lines in a file. The C++ program is successfully compiled and run on a Linux system. The program output is also shown below.
I am puzzled why one extra line is being read. Kindly help.

You are not checking if f.get() succeeds or fails. When it does fail, a is not updated, and you are not breaking the loop yet, so you end up acting on a's previous value again. And then the next loop iteration detects the failure and breaks the loop.
Change this:
while (f) {
f.get(a);
...
}
to this instead:
while (f.get(a)) {
...
}
That being said, you are also not taking into account that the last line in a file may not end with '\n', and if it does not then you are not counting that line. And also, you are assuming that every line always has at least 1 word in it, as you are incrementing words on every '\n' even for lines that have no words in them.
I would suggest using std::getline() to read and count lines, and std::istringstream to read and count the words in each line, eg:
#include <fstream>
#include <sstream>
#include <string>
#include <cctype>
using namespace std;
int main(void)
{
ifstream f;
string line, word;
int lines = 0, words = 0;
f.open("file.txt");
while (getline(f, line))
{
++lines;
std::istringstream iss(line);
while (iss >> word) {
++words;
}
}
f.close();
cout << "Number of words in file : " << words << endl;
cout << "Numbers of lines in the file : " << line << endl;
}

It is because you do not check in what state is stream that f.get(a) returns.

Related

While loop will only terminate if I use CTRL + C on the terminal

The prompt of the question is:
Write a program that prompts the user to input the name of a text file and then outputs the number of words in the file. You can consider a “word” to be any text that is surrounded by whitespace (for example, a space, carriage return, newline) or borders the beginning or end of the file.
I have successfully gotten the program to count how many words are in a file; no issues there.
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <string>
int main()
{
char file_name[16];
std::ifstream in_stream;
int count = 0;
char ch;
bool lastWasWhite = true;
std::string next;
// Ask the user for the file name
std::cout << "What is the name of the file? ";
std::cin >> file_name;
in_stream.open(file_name);
// See if we can open the file
if (!in_stream.is_open()) {
std::cout << "Something went wrong when opening your file.";
exit(1);
}
// Read the file, one character at a time
while (in_stream >> next) {
do {
std::cin.get(ch);
}
while (ch != ' ' && ch != '\n' && ch != '\t');
++count;
}
in_stream.close();
std::cout << "The file " << file_name << " contains " << count << " words." << std::endl;
return 0;
}
The only problem is that the only way for the program, or I think the "while loop" to finish, is for me to hit CTRL + C on the terminal so it force stops. Then I get the result I want.
This is my first post, so please let me know if there is any other information you would like to see.
Your outer loop is reading words from the file and counting them just fine (operator>> handles the whitespace for you).
However, your outer loop is also running an inner loop that is reading user input from stdin (ie, the terminal). That is where your real problem is. You are waiting on user input where you should not be doing so. So, simply get rid of the inner loop altogether:
while (in_stream >> next) {
++count;
}
That is all you need.

How to fix program that extracts comments from C++ code to another text file?(Problem:single line comments inside of block comments)

I've created a program that takes C++ code in one text file and outputs all of the comments from that code in another text file, however I've encountered a special situation, where the program does not do the necessary task. While it does extract most of the comments, it does not extract single line comments which are inside of a block comment.
Here is the C++ source code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
fstream fin; //file that will be read
fstream fout; //file that will be written
string row; // string variable which will store each row of the code
string comment; // string which will store the comment
string blockCommentStart = "/*";
string lineCommentStart = "//"; // the beginning symbols of comments so that they could be identified in the text file
string result;
fin.open("code.txt", ios::in);
fout.open("endresult.txt", ios::out);
if(!fin) cout << "File not found" << endl;
if(!fout) cout << "File not found" << endl;
getline(fin, row); //reads the first row from the file
while(fin)
{
for(int i = 0;i<row.size();i++)
{
while (row[i]!= '/') //goes through the row until it finds the '/' symbol
{
if(i>row.size()) break;
i++;
}
if (i>row.size()) break; //checks if the end of the row has been reached
if(row[i+1] =='/' || row[i+1]=='*') //checks if the next symbol is the start of a comment
{
result = row[i];
result+= row[i+1];
}
if(result!="")
{
comment = ""; //creates a single line comment
if(result==lineCommentStart)
{
while (i<row.size())
{
comment+=row[i]; //the comment string adds the symbols of the comment
i++;
}
fout << comment << endl; //comment gets output in the endresult.txt file
comment=""; //after the output, it is renewed to the beginning stage
break;
}
comment = ""; //creates a block comment
if(result==blockCommentStart || result==lineCommentStart)
{
comment=row[i];
comment+=row[i+1]; //writes the comment symbols in the output file
i+=2; //jumps over the comment symbols
while(row[i]!='/') //adds symbols to the comment whilst it hasn't reached the end symbols
{
if(i>=row.size())
{
fout << comment << " "; //comment gets output in the endresult.txt file
getline(fin, row); //reads the next line
comment = ""; //comment string gets renewed for the new row
i=0; //moves the iterator back to the beginning position
}
comment+=row[i];
i++;
}
fout << comment << endl;
}
result = ""; //results gets renewed to the beginning stage
}
}
getline(fin,row); // reads the next line
};
fin.close();
fout.close(); //after the work is done, the files are closed
}
Here is the content of the test file code.txt:
// merge algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::merge, std::sort
#include <vector> // std::vector
using namespace std;
int main () {
vector<int> first = {1,5,8};
int second[] = {2,3,7,9};
vector<int> v(7);
merge (first.begin(),first.end(),second,second+4,v.begin());
std::cout << "The resulting vector contains:";
for(auto &i: v) cout<<i<<" "; cout<<endl;
}
/*
Comment which is in
multiple
// single line inside block
lines or a block
*/
//comment
Here is the result of the program(endresult.txt):
// merge algorithm example
// std::cout
// std::merge, std::sort
// std::vector
/* Comment which is in multiple /
//comment
So as one can see, the single line comment inside the block comment breaks the entire block comment structure. I would really appreciate any help or insight on how to solve this problem. Thank you!
EDIT: I've tried solving this further by replacing the
while(row[i]!='/')
line in the code near the blockComment segment with
while(row[i]!='/' || row[i-1]!='*')
and it now seems to include the single line comment however the formatting is a little wonky because the entire block comment then gets output together in a single line. Is there an easy solution to this, something like adding an endline after every row that is read?

Need to read a line of text from an archive.txt until "hhh" its found and then go to the next line

My teacher gave me this assignment, where I need to read words from a file and then do some stuff with them. My issue is that these words have to end with "hhh", for example: groundhhh, wallhhh, etc.
While looking for a way to do this, I came up with the getline function from the <fstream> library. The thing is that getline(a,b,c) uses 3 arguments, where the third argument is to read until c is found but c has to be a char, so it doesn't work for me.
In essence, what I'm trying to achieve is reading a word from a file, "egghhh" for example, and make it so if "hhh" is read then it means the line finishes there and I receive "egg" as the word. My teacher used the term "sentinel" to describe this hhh thing.
This is my try:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
ifstream read_archive;
void showListWords(){
string word;
string sentinel = "hhh";
while (getline(read_archive,word,sentinel)){
cout << word << endl;
}
}
void openReadArchive(){
read_archive.open("words.txt");
if(read_archive.fail())
cout << "There is something wrong with the archive" << endl;
}
As you discovered, std::getline() allows you to specify only 1 char for the "sentinel" that it stops reading on, where '\n' (line break) is the default.
What you can do is use this default to read an entire line of text from the file into a std::string, then put that string into an std::istringstream and read words from that stream until the end of the stream is reached or you find a word that ends with "hhh", eg:
#include <sstream>
#include <limits>
void showListWords()
{
string line, word;
string sentinel = "hhh";
while (getline(read_archive, line))
{
istringstream iss(line);
while (iss >> word)
{
if (word.length() > sentinel.length())
{
string::size_type index = word.length() - sentinel.length();
if (word.compare(index, sentinel.length(), sentinel) == 0)
{
word.resize(index);
iss.ignore(numeric_limits<streamsize>::max());
}
}
cout << word << endl;
}
}
}
In which case, you could alternatively just read words from the original file stream, stopping when you find a word that ends with "hhh" and ignore() the rest of the current line before continuing to read words from the next line, eg:
#include <limits>
void showListWords()
{
string word;
string sentinel = "hhh";
while (read_archive >> word)
{
if (word.length() > sentinel.length())
{
string::size_type index = word.length() - sentinel.length();
if (word.compare(index, sentinel.length(), sentinel) == 0)
{
word.resize(index);
read_archive.ignore(numeric_limits<streamsize>:max, '\n');
}
}
cout << word << endl;
}
}

Why is this word sorting program only looping once?

I'm trying to create a word sorting program that will read the words in a .txt file and then write them to a new file in order from shortest words to longest words. So, for instance, if the first file contains:
elephant
dog
mouse
Once the program has executed, I want the second file (which is initially blank) to contain:
dog
mouse
elephant
Here's the code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string word;
ifstream readFrom;
ofstream writeTo;
readFrom.open("C:\\Users\\owner\\Desktop\\wordlist.txt");
writeTo.open("C:\\Users\\owner\\Desktop\\newwordlist.txt");
if (readFrom && writeTo)
{
cout << "Both files opened successfully.";
for (int lettercount = 1; lettercount < 20; lettercount++)
{
while (readFrom >> word)
{
if (word.length() == lettercount)
{
cout << "Writing " << word << " to file\n";
writeTo << word << endl;
}
}
readFrom.seekg(0, ios::beg); //resets read pos to beginning of file
}
}
else
cout << "Could not open one or both of files.";
return 0;
}
For the first iteration of the for loop, the nested while loop seems to work just fine, writing the correct values to the second file. However, something goes wrong in all the next iterations of the for loop, because no further words are written to the file. Why is that?
Thank you so much.
while (readFrom >> word)
{
}
readFrom.seekg(0, ios::beg); //resets read pos to begin
The while loop will continue until special flags are set on readFrom, namely, the EOF flag. Seeking to the beginning does not clear any flags, including EOF. Add the following line right before the seek to clear the flags and your code should work fine.
readFrom.clear();
After seek, clear the EOF flag.
readFrom.clear();

why does vector.size() read in one line too little?

when running the following code, the amount of lines will read on less then there actually is (if the input file is main itself, or otherwise)
why is this and how can i change that fact (besides for just adding 1)?
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
// open text file for input
string file_name;
cout << "please enter file name: ";
cin >> file_name;
// associate the input file stream with a text file
ifstream infile(file_name.c_str());
// error checking for a valid filename
if ( !infile ) {
cerr << "Unable to open file "
<< file_name << " -- quitting!\n";
return( -1 );
}
else cout << "\n";
// some data structures to perform the function
vector<string> lines_of_text;
string textline;
// read in text file, line by line
while (getline( infile, textline, '\n' )) {
// add the new element to the vector
lines_of_text.push_back( textline );
// print the 'back' vector element - see the STL documentation
cout << "line read: " << lines_of_text.back() << "\n";
}
cout<<lines_of_text.size();
return 0;
}
The code you have is sound. Here's a small test case that might help:
void read_lines(std::istream& input) {
using namespace std;
vector<string> lines;
for (string line; getline(input, line);) {
lines.push_back(line);
cout << "read: " << lines.back() << '\n';
}
cout << "size: " << lines.size() << '\n';
}
int main() {
{
std::istringstream ss ("abc\n\n");
read_lines(ss);
}
std::cout << "---\n";
{
std::istringstream ss ("abc\n123\n");
read_lines(ss);
}
std::cout << "---\n";
{
std::istringstream ss ("abc\n123"); // last line missing newline
read_lines(ss);
}
return 0;
}
Output:
read: abc
read:
size: 2
---
read: abc
read: 123
size: 2
---
read: abc
read: 123
size: 2
I think I have tracked down the source of your problem. In Code::Blocks, a completely empty file will report that there is 1 line in it (the current one) in the gizmo on the status bar at the bottom of the IDE. This means that were you actually to enter a line of text, it would be line 1. In other words, Code::Blocks will normally over-report the number of actual lines in a file. You should never depend on CB, or any other IDE, to find out info on files - that's not what they are for.
Well, if the last line of your file is just '\n', you don't push it into the vector. If you want it to be there, change the loop to:
while (getline( infile, textline, '\n' ).gcount() > 0)
{
if (infile.fail()) break; //An error occurred, break or do something else
// add the new element to the vector
lines_of_text.push_back( textline );
// print the 'back' vector element - see the STL documentation
cout << "line read: " << lines_of_text.back() << "\n";
}
Use the gcount() member to check how many characters were read in the last read - this will return 1 if it only read a delimiter character.
Ok so here is an explanation that you will hopefully understand. Your code should work fine if the file we're talking about doesn't end with newline. But what if it does? Let's say it looks like this:
"line 1"
"line 2"
""
Or as a sequence of characters:
line 1\nline 2\n
This file has THREE lines -- the last one being empty but it's there. After calling getline twice, you've read all the characters from the file. The third call to getline will say oops, end of file, sorry no more characters so you'll see only two lines of text.