Using the fstream getline() function inside a class - c++

I'm trying to load lines of a text file containing dictionary words into an array object. I want an array to hold all the words that start with "a", another one for "b" ... for all the letters in the alphabet.
Here's the class I wrote for the array object.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
class ArrayObj
{
private:
string *list;
int size;
public:
~ArrayObj(){ delete list;}
void loadArray(string fileName, string letter)
{
ifstream myFile;
string str = "";
myFile.open(fileName);
size = 0;
while(!myFile.eof())
{
myFile.getline(str, 100);
if (str.at(0) == letter.at(0))
size++;
}
size -= 1;
list = new string[size];
int i = 0;
while(!myFile.eof())
{
myFile.getline(str, 100);
if(str.at(0) == letter.at(0))
{
list[i] = str;
i++;
}
}
myFile.close();
}
};
I'm getting an error saying:
2 IntelliSense: no instance of overloaded function "std::basic_ifstream<_Elem, _Traits>::getline [with _Elem=char, _Traits=std::char_traits<char>]" matches the argument list d:\champlain\spring 2012\algorithms and data structures\weeks 8-10\map2\arrayobj.h 39
I guess it's requiring me to overload the getline function, but I'm not quite certain how to go about or why it's necessary.
Any advice?

the function for streams that deals with std::string is not a member function of istream but rather a free function it is used like so. (the member function version deals with char*).
std::string str;
std::ifstream file("file.dat");
std::getline(file, str);
It is worth noting there are better safer ways to do what you are trying to do like so:
#include <fstream>
#include <string>
#include <vector>
//typedeffing is optional, I would give it a better name
//like vector_str or something more descriptive than ArrayObj
typedef std::vector<std::string> > ArrayObj
ArrayObj load_array(const std::string file_name, char letter)
{
std::ifstream file(file_name);
ArrayObj lines;
std::string str;
while(std::getline(file, str)){
if(str.at(0)==letter){
lines.push_back(str);
}
}
return lines;
}
int main(){
//loads lines from a file
ArrayObj awords=load_array("file.dat", 'a');
ArrayObj bwords=load_array("file.dat", 'b');
//ao.at(0); //access elements
}
don't reinvent the wheel; checkout vectors they are standard and will save you a lot of time and pain.
Final try not to put in using namespace std that is bad for a whole host of reasons I wont go into; instead prefix std objects with std:: so like std::cout or std::string.
http://en.cppreference.com/w/cpp/container/vector
http://en.cppreference.com/w/cpp/string/basic_string/getline
http://en.cppreference.com/w/cpp/string

Related

creating vectors for individually parsed sentences c++

class Read
{
public:
Read(const char* filename)
:mFile(filename)
{
}
void setString()
{
while(getline(mFile, str, '.'))
{
getline(mFile, str, '.');
str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
}
}
private:
ifstream mFile;
string str;
};
int main()
{
Read r("sample.txt");
return 0;
}
My ultimate goal is to parse through each sentence in the file so I used getline setting the delimiter to '.' to get each individual sentence. I want to create a sentence vector but am not really sure how to do so.
The file is pretty big so it will have a lot of sentences. How do I create a vector for each sentence?
Will it simply be vector < string > str? How will it know the size?
EDIT: I added a line of code to remove the '\n'
EDIT: Got rid of !eof
while(!myFile.eof())
getline(mFile, str, '.');
Where did you find that? Please put it back. Try:
std::vector<std::string> sentences;
while(std::getline(mFile, str, '.'))
sentences.push_back(str);
The vector container has a .size() function to return the number of populated elements. You should google "std::vector" and read through the functions in the API.
Vectors are dynamica arrays. You need not to worry about the size of the vector. You can use push_back() function to add element in the vector. I have made some changes in your code. Please check if this work for you..
#include<vector>
using namespace std;
class Read
{
public:
Read(const char* filename)
:mFile(filename)
{
}
void setString()
{
while(getline(mFile, str, '.'))
{
vec.push_back(str);
}
}
private:
ifstream mFile;
string str;
vector<string> vec;
};
int main()
{
Read r("sample.txt");
return 0;
}
#include <vector>
using namespace std;
...
vector<string> sentences;
sentences.push_back(line);
The vector is a dynamic array and it will resize itself as you keep adding sentences. If you know the number of sentences, you can increase the performance by calling:
sentences.resize(number of sentences here)

Putting strings into vector gives empty strings

