C++: Counting function only counts first line - c++

I have a problem trying to count words inside of a vector. A vector holds every line from a file as an object. v[0] is the first line, v[1] is the second line, so on.
For my countWords() function, it only works for counting v[0]. Any object past that is ignored or missed some how. Any ideas? Thanks in advance.
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int countWords(vector<string> v)
{
stringstream ss;
string word;
int count = 0;
for(int i = 0; i < v.size(); i++) {
ss.str(v[i]);
while (ss >> word)
count++;
}
return count;
}
void readFile(string filename,vector<string> &v)
{
fstream file;
string line;
file.open(filename,ios::in);
while(getline(file,line)) { //Reads the file line by line ...
if(line == "") //... ignoring any empty lines ...
continue;
v.push_back(line); //... and puts them into our vector.
}
file.close();
}
int main(int argc,char* argv[])
{
if (argc != 2) { //Terminate unless the user enters -ONE- entry.
cout << "Usage: " << argv[0] << " <filename>" << endl;
exit(1);
}
string filename = argv[1];
vector<string> fileContents;
readFile(filename,fileContents);
cout << countWords(fileContents) << endl;
}

As an alternative to RichieHindle's answer, this works too. Just have the stringstream scope local to the for loop and it will reset properly.
int countWords(vector<string> v)
{
string word;
int count = 0;
for(int i = 0; i < v.size(); i++) {
stringstream ss(v[i]);
while (ss >> word)
count++;
}
return count;
}

Before you reuse stringstream you must do
ss.clear();
after your while loop.
You could also declare it inside the for() loop, but then it would be reinitialized again. For readabillity, this might be better. Performancewise it could make a difference.

I bet ss goes into an error state when you've exhausted it for the first time and doesn't reset just because you call str.
Declare ss inside the for loop and pass the string directly to the constructor. This avoids such problems.
In general, you have the bad habit of declaring your variables in a bunch instead of closest to where you need them, and not using constructors. For example, you could pass the filename to fstream's constructor instead of calling open. And you could use ifstream so you don't need the second argument.

Related

How to get the lines of a file and the amount of characters per line

I am trying to get the amount of lines in a file in order to make an array to store the characters of each line. I think the problem is with the two while loops iterating at the same time.
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
using std::cin;
using std::cout;
using std::cerr;
using std::endl;
using std::string;
int main(int argc, char *argv[]){
cout << "input file" << endl;
string file;
cin >> file;
ifstream inFile;
inFile.open(file, ios::in);
cout << "success1";
if (inFile.is_open()) {
vector<double> inputs;
string line;
string s;
int current;
int sTotal;
while(!inFile.eof()) {
getline(inFile, s);
sTotal++;
}
while (!inFile.eof()) {
getline(inFile, line);
cout << "success2";
char *lineArr = new char[line.length()][sTotal];
for(int j = 0;j < sTotal;j++){
for (int i = 0; i < sizeof(lineArr); i++) {
lineArr[i] = line[i];
}
}
}
}else cout << "fail";
}
Any help would be appreciated. Im probably looking at this all wrong.
A main issue with your code is that you consume the stream in the first while loop and then try to do it again. They are not "iterating at the same time", they are two successive loops with the same exit condition. In virtually every case that results in the second one not executing at all.
Also, if you are allowing yourself to use str::string and std::vector, avoid using char*, it only adds confusion.
Try a single loop, using a vector<string> to push each line you get and print in the end the length of the loop. Have a look at these links: std::vector, getline
A rough sketch of what I mean:
vector<string> lines;
string line;
while(getLine(inFile, line)){
lines.push(line);
}
cout << lines.size() << endl;
Then if you want to count the words per line, iterate again through the vector, and split each element; there are several ways to do that as well.

segmentation fault (core dumped) reading line into array

