Segmentation Fault Before main() - c++

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();
}

Related

Reading words from a file into dynamically allocated array

So I am trying to read string data from a file into a dynamically allocated array, but I cannot seem to get the correct code for doing so. I have below a code using an array of a preset size but that would not be efficient, hence why I want to use dynamic memory allocation. I know I have to use pointers but I am fairly new to the concept so any help would be greatly appreciated.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <string>
#define SIZE 100
using namespace std;
void loadData();
int main()
{
loadData();
return 0;
{
string fileName;
std::string wordArray[SIZE];
cout << "Please enter the name of the text file you want to process followed by '.txt': " << endl;
cin >> fileName;
ifstream dataFile(fileName);
if (dataFile.fail()) {
cerr << fileName << " could not be opened." << endl; //error message if file opening fails
exit(-1);
}
while (!dataFile.eof()) {
for (int i = 0; i < SIZE; i++) {
dataFile >> wordArray[I];
for (std::string& s : wordArray) //this for loop transforms all the words in the text file into lowercase
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) { return std::tolower(c); });
}
}
dataFile.close();
}
}
The below program show how you can store the strings read from the input.txt file and store them in lowercase in a std::vector.
Version 1: Stores word by word(in lowercase) into the vector
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <sstream>
int main()
{
std::ifstream inputFile("input.txt");
//create vector that will contain the words in the file in lowercase
std::vector<std::string> wordVec;
std::string line, individualWord;
if(inputFile)
{
while(std::getline(inputFile, line, '\n'))
{
std::istringstream ss(line);
while(ss >> individualWord)//word by word
{
std::transform(individualWord.begin(), individualWord.end(), individualWord.begin(),
[](unsigned char c)
{ return std::tolower(c);
});
wordVec.push_back(individualWord);
}
}
}
else
{
std::cout<<"file could not be opened"<<std::endl;
}
inputFile.close();
//lets print out the elements of the vector to check if elements are correctly stored in lowercase
for(const std::string &elem: wordVec)
{
std::cout<<elem<<std::endl;
}
return 0;
}
The output of version 1 can be seen here.
Version 2: Stores a complete single line(in lowercase) into the vector
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
int main()
{
std::ifstream inputFile("input.txt");
//create vector that will contain the words in the file in lowercase
std::vector<std::string> wordVec;
std::string line;
if(inputFile)
{
while(std::getline(inputFile, line, '\n'))
{
std::transform(line.begin(), line.end(), line.begin(),
[](unsigned char c)
{ return std::tolower(c);
});
wordVec.push_back(line);
}
}
else
{
std::cout<<"file could not be opened"<<std::endl;
}
inputFile.close();
//lets print out the elements of the vector to check if elements are correctly stored in lowercase
for(const std::string &elem: wordVec)
{
std::cout<<elem<<std::endl;
}
return 0;
}
The output of the above(version 2) program can be seen here.
The difference between version 1 and 2 is that version 1 reads a complete line and then read word by word and store those words(in lowercase) into the std::vector while version 2 reads a complete line(which ends with '\n') and store that line(in lowercase) into the std::vector.
Sometime life could be easy. By using modern C++ elements, the implementation will be really simple in the end.
I am not so sure what I should explain for 3 lines of code. It is basically visible with the comments in the code.
Please see the first solution:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <cctype>
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
// Define a vector and read all words from the file
std::vector words(std::istream_iterator<std::string>(inputStream), {});
// Show result to the user
std::copy(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
}
Then next solution converts the word into lower case. So, I had to write 4 statements. Please see the below second solution.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <cctype>
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
std::vector<std::string> words{};
// Read all words from the file and convert to lower case
std::transform(std::istream_iterator<std::string>(inputStream), {}, std::back_inserter(words),
[](std::string w) { for (char& c : w) c = std::tolower(c); return w; });
// Show result to the user
std::copy(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
}
If data does not need to be stored, we can come up with a 2-statement version.
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iterator>
#include <cctype>
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
// Read all words from the file and convert to lower case and output it
std::transform(std::istream_iterator<std::string>(inputStream), {}, std::ostream_iterator<std::string>(std::cout, "\n"),
[](std::string w) { for (char& c : w) c = std::tolower(c); return w; });
}
}
Sometime teachers want the students to learn dynamic memory management using pointers.
But the usage of pointers for owned memory is strongly discouraged. The std::vector has been invented for that reason over a decade ago.
Anyway, I will also show a solution using new. It works, but you should not use it.
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
// Do some initial allocation of memory
std::string* words = new std::string[1]{};
unsigned int numberOfAvaliableSlotsInDynamicArray{1};
// Now we want to read words. We want also to count the words,so that we can allocate appropriate memory
std::string word{};
unsigned int wordCounter{};
// Read all words in a loop
while (inputStream >> word) {
// Check, if we still have enough space in our dynamic array
if (wordCounter >= numberOfAvaliableSlotsInDynamicArray) {
// Oh, we are running out of space. Get more memory
numberOfAvaliableSlotsInDynamicArray *= 2;
std::string* temp = new std::string[numberOfAvaliableSlotsInDynamicArray]{};
// Copy all existing data into new array
for (unsigned int i{}; i < wordCounter; ++i)
temp[i] = words[i];
// Delete old memory
delete[] words;
// And assign new storage to words
words = temp;
}
// STore the recently read word at the end of the array
words[wordCounter] = word;
// Count words. Now we have one word more
++wordCounter;
}
// Now we have read all words from the file. Show output
for (unsigned int i{}; i < wordCounter; ++i)
std::cout << words[i] << '\n';
// Release memory
delete[] words;
}
}
And even smart pointers, which shall be used as pointers if at all, are not nice.
Also the following should not be used.
#include <iostream>
#include <fstream>
#include <string>
#include <memory>
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
// Do some initial allocation of memory
std::unique_ptr<std::string[]> words = std::unique_ptr<std::string[]>(new std::string[1]);
unsigned int numberOfAvaliableSlotsInDynamicArray{ 1 };
// Now we want to read words. We want also to count the words,so that we can allocate appropriate memory
std::string word{};
unsigned int wordCounter{};
// Read all words in a loop
while (inputStream >> word) {
// Check, if we still have enough space in our dynamic array
if (wordCounter >= numberOfAvaliableSlotsInDynamicArray) {
// Oh, we are running out of space. Get more memory
numberOfAvaliableSlotsInDynamicArray *= 2;
std::unique_ptr<std::string[]> temp = std::unique_ptr<std::string[]>(new std::string[numberOfAvaliableSlotsInDynamicArray]);
// Copy all existing data into new array
for (unsigned int i{}; i < wordCounter; ++i)
temp[i] = std::move(words[i]);
// And assign new storage to words
words = std::move(temp);
}
// STore the recently read word at the end of the array
words[wordCounter] = word;
// Count words. Now we have one word more
++wordCounter;
}
// Now we have read all words from the file. Show output
for (unsigned int i{}; i < wordCounter; ++i)
std::cout << words[i] << '\n';
}
}
Conclusion: Use a std::vector.