Here's part my code:
#include <stdio.h>
#include<string>
#include<string.h>
#include<algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main(){
FILE *in=fopen("C.in","r");
//freopen("C.out","w",stdout);
int maxl=0;
int i;
string word;
vector<string> words;
while(!feof(in)){
fscanf(in,"%s ",word.c_str());
int t=strlen(word.c_str());
if(t>maxl){
maxl=t;
words.clear();
words.insert(words.end(),word);
}else if (t==maxl){
words.insert(words.end(),word);
}
}
the problem occurs at
words.insert(words.end,word)
while
word
contains the word from my file, the vector item
words[i]
contains an empty string.
How is this possible?
fscanf(in,"%s ",word.c_str());
That's never going to work. c_str() is a const pointer to the string's current contents, which you mustn't modify. Even if you do subvert const (using a cast or, in this case, a nasty C-style variadic function), writing beyond the end of that memory won't change the length of the string - it will just give undefined behaviour.
Why not use C++ style I/O, reading into a string so that it automatically grows to the correct size?
std::ifstream in(filename);
std::string word;
while (in >> word) {
if (word.size() > maxl) {
maxl = word.size();
words.clear();
words.push_back(word);
} else if (word.size() == maxl) {
words.push_back(word);
}
}

std::vector subscript out of range while reading a file into the vector of strings in C++

I am new to c++. I am learning fast, but i dont know much yet.
I cannot see the problem with index in this function:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
void get_rows(string filepath, vector<string> &rows);
int main() {
vector<string> rows;
get_rows("ninja.txt", rows);
for (int i = 0; i < rows.size(); i++) {
cout << rows[i] << endl;
}
}
void get_rows(string filepath, vector<string> &rows) {
ifstream file;
file.open(filepath);
string str;
int index = 0;
while (!file.eof()) {
getline(file, str);
rows[index] = str;
index++;
}
}
Any help will be appreciated.
You have constructed an std::vector<string> object:
vector<string> rows;
and then later you are trying to access its elements although there are no elements in this vector yet:
rows[index] = str;
You should push new elements into the vector using push_back method:
rows.push_back(str);
Also note that using while (!file.eof()) is wrong becuase getline might fail inside the loop:
while (!file.eof()) {
getline(file, str);
...
}
Your loop should look the following way:
while (std::getline(file, str)) {
if (str.empty()) continue; // we skip empty lines
rows.push_back(str); // and push non-empty lines at the end
}
vector<string> rows;
^
size() is 0
get_rows("ninja.txt", rows);
void get_rows(string filepath, vector<string> &rows) {
//...
int index = 0;
rows[index] = str; // but there is no rows[0] yet
//...
}
you should either use push_back to add new elements into vector or create a vector with specified size at the beginning (if it is known)
vector<string> rows(160);
which has advantage over the former as you can avoid potential reallocation (which may invalidate pointers to vector elements i.e)

Initializer but incomplete type?