I'm trying to read in a file into an array so that I could process the array into selection sort. But when I try to read in the file, I get a segmentation fault(core dumped) error. Here is my code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string array[40707];
int loop = 0;
int loop2;
string line;
ifstream inFile("beowulf.txt");
if (inFile.is_open())
{
while(!inFile.eof())
{
getline(inFile, line);
array[loop] = line;
loop++;
}
inFile.close();
}
else cout << "Unable to open file" << endl;
for (loop2 =0; loop2 <= loop; loop2++)
cout << array[loop2] << endl;
return 0;
}
Change your string array to:
std::vector<std::string> array;
Then you can read the file and copy into the vector simply as:
std::copy(std::istream_iterator<std::string>(inFile),
std::istream_iterator<std::string>(),
std::back_inserter(array));
EDIT: To read the file line-by-line, either define your own insert_iterator or do it like this:
std::string line;
while (getline(inFile, line))
array.push_back(line);
Your code will then change to something like this
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<string> array;
string line;
ifstream inFile("beowulf.txt");
if (!inFile.is_open()) {
cerr << "Unable to open file" << endl;
return 1;
}
while (getline(inFile, line))
array.push_back(line);
inFile.close();
for (int i = 0; i < array.size(); ++i)
cout << array[i] << endl;
return 0;
}
Two potential error cases I can see right away.
Overrunning the end of the end of array. It can happen while reading because there is no guard on loop. If the array read was exactly 40707 lines it will happen while printing out when loop2 == loop. Either of these is probably the cause of the segfault. The recommended solution is to use C++'s std::vector because it will dynamically size itself to the input and automate iterating through the items stored.
The second error is less severe, but allows an infinite loop on an IO error. The end of file may never be reached if an error prevents reading a line and places the stream in an error condition. Unlikely to happen with getline, but a common occurrence with formatted reads.
Using most of the OP's solution:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<string> array; // vector eliminates buffer overflow problem of string array.
//int loop = 0; vector also eliminates the need for this counter
//int loop2; and this one
string line;
ifstream inFile("beowulf.txt");
if (inFile.is_open())
{
while(getline(inFile, line)) //exits if anything goes wrong with the file IO
{
array.push_back(line); // stores line in vector
}
inFile.close();
}
else cout << "Unable to open file" << endl;
// C++11 way
for (string const & str: array)
{ // iterates through the string automatically
cout << str << endl;
}
/* Old C++ way
for (vector<string>::iterator str = array.begin();
token != array.end();
++token)
{ // iterates through the string manually
cout << *str << endl;
}
*/
return 0;
}

Segmentation Fault Before main()

I'm having a problem with this code. After compiling with g++, I run a.out and I get a segmentation fault and no "here" displayed. The code is pretty short:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
bool inWords(vector<string> words, string str);
int main()
{
cout << "here";
vector<string> words;
string str;
istringstream iss;
ifstream file("data.txt", ifstream::in);
// read in words
for(int i = 0; file >> str; /*no i++*/)
{
if(str[str.length() - 1] == '.')
str.erase( str.length()-1);
// if word has a period at the end, erase it
if(!inWords(words, str))
{
// if word is not in vector words, add it
words.push_back(str);
i++;
}
}
// output each word
for (vector<string>::size_type i = 0; i < words.size(); i++)
cout << words[i];
// return to beginning of file
file.clear();
file.seekg(0, ios::beg);
// read in sentences
// to be implemented
file.close();
return 0;
}
bool inWords(vector<string> words, string str)
{
for(int i = 0; !words[i].empty(); i++)
if(words[i] == str) { return true; }
return false;
}
As far as I know, nothing should be a problem. data.txt is definitely in the same directory as the file and I receive no arguments from the command line. Can anyone help?
It won't be before main. Try using a debugger to see where it happens (eg GDB) which are incredibly handy tool. The reason you don't see "here" is because the buffer isn't flushed. Put a << std::endl after it so that it forces output at that point.
A technicality: You can segfault before main but that will happen in a constructor. I see you have no custom objects defined/instantiated in global scope.
The technique for iterating the vector in inWords is wrong and interacts with elements past the end of the vector causing the segmentation fault.
This program immediately accesses words[0] in inWords when the words vector is empty and words[0] (the first element of the vector) does not exist yet because the size of the vector is still zero but the loop does not do anything to avoid this condition.
I think inWords could be better implemented with std::find, perhaps like this:
bool inWords(const vector<string>& words, const string& str)
{
return std::find(words.begin(), words.end(), str) != words.end();
}
Remember to #include <algorithm> to make use of std::find. I also changed the parameters to pass by const reference, so you'll need to change the forward declaration of that function. I also added an endl to the output to make it readable.
Full text of repair:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;
bool inWords(const vector<string>& words, const string& str);
int main()
{
vector<string> words;
string str;
istringstream iss;
ifstream file("data.txt", ifstream::in);
// read in words
for(int i = 0; file >> str; /*no i++*/)
{
if(str[str.length() - 1] == '.')
str.erase( str.length()-1);
// if word has a period at the end, erase it
if(!inWords(words, str))
{
// if word is not in vector words, add it
words.push_back(str);
i++;
}
}
// output each word
for (vector<string>::size_type i = 0; i < words.size(); i++)
cout << words[i] << std::endl;
// return to beginning of file
file.clear();
file.seekg(0, ios::beg);
// read in sentences
// to be implemented
file.close();
return 0;
}
bool inWords(const vector<string>& words, const string& str)
{
return std::find(words.begin(), words.end(), str) != words.end();
}