How to get input an array of strings with \n as delimiter?

#include<bits/stdc++.h>
using namespace std;
int main()
{
int i=0;
char a[100][100];
do {
cin>>a[i];
i++;
}while( strcmp(a[i],"\n") !=0 );
for(int j=0;j<i;i++)
{
cout<<a[i]<<endl;
}
return 0;
}
Here , i want to exit the do while loop as the users hits enter .But, the code doesn't come out of the loop..
The following reads one line and splits it on white-space. This code is not something one would normally expect a beginner to write from scratch. However, searching on Duckduckgo or Stackoverflow will reveal lots of variations on this theme. When progamming, know that you are probably not the first to need the functionality you seek. The engineering way is to find the best and learn from it. Study the code below. From one tiny example, you will learn about getline, string-streams, iterators, copy, back_inserter, and more. What a bargain!
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <vector>
int main() {
using namespace std;
vector<string> tokens;
{
string line;
getline(cin, line);
istringstream stream(line);
copy(istream_iterator<string>(stream),
istream_iterator<string>(),
back_inserter(tokens));
}
for (auto s : tokens) {
cout << s << '\n';
}
return 0;
}
First of all, we need to read the line until the '\n' character, which we can do with getline(). The extraction operator >> won't work here, since it will also stop reading input upon reaching a space. Once we get the whole line, we can put it into a stringstream and use cin >> str or getline(cin, str, ' ') to read the individual strings.
Another approach might be to take advantage of the fact that the extraction operator will leave the delimiter in the stream. We can then check if it's a '\n' with cin.peek().
Here's the code for the first approach:
#include <iostream> //include the standard library files individually
#include <vector> //#include <bits/stdc++.h> is terrible practice.
#include <sstream>
int main()
{
std::vector<std::string> words; //vector to store the strings
std::string line;
std::getline(std::cin, line); //get the whole line
std::stringstream ss(line); //create stringstream containing the line
std::string str;
while(std::getline(ss, str, ' ')) //loops until the input fails (when ss is empty)
{
words.push_back(str);
}
for(std::string &s : words)
{
std::cout << s << '\n';
}
}
And for the second approach:
#include <iostream> //include the standard library files individually
#include <vector> //#include <bits/stdc++.h> is terrible practice.
int main()
{
std::vector<std::string> words; //vector to store the strings
while(std::cin.peek() != '\n') //loop until next character to be read is '\n'
{
std::string str; //read a word
std::cin >> str;
words.push_back(str);
}
for(std::string &s : words)
{
std::cout << s << '\n';
}
}
You canuse getline to read ENTER, run on windows:
//#include<bits/stdc++.h>
#include <iostream>
#include <string> // for getline()
using namespace std;
int main()
{
int i = 0;
char a[100][100];
string temp;
do {
getline(std::cin, temp);
if (temp.empty())
break;
strcpy_s(a[i], temp.substr(0, 100).c_str());
} while (++i < 100);
for (int j = 0; j<i; j++)
{
cout << a[j] << endl;
}
return 0;
}
While each getline will got a whole line, like "hello world" will be read once, you can split it, just see this post.

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.

C++: Counting function only counts first line

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.

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;
}