The following code gives me 2 errors when i compile
#include <iostream>
#include <fstream>
#include <cstring>
#include "Translator.h"
using namespace std;
void Dictionary::translate(char out_s[], const char s[])
{
int i;
char englishWord[MAX_NUM_WORDS][MAX_WORD_LEN];
for (i=0;i < numEntries; i++)
{
if (strcmp(englishWord[i], s)==0)
break;
}
if (i<numEntries)
strcpy(out_s,elvishWord[i]);
}
char Translator::toElvish(const char elvish_line[],const char english_line[])
{
int j=0;
char temp_eng_words[2000][50];
//char temp_elv_words[2000][50]; NOT SURE IF I NEED THIS
std::string str = english_line;
std:: istringstream stm(str);
string word;
while( stm >> word) // read white-space delimited tokens one by one
{
int k=0;
strcpy (temp_eng_words[k],word.c_str());
k++;
}
for (int i=0; i<2000;i++) // ERROR: out_s was not declared in this scope
{
Dictionary::translate (out_s,temp_eng_words[i]); // ERROR RELATES TO THIS LINE
}
}
Translator::Translator(const char dictFileName[]) : dict(dictFileName)
{
char englishWord[2000][50];
char temp_eng_word[50];
char temp_elv_word[50];
char elvishWord[2000][50];
int num_entries;
fstream str;
str.open(dictFileName, ios::in);
int i;
while (!str.fail())
{
for (i=0; i< 2000; i++)
{
str>> temp_eng_word;
str>> temp_elv_word;
strcpy(englishWord[i],temp_eng_word);
strcpy(elvishWord[i],temp_elv_word);
}
num_entries = i;
}
str.close();
}
}
The first one is at std::string istringstream stm(str); where it says it the variable has an initializer but incomplete type. If I put in std::string istringstream stm(str); it says expected initializer before stm andstm was not declared in the scope.
It also says out_s was not declared in this scope at Dictionary::translate (out_s,temp_eng_words[i]);. I don't see why one parameter is recognisied and one is not?
Thanks in advance.
You have to include header file:
#include <sstream>
#include <string>
when you want to use stringstream and string.
Meanwhile:
Dictionary::translate (out_s,temp_eng_words[i]);
If out_s is not a member of the class, you seems forgot to define out_s before using it inside toElvish.
Meanwhile:
while( stm >> word) // read white-space delimited tokens one by one
{
int k=0; //^^Why do you initialize k everytime you read a word?
strcpy (temp_eng_words[k],word.c_str());
k++;
}
You just need to include sstream
Your translator would be much simpler if you used std::map.
#include <map>
#include <string>
// map[english word] returns the elvish word.
typedef std::map<std::string, std::string> Dictionary;
// Define the dictionary
Dictionary english_to_elvish_dictionary;
std::string To_Elvish(const std::string& english_word)
{
Dictionary::iterator iter;
std::string elvish_word;
iter = english_to_elvish_dictionary.find(english_word);
if (iter != english_to_elvish_dictionary.end())
{
// English word is in dictionary, return the elvish equivalent.
elvish_word = *iter;
}
return elvish_word;
}
The above code fragment replaces most of your code and reduces your issues with arrays of arrays of C-strings. Less code == less problems.
To see a list of issues your having, search StackOverflow for "[c++] elvish english".

populating char**

I'm trying to read in a list of files from another file. The file reading works but populating the array of char* isn't. It works for the first iteration but then gets a bad pointer on the next line. I tried with a vector of strings but was having problems, I think due to its destructor trying to free an argv.
char **datafiles = (char**)malloc(0);
int filecount = 0;
master.AddDataFiles(argv[1],datafiles,filecount);
int Manager::AddDataFiles(char *filename, char **filelist, int &filecount)
{
const int LINEMAX = 64;
struct stat info;
std::ifstream is(filename);
if (is.fail()) return 1;
char buffer[LINEMAX];
while(!is.eof())
{
is.getline(buffer,LINEMAX);
realloc(filelist,sizeof(char**) * (filecount + 1));
filelist[filecount] = (char*) malloc(std::strlen(buffer) + 1);
std::strcpy(filelist[filecount],buffer);
filecount++;
}
return 0;
}
Using realloc correctly is a bit tricky -- it can (and sometimes, but not always, will) return a different pointer than the one you passed to it, so you have to do something like this:
char **temp = realloc(filelist, sizeof(char**) * filecount+1);
if (temp != NULL)
filelist = temp;
else
failed_allocation();
Also note that your while (!file.eof()) is a classic mistake -- it won't sense the end of the file correctly.
I'd revisit the vector of strings option though. Perhaps you could post the code you had, and ask about whatever problem(s) you encountered with it. Getting it to work well will almost certainly be less work than fixing this, and the result will almost certainly be more solid and understandable.
Correct code for this would look something like:
std::vector<std::string> Manager::AddDataFiles(std::string const &filename) {
std::ifstream infile(filename.cstr());
std::vector<std::string> filenames;
std::string temp;
while (std::getline(infile, temp))
filenames.push_back(temp);
return filenames;
}
Why don't you use std::vector<std::string> instead of char**? This elegantly solves your problem!
If the filenames do not contain space then here is even more elegant solution (or else you can see Jerry's solution):
void Manager::AddDataFiles(const char *filename, std::vector<std::string> &filelist)
{
std::istream_iterator<string> start(std::ifstream(filename));
std::istream_iterator<string> end;
std::copy(start, end, std::back_inserter(filelist));
}
For this, you've to include these:
#include <iterator>
#include <algorithm>
#include <vector>
#include <string>
Use a std::string and std::vector<std::string>. It might also makes sense to have the file list a member.
#include <fstream>
#include <string>
#include <vector>
void Manager::AddDataFiles(const std::string& filename)
{
std::ifstream in(filename.c_str());
for( std::string line; std::getline(in, line); ) {
filelist.push_back(line);
}
}