I need to parse a string containing a comma separated list of objects, like a="1,2,3,4,5".
I also want the object type to be interchangeable, it is defined by typedef type val_type.
My approach is
vector<val_type> v();
istringstream iss(a); string x; val_type y;
while (getline(iss, x, ',') && istringstream(x) >> y) v.push_back(y);
Which works, but apparently runs much slower (about 5x) compared to using atoi() assuming the object type is an integer.
while (getline(iss, x, ',')) v.push_back(atoi(x.c_str()));
Is that huge difference in performance to be expected? Any clever way to get around this issue?
Related
4;
Spadina;76 156
Bathurst;121 291
Keele;70 61
Bay;158 158
This is what file contains in it. I need to read them and save them into variables.
4 is for dynamic memory allocation. 4 means there are 4 stations.
Spadina, Bathrust, etc.. they are station names. first number, which comes right after station names, is number of student passes and the second number is number of adult pass.
So, basically I have 4 variables and they are;
int numberOfStation;
int studentPass;
int adultPass;
string stationName;
I spent 4 hours but still cannot read the file and save it into variable
Thank you.
A possible solution is to read every line with e.g. std::getline then parse each such line string. You'll use the appropriate methods of std::string to search inside it (with find) and split it (with substr). You might also access some individual character in that string using at or the [] operator of std::string; alternatively, you might perhaps parse each line -or relevant parts of them- using std::istringstream (I am not sure it is appropriate in your case). You might be interested by std::to_string...
An important thing is to define exactly (not only thru examples) the possible acceptable inputs. You could for example use some EBNF to formalize that. You should probably care about character encoding (try first by assuming a simple single-byte encoding like ASCII, then later consider UTF-8 if your system uses it).
For example, can the station names (I guess you talk about subway stations) contain digits, or spaces, or underscores, or commas, etc.... ? Could they be French names like Hôtel de Ville (a metro station in central Paris) or Saint-Paul or Bourg-La-Reine (where I am), or Russian names like Молодёжная in Moscow? (I guess that station names in Tokyo or in Jerusalem might be even funnier to parse).
BTW, explicitly entering the number of entries (like your initial 4) is very user-unfriendly. You could have some lexical conventions and e.g. use some tagging or separators.
At last, you might want to keep the information for every travel. Then you'll probably need to define some struct or class (not simply four scalar variables). At that point your program is becoming more interesting!
First group your variables in struct.
struct MyStruct
{
int studentPass;
int adultPass;
string stationName;
};
Now read size of struct in file and allocate it dynamically
MyStruct *p;
s >> N;
p = new MyStruct[N];
Now in for loop you read string with delimiter ';' and other two vars are ints
for (int i = 0; i < N; i++)
{
getline(s, p[i].stationName, ';');
s >> p[i].studentPass >> p[i].adultPass;
}
Where var s is istream type of variable with flag std::in
I recommend that you create a struct to hold your stations:
struct station{
string _stationName;
int _studentPass;
int _adultPass;
};
Then create an operator to use with your struct (props to Sly_TheKing for the getline idea):
std::istream& operator>>(std::istream& is, station& rhs){
getline(is, rhs._stationName, ';');
is >> rhs._studentPass >> rhs._adultPass >> ws;
return is;
}
Say that your ifstream is called foo. You can read these into a vector like this:
foo.ignore(numeric_limits<streamsize>::max(), '\n');
vector<station> bar{istream_iterator<station>(foo), istream_iterator<station>()};
My problem is, I want to convert a string into a long int. For that, i use an istringstream that way:
long x;
string lString;
istringstream istr;
getLine(cin, lString);
istr.str(lString);
if(!(istr>>x)) return false; //Edited after answer below
(the conversion and the cin are actually in two different methods, I just put the related code together).
The following code returns false if I type "1", but not if I type "1.0". I could search for . in the string and add it if ther isn't, but isn't there a method to convert string to long ?
It's because of the operator precedence. The ! operator has higher precedence than the >> operator, so for the compiler what you have written is
if ((!istr) >> x)
You need to add your own parentheses:
if (!(istr >> x))
When using stdio.h, I can easily read certain kinds of formatted input like this:
FILE* fin = fopen(...);
fscanf(fin, "x = %d, y = %d", &x, &y);
The great thing about this is that I don't really have to worry about how many spaces there are between the character 'x' and the following '=', and other minor details.
In C++ it appears to me as though,
ifstream fin(...);
string s;
fin >> s;
may result in s being "x" or "x=", or even "x=12" depending on the spacing of the input.
Is there a convenient way to get behavior similar to scanf/fscanf using iostream/fstream?
This is actually surprisingly easy, given a prerequisite. I have these three functions that I stick in a header somewhere. These allow you to stream in character literals, and string literals. I've never quite understood why these aren't standard.
#include <iostream>
//These are handy bits that go in a header somewhere
template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e(&sliteral)[N]) {
e buffer[N-1] = {}; //get buffer
in >> buffer[0]; //skips whitespace
if (N>2)
in.read(buffer+1, N-2); //read the rest
if (strncmp(buffer, sliteral, N-1)) //if it failed
in.setstate(std::ios::failbit); //set the state
return in;
}
template<class e, class t>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e& cliteral) {
e buffer(0); //get buffer
in >> buffer; //read data
if (buffer != cliteral) //if it failed
in.setstate(std::ios::failbit); //set the state
return in;
}
//redirect mutable char arrays to their normal function
template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, e(&carray)[N]) {
return std::operator>>(in, carray);
}
Given those, the rest is easy:
in>>'x'>>'='>>data.first>>','>>'y'>>'='>>data.second;
Proof here
For more complex situations, you probably want to use std::regex or boost::regex, or maybe a real lexer/parser.
like you specify format in fscanf/scanf using %xx you can specify format using stream manipulators as detailed in this tutorial -
http://www.tenouk.com/Module18.html
very comprehensive. stream manipulator is near the bottom of the page.
The short answer is "no".
A slightly longer answer is "You can probably build something that does that". For example, you could read the line of text, and then use a suitable "replace spaces with empty string" type function. Or perhaps something like this:
int x, y;
string s;
getline(cin, s, '=');
cin.get(); // Get rid of =
cin >> x;
getline(cin, s, '=');
cin >> y;
Alternatively, using cin.ignore to skip things (since the string reading is not really useful uness you want to know that 'x' and 'y' are actually 'x' and 'y'=:
int x, y;
cin.ignore(1000000, '='); // Skip up to a '='
cin >> x;
cin.ignore(1000000, '='); // Skip up to a '='
cin >> y;
This will "break" if someone enteres over 100k characters without an = sign, and there is need for error checking to see that "garbage" isn't coming in - just like fscanf does. if (cin >> x) would take care of the "detect that something went wrong, but you need to then do something sensible with the fact that it's gone wrong, which I'm not sure of right now...
Of course, since C++ supports (nearly) all of C, you can of course always use whatever members of the <cstdio> functions that you would like to use, as well. [And in at least some cases, they are actually a bit better].
I'm trying to read data values from a text file and store them in a data structure. I could achieve something like this fairly simply in Java but I am unsure how to best approach the problem.
A line of data is x, y, sigma like 1.1 1.2 1.3. I know how to get the entire line into a string in c++, using getline(myFile, string) but I'm not sure if this is the correct approach or if it is, where to go from there. Is it possible to split the line then parse the split strings as double values?
My data structure looks like this:
struct datapoint {
double x;
double y;
double sigma;
double weight;
double xSquared;
double xy;
};
My questions are these:
Is there a way to parse numbers into structures, where each line of the .txt file is a struct in an array of datapoint?
If there is no direct method, are there equivalents in c++ of Java's split() and Double.parseDouble(string) methods?
Thanks.
Just reading them in with
std::cin >> x >> y >> sigma >> weight >> xSquared >> xy;
might be the easiest solution for your problem...
However, if you really need to do the splitting, have a look here: http://www.cplusplus.com/faq/sequences/strings/split/
I have a problem in c++, and I hope that some expert here could help me.
the problem is, there is a .txt file, that has some information. I want to write a program that uses this information.
the .txt file will be like this:
variable 1: 711
variable 2: [8 5 6 11]
variable 3: xyz zyx yyy
the program should read the values from the file and then use them, any idea how to do it ?!!
/* UPDATE */
what should be done is to read from the text file the numerical values, only the numerical values of the variables, and then use these values in the real program.
for example the value of variable 1 is read and put to a variable x, then in the program we may define
int y=7*x ;
I know how to read a file, but what I don't know is how to read specific values of the variables.
Basically the program should read the file, recognize the name of the variable, get the value, and then use it.
many thanks
Looks like you want to have some configuration-like file to store some information for your program to use. There are many implementations of config-files management (read, retrieve, store, save). I stick to small and convenient libconfig : http://www.hyperrealm.com/libconfig/
It supports integer, floating-point and boolean variables, and arrays, lists and enclosed sections also.
I think it's implicit in your question that the variables could have different types, such as numbers, arrays or strings. In that case, you must decide whether to use say boost::any<> or boost::variant<>, or create your own variable type ala:
struct Var
{
enum { Double, String, Array } Type;
Type type_;
double double_;
std::string string_;
std::vector<double> array_;
Var(Type t) : type_(t), double_(0) { } // empty double/string/array
Var(double d) : type_(Double), double_(d) { }
Var(const std::string& s) : type_(String), string_(s) { }
...
};
Alternatively, you could use C++'s inbuilt polymorphism and have a base class and a derived class for each real-world data type. It might even be enough for you to always store the textual representation, and store everything in a std::string. However you do it, let's say you store values in a type named "Type".
It seems the name of the variables is to be specified on the left of the colon. If you know corresponding variables will be hard-coded in your program, then you can then set those pre-existing variables to the values on the right. Otherwise, you need to create variables associated with those identifier names dynamically: this can be done with a std::map<std::string, Type> keyed on identifier. You could populate it as in:
std::map<std::string, Type> variables;
variables["age"] = Type(28);
variables["name"] = Type("Fred");
For parsing the actual text file, you can use iostreams. Read a line at a time ala:
std::string line;
while (getline(cin, line))
{
std::string::pos n = line.find(':');
if (pos != std::string::npos)
{
std::string identifier = line.substr(0, n - 1);
Type var;
{
// try parsing it as a number...
std::istringstream iss = line.substr(n + 1);
char c;
double d;
if (iss >> d && !(iss >> c))
var = d;
}
{
// try parsing it as an array...
std::istringstream iss = line.substr(n + 1);
var.clear(Array);
double d;
if (iss >> c && c == '[')
while (iss >> d)
result.array_.push_back(d);
if (iss && iss >> c && c == ']' && !(is >> c))
// parsed successfully as an array - use "result"
}
{
// it's a string...
var = line.substr(n + 1);
}
You should try to make a start on this and ask for specific help where you get stuck.
Note: you may find it easier to get a working (albeit slow) program meeting this requirement in a language such as Ruby, python or perl.