Reading multiple lines of strings from a file and storing it in string array in C++

I have a class called StringList consisting of a constructor and a de-structor. What I am shooting for is for the ability of my program to retain its strings in the array even after it is not running. The way i want to do this is to have my constructor function read strings from a file and store them into my string array (str[]). My de-structor will save my current strings into my file. I am having trouble reading and storing from the file when memory is created. I want each word to be one element in the array.
For example, in the file that is being read from, the strings are stored as such:
HELLO
MOM
DAD
FOUR
YELLOW
I want each word to be a slot. In other words. str[0] = HELLO, str[1]= MOM, str[2]=DAD and such.
Here is my constructor function:
StringList::StringList()
{
numberOfStrings=0;
str = new string[1000000];
ifstream myfile ("Read.txt");
if (myfile.is_open())
{
for (int i = 0; i < 1000000; i++)
{
getline(myfile,str[i]);
numberOfString++;
}
myfile.close();
}
}
Problem here is the for (int i=0; i<100000;i++) line
What this did is continue to fill each blank space into the element until it reached 100000.
Same if i put i<20, it would read all the contents and add blanks to fill to 20. Is there anyway to fill up to the amount of actual strings in the txt. file?
NumberOfStrings++ is outside of your for loop when you read (i.e. it only gets incremented once). Also please consider using std::vector<std::string> instead of a dynamic array.
Here's a version of your code using std::vector instead of an array:
#include <vector>
#include <fstream>
#include <iostream>
#include <string>
class StringList
{
public:
StringList(): str(1000000), numberOfStrings(0)
{
std::ifstream myfile ("Read.txt");
if (myfile.is_open())
{
for (int i = 0; i < str.size(); i++)
{
getline(myfile, str[i]);
numberOfStrings++;
}
myfile.close();
}
}
StringList::~StringList()
{
std::ofstream os("Read.txt");
for (int i = 0; i <numberOfStrings; i++)
{
os << str[i] << std::endl;
}
}
private:
std::vector<std::string> str;
int numberOfStrings;
};
As you can see the changes are rather minimal.
The numberOfStrings variable is only updated once after the for loop has finished. You can also simplify this without the need to specify a large number of lines to read by checking the return value of getline for failure. If you try to read past the end of file is getline will return false.
numberOfStrings = 0;
str = new std::string[1000000];
std::ifstream myfile("Read.txt");
if (myfile.is_open())
{
std::string line;
while(getline(myfile, str[numberOfStrings]))
numberOfStrings++;
myfile.close();
}
You can simplify this even further by using std::vector. To expand on the example provided in your answer StringList might look something like below.
StringList.h
#include <vector>
#include <string>
class StringList
{
public:
StringList();
void PrintWords();
private:
size_t numberOfLines;
std::vector<std::string> str;
};
StringList.cpp to read in single line into each string
#include "StringList.h"
#include <fstream>
StringList::StringList()
{
std::ifstream myfile("Read.txt");
if (myfile.is_open())
{
std::string line;
while(getline(myfile, line))
{
lines_.push_back(line);
}
myfile.close();
}
numberOfLines = str.size();
}
StringList.cpp to read in single word into each string using std::istream_itertor and std::copy
#include "StringList.h"
#include <fstream>
#include <istream>
#include <algorithm> // std::copy
#include <iterator> // istream_iterator
StringList::StringList()
{
std::ifstream myfile("Read.txt");
if (myfile.is_open())
{
std::copy(
istream_iterator<string>(myfile),
istream_iterator<string>(),
back_inserter(str));
}
numberOfLines = str.size();
}
Some other function to print the words
StringList::PrintWords()
{
for(size_t i = 0; i < numberOfLines; ++i)
{
std::cout << str[i] << std::endl;
}
}
I also recommend avoiding using using namespace std in your code. It pulls everything from std into the current scope (typically the global namespace) and can cause conflicts with identifiers.
This would be my approach to reading the data (this doesn't quite work the same as the other answers, but as long as your wordlist doesn't contain words with whitespace, it should work fine).
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
int main()
{
std::fstream myFile("read.txt");
if (myFile.is_open())
{
std::istream_iterator<std::string> iter(myFile), end;
std::vector<std::string> str(iter, end);
// print contents
for (int i = 0; i < str.size(); i++)
std::cout << i << ": " << str[i] << std::endl;
}
}
References:
istream_iterator
vector
You can continue to averse std::vector all you want, but for a scenario like this, it is the best tool for the job.

