I need to implement a mechanism where I can initialize a vector of my custom class using a text source, where each line of the source is representing one instance of my class. To achieve this, I implemented the operator >> for my class and stringstream. When I read the source, I go line-by-line and get a substream of my original source, then parse the substream each time. This has three benefits for me. First, this way I can make sure that one line of the text source would represent exactly one instance of my class. Second, as the rest of the line after parsing is ignored, I can safely add any comment in any line of my text source, which would surely get ignored by the parser. And third, I don't need to mention the length of the vector in my original source, since the first time I get a parsing error (I check the fail and bad bits of the stream to confirm this) I know that the vector declaration is over.
To parse line-by-line, I'm using the following code:
std::stringstream fullStream;
std::stringstream lineStream;
std::string str;
bool isValid;
myClass newInstance;
std::vector < myClass > result;
// Fill fullStream from external source (codepart omitted)
isValid = true;
while ( isValid && ! fullStream.eof ( ) ) {
std::getline ( fullStream, str );
lineStream.clear ( );
lineStream.str ( str );
lineStream >> newInstance;
isValid = ! lineStream.fail ( );
if ( isValid ) {
result.push_back ( newInstance );
}
}
Although this code works fine, I'm wondering if there was a better way to achieve the same result. Specially, if there was a more efficient way to extract a line from fullStream to lineStream.
Thanks,
Ádám
First, if the code works, it is really only by chance. The idiomatic
way of handling this is:
std::string line;
while ( std::getline( fullStream, line ) ) {
std::istringstream lineStream( line );
lineStream >> newInstance;
if ( lineStream ) {
result.push_back( newInstance );
} else {
fullStream.setstate( std::ios_base::failbit );
}
}
Checking eof() before a read is rarely useful, and not checking the
results of your getline before using it is almost certainly an error.
Trying to reuse a stringstream is more complex and error prone than
simply creating a new one; there is all sorts of state which may or may
not have to be reset. Streams have a mechanism for memorizing error
state, so you probably want to use this. (If you want to continue using
fullStream for other things after the error, the problem is more
complex, because you've already extracted the line which failed, and you
can't put it back.) And if you're only reading, you should use
std::istringstream, and not std::stringstream (which has a lot of
extra baggage); in general, it's very, very rare to use a bi-directional
stream.
One obvious alternative would be to have your operator>> do line-by-line reading itself, so you don't have to do that externally:
class MyClass {
// some sort of data to demonstrate the idea:
int x;
std::string y;
friend std::istream &operator>>(std::istream &is, MyClass &m) {
std::string temp;
std::getline(is, temp);
std::istringstream buffer(temp);
buffer >> m.x >> m.y;
return is;
}
};
With that, code to read data from a file becomes a little more straightforward:
std::copy(std::istream_iterator<MyClass>(fullStream),
std::istream_iterator<MyClass>(),
std::back_inserter(result));
Edit: if you don't want to build the line-oriented reading directly into the operator>> for MyClass, another possibility is to use a proxy class:
class LineReader {
MyClass object;
public:
operator MyClass() { return object; }
friend std::istream &operator>>(std::istream &is, LineReader &d) {
std::string line;
std::getline(is, line);
std::istringstream buffer(line);
buffer >> d; // delegate to the object's own stream-oriented reader.
}
};
Then when you want to do line-oriented reading, you read objects of the proxy class, but store objects of the original class:
std::vector<MyClass>((std::istream_iterator<LineReader>(some_stream)),
std::istream_iterator<LineReader>());
But, when/if you want to read a stream of objects instead of lines of objects, you use the object's own operator>> directly:
std::vector<MyClass>((std::istream_iterator<MyClass>(stream),
std::istream_iterator<MyClass>());
Related
I am a little stuck on this area. By using BookList.insert(book, Position::BOTTOM) I would get cquery saying this error on the BookList:
cannot use dot operator on a type
I can't think of another way to insert into booklist than this.
Could this be from the if statement? I intention it to do "if (stream works) {insert into booklist}".
Here is the code, and at the bottom there is a link to a picture of the code.
std::istream& operator>>(std::istream& stream, BookList& book_list) {
if (!book_list.containers_are_consistent()) {
throw BookList::InvalidInternalStateException(
"Container consistency error in operator>>");
}
std::string label_holder;
size_t count;
// Read in data from a stream and use it to fill in the data of a BookList
// object. Create a function that will read the output created by the
// ostream operator above into an object properly.
stream >> count;
for (int i = 0; i < count; i++) {
stream.ignore(100, ':');
}
if (stream) {
BookList.insert(book, Position::BOTTOM);
}
return stream;
}
I'm assuming that insert() is a member function of the BookList class. If this assumption is correct, when using the . notation, it must be called on an instance of BookList rather than on the class name itself. So, replace this:
BookList.insert(book, Position::BOTTOM);
with this
book_list.insert(book, Position::BOTTOM);
Consider I have the following csv file:
10.0.0.26
10.0.0.30
10.0.0.34
10.0.0.38
...
...
Consider I have a vector:
std::vector<L3Address> destAddresses;
I want a piece of code the will read the above csv file till the very last address and store each of that address in the above vector.
std::ifstream data("data.csv");
while(//till the end of file){
//read from csv
//write to vector
}
I have tried the following:
std::ifstream data("data.csv");
while(std::getline(data,std::string value){
destAddresses.push_back(value);
}
But this isn't working somehow.
Let's go look at the documentation of L3Address (note 1). Here we can see a function bool L3Address::tryParse(const char * str), which will try to interpret a C-string as an address, and tell you if it succeded.
std::ifstream data("data.csv");
std::string line;
while (std::getline(data, line) {
L3Address addr;
if (addr.tryParse(line.c_str())) {
destAddresses.push_back(addr);
}
else
{
// TODO: handle malformed address
}
}
Note 1: The tooling that generated this documentation is formatting it very badly, so it is excusable that you couldn't get here yourself.
Your example doesn't seem to compile. Besides the missing parenthesis the getline function you are using requires a variable reference as the second argument. Hence, you should fix your code by defining the value variable before use.
Additionally to construct the L3Address you can benefit from the constructor accepting const char*.
std::ifstream data("data.csv");
std::string value;
while(std::getline(data,value))
{
destAddresses.emplace_back(value.c_str());
}
trying to do a project for class, but keep getting the error: no instance of overloaded function matches argument list relating to the implementation of the rows vector. the area that is specifically highlighted is the . operator before push_back and insert.
void holdLines(ifstream in, vector<string> rows) {
string line;
string prevLine;
vector<string> rows;
int lineNumber = 0;
int vectorNumber = 0;
while(true) {
getline(in, line);
if(in.fail()) {
break;
}
lineNumber++;
vectorNumber = lineNumber - 1;
rows.push_back(lineNumber);
rows.insert(prevLine, line);
}
}
You are trying to pass an integer to push_back when a string is required.
It also looks like your local variable "rows" is named the same as your parameter "rows".
http://www.cplusplus.com/reference/vector/vector/push_back/
Your compiler is correct: there is no overload of std::vector<std::string>::push_back that accepts an int, because a std::vector<std::string> stores std::strings, not ints.
It's quite unclear from code alone what you are trying to do, due to the myriad mistakes, but start by replacing your push_back call with something sensible.
There is no method insert with two parameters of type std::string as you are trying to call
rows.insert(prevLine, line);
Also it is not clear what you are trying to do in this statement.
Edit: After you updated yor code nevertheless this statemenet
rows.push_back(lineNumber);
also is wrong because the rows is declared as a vecto of strings. It is not a vector of int and moreover class std::string does not have an appropriate constructor.
But in any case the function does not make sense because you declared a local variable with the same name as the second parameter and tried to fill this local vector that will be deleted after exiting the function
void holdLines(ifstream in, vector<string> rows) {
^^^^^^^^^^^^^^^^^^
string line;
string prevLine;
vector<string> rows;
^^^^^^^^^^^^^^^^^^^^
//..
I think the function should be declared either like
void holdLines(ifstream in, vector<string> &rows);
^^^
or like
vector<string> holdLines(ifstream in);
Take into account that instead of this statements
while(true) {
getline(in, line);
if(in.fail()) {
break;
}
//...
you could write
while ( getline( in, line ) )
{
//...
If you need simply to fill the vector that is passed as the argument then the function can look the following way
void holdLines( std::ifstream &in, std::vector<std::string> &rows )
{
std::string line;
while ( std::getline( in, line ) ) rows.push_back( line );
}
I'm trying to parse a file which is in Key<whitespace>Value format. I'm reading the file lines in an std::istringstream object, and I'm extracting a Key string from it. I want to avoid accidentally changing the value of this Key string by making it const.
My best attempt was initializing a temporary VariableKey object, and then making a constant one out of it.
std::ifstream FileStream(FileLocation);
std::string FileLine;
while (std::getline(FileStream, FileLine))
{
std::istringstream iss(FileLine);
std::string VariableKey;
iss >> VariableKey;
const std::string Key(std::move(VariableKey));
// ...
// A very long and complex parsing algorithm
// which uses `Key` in a lot of places.
// ...
}
How do I directly initialize a constant Key string object?
It's arguably better to separate file I/O from processing, and instead of creating a const Key inside the same function - call a line-processing function that takes a const std::string& key parameter.
That said, if you want to continue with your current model, you can simply use:
const std::string& Key = VariableKey;
There's no need to copy or move anything anywhere. Only const std::string members functions will be accessible via Key.
You can avoid the "scratch" variable by extracting the input into a function:
std::string get_string(std::istream& is)
{
std::string s;
is >> s;
return s;
}
// ...
while (std::getline(FileStream, FileLine))
{
std::istringstream iss(FileLine);
const std::string& Key = get_string(iss);
// ...
(Binding the function's result to a const reference extends its lifetime.)
I recently found the need to parse a std::string to a boost::posix_time::ptime. The question of how to accomplish this task is answered well here: How to parse date/time from string?.
I know it only requires a few lines of code (basically the istringstream declaration, locale/time_input_facet imbuance, and extraction to ptime), but I wanted to wrap that functionality in a convenience function and make it available as a C++ library for my various programs that work with ptimes.
If possible, I'd like to get some feedback from C++ gurus on my design, just so I can know if I've done anything obviously wrong or if there are better approaches out there. Particularly, I'm not very familiar with how locales and time_input_facets work, so I hope I don't have a memory leak or anything.
Here it is:
namespace mydt {
void imbueDTFormat(std::istringstream& iss, const std::string& format );
void parse(const std::string& input, const std::string& format, boost::posix_time::ptime& out ); // (1)
void parse(const std::string& input, std::istringstream& iss, boost::posix_time::ptime& out ); // (2)
void parse(std::istringstream& iss, boost::posix_time::ptime& out ); // (3)
void imbueDTFormat(std::istringstream& iss, const std::string& format ) {
iss.imbue(std::locale(std::locale::classic(), new boost::posix_time::time_input_facet(format) )); // see <http://en.cppreference.com/w/cpp/locale/locale/locale>: "Overload 7 is typically called with its second argument, f, obtained directly from a new-expression: the locale is responsible for calling the matching delete from its own destructor."
} // end imbueDTFormat()
void parse(const std::string& input, const std::string& format, boost::posix_time::ptime& out ) { // (1)
static std::istringstream iss;
imbueDTFormat(iss, format );
parse(input, iss, out );
} // end parse()
void parse(const std::string& input, std::istringstream& iss, boost::posix_time::ptime& out ) { // (2)
// assumes iss has already been imbued with the desired time_input_facet
iss.str(input);
parse(iss, out );
} // end parse()
void parse(std::istringstream& iss, boost::posix_time::ptime& out ) { // (3)
// assumes iss has already been imbued with the desired time_input_facet AND has been initialized with the input str
iss >> out;
} // end parse()
} // end namespace mydt
The first function I wrote was parse (1). It provides the easiest interface; just pass the input string, format string, and the ptime OUT var, and the parsing is done. It uses a static istringstream to complete the parsing, so no allocations are necessary for that. But when I looked at the function after I wrote it, I realized that if you have a single format that you repeatedly want to parse from, then it's wasteful to repeatedly allocate a new time_input_facet from it and imbue the same istringstream with it.
So I figured that you could do better by having the calling code create its own (perhaps static) istringstream, imbue the format once, and then repeatedly use that istringstream to parse. Hence I wrote parse (2) for that purpose. So calling code could have one dedicated function for each format, like so:
void parseServerDT(const std::string& input, boost::posix_time::ptime& out );
void parseHostDT(const std::string& input, boost::posix_time::ptime& out );
// in main, or some other code context
boost::posix_time::ptime serverDT; parseServerDT(getServerDTStr(), serverDT );
boost::posix_time::ptime hostDT; parseHostDT(getHostDTStr(), hostDT );
void parseServerDT(const std::string& input, boost::posix_time::ptime& out ) {
static bool first = true;
static std::istringstream iss;
if (first) {
first = false;
mydt::imbueDTFormat(iss, SERVERDT_FORMAT );
} // end if
mydt::parse(input, iss, out );
} // end parseServerDT()
void parseHostDT(const std::string& input, boost::posix_time::ptime& out ) {
static bool first = true;
static std::istringstream iss;
if (first) {
first = false;
mydt::imbueDTFormat(iss, HOSTDT_FORMAT );
} // end if
mydt::parse(input, iss, out );
} // end parseHostDT()
I think this design should provide maximum convenience for calling code, and should minimize the memory and performance impact. You could define as many parseXDT() functions as you like (and even create a macro to reduce the duplicate code in those functions.)
Any feedback would be much appreciated. Thanks!
At the very least
make the statics threadlocal. With lacking compilers this involves dynamically allocating them (not an issue since it's a one time cost)
most importantly, clear the stream error state and contents before re-use
void parseHostDT(const std::string& input, boost::posix_time::ptime& out ) {
thread_local std::istringstream* iss = [] {
first = false;
mydt::imbueDTFormat(iss, HOSTDT_FORMAT);
}();
iss->clear();
iss->str("");
mydt::parse(input, iss, out);
}