std::getline with char* to strings - c++

I have this code for reading from a file:
MyObject* LoadObject(wstring filePath)
{
ifstream fileReader;
fileReader.open(filePath);
if (fileReader.is_open())
{
string currentLine;
//std::basic_istream &std::getline
while (getline(fileReader, currentLine))
{
//Logic for loading MyObject* here
}
}
}
Now I'd like to be able to read from a char* buffer as well, is there a way I can keep the same reading logic and just changing how I read the lines?
MyObject* LoadObject(char* buffer, ulong length)
{
//Change how I read each line
//Keep the same logic for loading MyObject*
}

Move your work into something that takes a std::istream & to read from:
MyObject *ReadObject(std::istream &is)
{
string currentLine;
while (getline(is, currentLine))
{
//Logic for loading MyObject* here
}
}
Now just make your other functions use this one:
MyObject* LoadObject(wstring filePath)
{
ifstream fileReader;
fileReader.open(filePath);
if (fileReader.is_open())
{
return ReadObject(fileReader);
}
... //return something else
}
MyObject* LoadObject(char* buffer, ulong length)
{
std::string str(buffer, length);
std::istringsteam iss(str);
return ReadObject(iss);
}
Some small notes:
Pass filePath by const reference since you're not changing it and don't need a copy.
Make buffer a const char * if it's a C string, since you don't need to modify it. Better to use one parameter that knows the length instead of relying on the caller to synchronize the two parameters.
Consider returning a MyObject by value unless you really need the pointer.
Consider naming the two overloads of LoadObject differently to convey their intent. For all the caller knows, the C string overload could be a file path as well.

I'm not sure it would perform well, but you can do something like this:
std::istringstream iss(std::string(buffer, length));
while (getline(iss, currentLine))
{ ... }

You should modify your existing method and take actual reading code into separate method:
MyObject* LoadObjectFromStream(std::istream &in)
{
string currentLine;
//std::basic_istream &std::getline
while (getline(fileReader, currentLine))
{
//Logic for loading MyObject* here
}
}
Then use this method in both variants:
MyObject* LoadObject(wstring filePath)
{
ifstream fileReader;
fileReader.open(filePath);
if (fileReader.is_open())
return loadObjectFromStream( fileReader );
}
MyObject* LoadObject(const char *buffer, size_t size )
{
istrstream stream( buffer, size );
return loadObjectFromStream( stream );
}
you may want to make LoadObjectFromStream private etc, but that details of implementation.

Related

ifstream as class member [duplicate]