Trying to read from a file and skip punctuation in C++, tips?

I'm trying to read from a file, and make a vector of all the words from the file. What I tried to do below is have the user input the filename, and then have the code open the file, and skip characters if they aren't alphanumeric, then input that to a file.
Right now it just closes immediately when I input the filename. Any idea what I could be doing wrong?
#include <vector>
#include <string>
#include <iostream>
#include <iomanip>
#include <fstream>
using namespace std;
int main()
{
string line; //for storing words
vector<string> words; //unspecified size vector
string whichbook;
cout << "Welcome to the book analysis program. Please input the filename of the book you would like to analyze: ";
cin >> whichbook;
cout << endl;
ifstream bookread;
//could be issue
//ofstream bookoutput("results.txt");
bookread.open(whichbook.c_str());
//assert(!bookread.fail());
if(bookread.is_open()){
while(bookread.good()){
getline(bookread, line);
cout << line;
while(isalnum(bookread)){
words.push_back(bookread);
}
}
}
cout << words[];
}
I think I'd do the job a bit differently. Since you want to ignore all but alphanumeric characters, I'd start by defining a locale that treats all other characters as white space:
struct digits_only: std::ctype<char> {
digits_only(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table() {
static std::vector<std::ctype_base::mask>
rc(std::ctype<char>::table_size,std::ctype_base::space);
std::fill(&rc['0'], &rc['9']+1, std::ctype_base::digit);
std::fill(&rc['a'], &rc['z']+1, std::ctype_base::lower);
std::fill(&rc['A'], &rc['Z']+1, std::ctype_base::upper);
return &rc[0];
}
};
That makes reading words/numbers from the stream quite trivial. For example:
int main() {
char const test[] = "This is a bunch=of-words and 2#numbers#4(with)stuff to\tseparate,them, I think.";
std::istringstream infile(test);
infile.imbue(std::locale(std::locale(), new digits_only));
std::copy(std::istream_iterator<std::string>(infile),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
For the moment, I've copied the words/numbers to standard output, but copying to a vector just means giving a different iterator to std::copy. For real use, we'd undoubtedly want to get the data from an std::ifstream as well, but (again) it's just a matter of supplying the correct iterator. Just open the file, imbue it with the locale, and read your words/numbers. All the punctuation, etc., will be ignored automatically.
The following would read every line, skip non-alpha numeric characters and add each line as an item to the output vector. You can adapt it so it outputs words instead of lines. I did not want to provide the entire solution, as this looks a bit like a homework problem.
#include <vector>
#include <sstream>
#include <string>
#include <iostream>
#include <iomanip>
#include <fstream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
string line; //for storing words
vector<string> words; //unspecified size vector
string whichbook;
cout << "Welcome to the book analysis program. Please input the filename of the book you would like to analyze: ";
cin >> whichbook;
cout << endl;
ifstream bookread;
//could be issue
//ofstream bookoutput("results.txt");
bookread.open(whichbook.c_str());
//assert(!bookread.fail());
if(bookread.is_open()){
while(!(bookread.eof())){
line = "";
getline(bookread, line);
string lineToAdd = "";
for(int i = 0 ; i < line.size(); ++i)
{
if(isalnum(line[i]) || line[i] == ' ')
{
if(line[i] == ' ')
lineToAdd.append(" ");
else
{ // just add the newly read character to the string 'lineToAdd'
stringstream ss;
string s;
ss << line[i];
ss >> s;
lineToAdd.append(s);
}
}
}
words.push_back(lineToAdd);
}
}
for(int i = 0 ; i < words.size(); ++i)
cout << words[i] + " ";
return 0;
}