I have the following parser class that works in Visual C++
class Parser
{
private:
const char* filename;
std::ifstream filestream;
std::vector<std::string> tokens;
unsigned int linect;
public:
Parser(const char* filename);
bool readline();
std::string getstrtoken(unsigned int i) const { return tokens[i]; }
int getinttoken(unsigned int i) const { return atoi(tokens[i].c_str()); }
};
Parser::Parser(const char* filename) :
filename(filename),
linect(0)
{
filestream = ifstream(filename); // OK in VC++, not with GCC?
}
bool Parser::readline()
{
std::string line;
getline(filestream, line);
std::stringstream ss(line);
std::string token;
tokens.clear();
while(getline(ss, token, ' ')){ if(token != "") tokens.push_back(token); }
linect++;
return (filestream != NULL);
}
But when I try to compile it with GCC 4.8.2, I get errors saying that I cannot assign to filestream. From what I read elsewhere on this site, you can do
std::ifstream filestream(filename);
but you cannot do
std::ifstream filestream;
filestream = ifstream(filename);
which is essentially what I need to do if I want to declare filestream as a member of the Parser class and initialize it in the constructor.
I would like to have the file stream kept within the Parser class so that those who use the parser don't need to declare and keep track of it. It seems to me that this should be self-contained in the Parser class since its internal methods (e.g. readline()) are the only ones that use it.
Is there a way to achieve this that works with both platforms?
Thanks.
edit: My fix was to explicitly call the open() method of ifstream. My parser class constructor now looks like:
Parser::Parser(const char* filename) :
filename(filename),
linect(0)
{
filestream.open(filename);
// Do some checking to make sure the file exists, etc.
}
You can't, since std::ifstream has deleted copy constructor and copy assignment. You may get around by doing
filestream.swap(ifstream(filename)).
The fact that it compiles on visual studio is mostly because it gets inlined into either move assignment or move constructor(I'm not so good to tell you which exactly). If you try
std::ifstream myF;
filestream = myF;
it won't compile.
However you may try to do the move I wrote, or you can just call .open(http://en.cppreference.com/w/cpp/io/basic_ifstream/open)
I think a better solution would be for you to:
Construct a ifstream first.
Construct a Parser using the ifstream object.
Change Parser to store a reference to an istream object. This allows you the flexibility of being able parse the contents of a file, stdin, and a string.
class Parser
{
private:
std::istream& str;
std::vector<std::string> tokens;
unsigned int linect;
public:
Parser(std::istream& s) : str(s) ... {}
...
};
std::ifstream don't have a copy constructor, probably one of the many extensions of VC++. Correct code is:
Parser::Parser(const char* filename) :
filename(filename),
linect(0),
filestream(filename)
{
}
Please take note of member variable and parameter filename. Use this-> or change name (recommended, normally prefix is used for member variables _ or m_)

Vector iterator is not dereferencable, while try to read from file

I'm writing data (structure) into file using vector, and when I attempt to retrieve data using vector iterator and it gives me: "Vector iterator is not dereferenceable."
This is my code:
void CProgram_1_STLDlg::OnBnClickedBtnView()
{
// TODO: Add your control notification handler code here
CFile file;
CFileException e;
studentVector::iterator sit;
studentVector::iterator sBegin = sVector.begin();
studentVector::iterator sEnd = sVector.end();
CString path = _T("D:\\Student.txt");
if ( file.Open(path, CFile::modeRead, &e) ) {
while ( file.Read( (char *)&sVector, sizeof(sVector)) ) {
AfxMessageBox(_T("File opened in read mode."), MB_ICONINFORMATION);
AfxMessageBox(_T("ID:\t")+sit->id+L"\nName:\t"
+sit->name+L"\nMarks:\t"+sit->marks+L
"\nPercentage:\t"+sit->per+L"\nState:\t"+sit->state);
sit++;
}
//file.Read( (char *)&sData, sizeof(sData));
/*for ( sIterator = sVector.begin(); sIterator != sVector.end(); sIterator++ ) {
//AfxMessageBox(_T("ID:\t")+sIterator->id+L
"\nName:\t"+sIterator->name+L"\nMarks:\t"
+sIterator->marks+L"\nPercentage:\t"+sIterator->per+L
"\nState:\t"+sIterator->state);
//AfxMessageBox(_T("Hello..Testing...!"));
}
*/
} else {
AfxMessageBox(_T("Error! Unable to open file."), MB_ICONERROR);
}
}
Now I don't know how to resolve this error.
Note: Some of links I refer which google gave me, but I couldn't able to solve my problem.
You cannot simply overwrite the memory of a vector. That is pretty much guaranteed to corrupt your process.
Furthermore, you never assign anything to sit and yet expect it to contain something sensible.
You need to parse the data in Student.txt and use vector's member functions to fill it with sensible data. The assignment will probably tell you what the file looks like so that you can parse it.
A simple vector like
vector<char> cvec
could be overwritten
so something like
vector<char> cvec;
cvec.resize(100);
for(char i=0;i<100;i++)
cvec[i]=i;
will work.
If you resize to correct size. Otherwise you will corrupt memory
sizeof(sVector) will deliver the size of the vector class.
this is not related to the data since data inside the vector class is nothing more than a pointer.
example:
class simpleVector;
{
public:
simpleVector(unigned int size)
{
p=new int[size];
}
int* p;
}
func()
{
simpleVector v1(10);
simpleVector v2(100000);
printf("size v1= %d, Size v2= %d", sizeof(v1),sizeog(v2));
}
I have not checked, what sizeof will deliver for this class, but it definitely will be constant. Independent from the size that is given to constructor
An Iterator is an accessor to the Vector
but it needs to be initialized.
In the code above sit is not assigned to something. So you are not able to access something valid.
from the code line
AfxMessageBox(_T("ID:\t")+sit->id+L"\nName:\t"+sit->name+L"\nMarks:\t"+sit->marks+L"\nPercentage:\t"+sit->per+L"\nState:\t"+sit->state);
I see the vector shall contain a complex data type build from several strings.
so a vector element probably looks like
class student
{
std::string id;
std::string name;
std::string marks;
std::string per;
std::string state;
};
this is in minimum the information hold by each vector element.
usually strings have the property to have different length.
While id might be always of same length name probably don't.
Since it is not fixed length
even
file.Read( (char *)&sVector, sizeof(student))
would not work.
so I would suggest to add a reader to the 'Student' Class:
class student
{
std::string id;
std::string name;
std::string marks;
std::string per;
std::string state;
bool ReadElemFromFile(CFile& file)
{
id=ReadStringFromFile(file);
name=ReadStringFromFile(file);
marks=ReadStringFromFile(file);
per=ReadStringFromFile(file);
state=ReadStringFromFile(file);
if(id.empty()||name.empty()||marks.empty()||per.empty()||state.empty())
return false;
return true;
}
std::string ReadStringFromFile(CFile% file)
{
char c;
std::string s;
do
{
file.read(&c,1);
s+=c;
}
while(c!='\0')
return s;
}
};
I know reading that way is not the most performant way to do it, but it shows,that the string terminator stored to file indicates the length of each string
now back to your code
void CProgram_1_STLDlg::OnBnClickedBtnView()
{
// TODO: Add your control notification handler code here
CFile file;
CFileException e;
student* sit=new Student;
studentVector.clear();
CString path = _T("D:\\Student.txt");
if ( file.Open(path, CFile::modeRead, &e) ) {
while ( sit->ReadElemFromFile(CFile& file)) {
AfxMessageBox(_T("File opened in read mode."), MB_ICONINFORMATION);
AfxMessageBox(_T("ID:\t")+sit->id+L"\nName:\t"+sit->name+L"\nMarks:\t"+sit->marks+L"\nPercentage:\t"+sit->per+L"\nState:\t"+sit->state);
studentVector.push_back(*sit);
}
} else {
AfxMessageBox(_T("Error! Unable to open file."), MB_ICONERROR);
}
delete stud;
}
..."attempt to retrieve data using vector iterator and it gives me Vector iterator is not dereferenceable"...
Iterators are pointer-like objects, however unlike raw pointers, they prevent dereferencing (accessing of the value they point to) if they are "dangling".
In your case iterator sit is not initialized, not as, for example iterator sBegin = sVector.begin();, that is assigned to point to the beginning of the vector sVector.
Thus when you try to access an iterator that does not point to a valid value, you get an error.
In addition to that, to store an element to a vector you should use its member functions, not passing its address, as you do in your while loop.

use iostream or alternative for managing stream

I want to write a function which (simplified) takes as a parameter an input buffer of variable size, processes it (sequentially), and returns a buffer of a fixed size. The remaining part of the buffer has to stay in the "pipeline" for the next call of the function.
Question 1:
From my research it looks like iostream is the way to go, but apparently no one is using it. Is this the best way to go?
Question 2:
How can I declare the iostream object globally? Actually, as I have several streams I will need to write the iostream Object in a struct-vector. How do I do this?
At the moment my code looks like that:
struct membuf : std::streambuf
{
membuf(char* begin, char* end) {
this->setg(begin, begin, end);
}
};
void read_stream(char* bufferIn, char* BufferOut, int lengthBufferIn)
{
char* buffer = (char*) malloc(300); //How do I do this globally??
membuf sbuf(buffer, buffer + sizeof(buffer));//How do I do this globally??
std::iostream s(&sbuf); //How do I do this globally??
s.write(bufferIn, lengthBufferIn);
s.read(BufferOut, 100);
process(BufferOut);
}
I see no need for iostream here. You can create an object who has a reference to the buffer (so no copies involved) and to the position where it is left.
So something along this:
class Transformer {
private:
char const *input_buf_;
public:
Transformer(char const *buf) : input_buf_(buf) {
}
bool has_next() const { return input_buf_ != nullptr; } // or your own condition
std::array<char, 300> read_next() {
// read from input_buf_ as much as you need
// advance input_buf_ to the remaining part
// make sure to set input_buf_ accordingly after the last part
// e.g. input_buf_ = nullptr; for how I wrote hasNext
return /*the processed fixed size buffer*/;
}
}
usage:
char *str == //...;
Transformer t(str);
while (t.has_next()) {
std::array<char, 300> arr = t.read_next();
// use arr
}
Question 1: From my research it looks like iostream is the way to go, but apparently no one is using it. Is this the best way to go?
Yes (the std::istream class and specializations, are there to manage streams, and they fit the problem well).
Your code could look similar to this:
struct fixed_size_buffer
{
static const std::size_t size = 300;
std::vector<char> value;
fixed_size_buffer() : value(fixed_size_buffer::size, ' ') {}
};
std::istream& operator>>(std::istream& in, fixed_size_buffer& data)
{
std::noskipws(in); // read spaces as well as characters
std::copy_n(std::istream_iterator<char>{ in },
fixed_size_buffer::size);
std::begin(data.value)); // this leaves in in an invalid state
// if there is not enough data in the input
// stream;
return in;
}
Consuming the data:
fixed_size_buffer buffer;
std::ifstream fin{ "c:\\temp\\your_data.txt" };
while(fin >> buffer)
{
// do something with buffer here
}
while(std::cin >> buffer) // read from standard input
{
// do something with buffer here
}
std::istringstream sin{ "long-serialized-string-here" };
while(sin >> buffer) // read from standard input
{
// do something with buffer here
}
Question 2: How can I declare the iostream object globally? Actually, as I have several streams I will need to write the iostream Object in a struct-vector. How do I do this?
iostreams do not support copy-construction; Because of this, you will need to keep them in a sequence of pointers / references to base class:
auto fin = std::make_unique<std::ifstream>("path_to_input_file");
std::vector<std::istream*> streams;
streams.push_back(&std::cin);
streams.push_back(fin.get());
fixed_size_buffer buffer;
for(auto in_ptr: streams)
{
std::istream& in = &in_ptr;
while(in >> buffer)
{
// do something with buffer here
}
}

How to allocate and return a string without memory leak in C++?

Let's say I have 3 functions:
int A(void);
string B(const string& fileName);
void C(string& dataFromFile);
A() calls B() and uses B()'s return value as an input parameter to C(). In this case, I want to open the file in B() and read the data written in the file to a buffer. Then I want to return this data as a string. How can I implement B() to avoid memory leak? Because the pseudo-code below would cause memory leak.
string B(const string& fileName) {
// open file
char* buffer = new char[sizeOfFile];
// read from file and assogn the data to buffer
return buffer;
}
You can just use std::string which avoid any leaks.
std::string B(const std::string& fileName)
{
std::string buffer;
// open file
buffer.reserve(sizeOfFile);
// do stuff
return buffer;
}
EDIT : With the use of reserve which preallocates the needed memory and return value optimization this should not have huge performance lose compared to char*.
Just skip the middleman. Start with a string, read into the string, and return the string. I'm not sure which functions you are using to read your data, but whatever you can do with a char*, you can do with a std::string. For example, if you are using the read function of an ifstream, you can do this:
std::string buffer;
buffer.resize(sizeOfFile);
fin.read(&buffer[0], sizeOfFile);
return buffer;
Update the function to:
string B(const string& fileName) {
// open file
char* buffer = new char[sizeOfFile];
// read from file and assogn the data to buffer
string ret(buffer);
delete [] buffer;
return ret;
}
If you can use a string instead of a char*, it will be still better.
string B(const string& fileName) {
// open file
strin ret(sizeOfFile, `\0');
// read from file and assogn the data to ret
return ret;
}

How to have a file stream as a class member

I have the following parser class that works in Visual C++
class Parser
{
private:
const char* filename;
std::ifstream filestream;
std::vector<std::string> tokens;
unsigned int linect;
public:
Parser(const char* filename);
bool readline();
std::string getstrtoken(unsigned int i) const { return tokens[i]; }
int getinttoken(unsigned int i) const { return atoi(tokens[i].c_str()); }
};
Parser::Parser(const char* filename) :
filename(filename),
linect(0)
{
filestream = ifstream(filename); // OK in VC++, not with GCC?
}
bool Parser::readline()
{
std::string line;
getline(filestream, line);
std::stringstream ss(line);
std::string token;
tokens.clear();
while(getline(ss, token, ' ')){ if(token != "") tokens.push_back(token); }
linect++;
return (filestream != NULL);
}
But when I try to compile it with GCC 4.8.2, I get errors saying that I cannot assign to filestream. From what I read elsewhere on this site, you can do
std::ifstream filestream(filename);
but you cannot do
std::ifstream filestream;
filestream = ifstream(filename);
which is essentially what I need to do if I want to declare filestream as a member of the Parser class and initialize it in the constructor.
I would like to have the file stream kept within the Parser class so that those who use the parser don't need to declare and keep track of it. It seems to me that this should be self-contained in the Parser class since its internal methods (e.g. readline()) are the only ones that use it.
Is there a way to achieve this that works with both platforms?
Thanks.
edit: My fix was to explicitly call the open() method of ifstream. My parser class constructor now looks like:
Parser::Parser(const char* filename) :
filename(filename),
linect(0)
{
filestream.open(filename);
// Do some checking to make sure the file exists, etc.
}
You can't, since std::ifstream has deleted copy constructor and copy assignment. You may get around by doing
filestream.swap(ifstream(filename)).
The fact that it compiles on visual studio is mostly because it gets inlined into either move assignment or move constructor(I'm not so good to tell you which exactly). If you try
std::ifstream myF;
filestream = myF;
it won't compile.
However you may try to do the move I wrote, or you can just call .open(http://en.cppreference.com/w/cpp/io/basic_ifstream/open)
I think a better solution would be for you to:
Construct a ifstream first.
Construct a Parser using the ifstream object.
Change Parser to store a reference to an istream object. This allows you the flexibility of being able parse the contents of a file, stdin, and a string.
class Parser
{
private:
std::istream& str;
std::vector<std::string> tokens;
unsigned int linect;
public:
Parser(std::istream& s) : str(s) ... {}
...
};
std::ifstream don't have a copy constructor, probably one of the many extensions of VC++. Correct code is:
Parser::Parser(const char* filename) :
filename(filename),
linect(0),
filestream(filename)
{
}
Please take note of member variable and parameter filename. Use this-> or change name (recommended, normally prefix is used for member variables _ or m_)