I have a c_str which contains [51,53]. I want to split these pairs in two integers. They are in a c_str because I read them from an input file.
There must be an easy way to parse them. I was thinking of using the .at function:. But I am sure I made it way to complicated. Plus, it does not work, since it outputs:
pair: 0x7ffffcfc2998
pair: 0x7ffffcfc2998
etc
string pairstring = buffertm.c_str();
stringstream pair1, pair2;
int pairint1, pairint2;
pair1 << pairstring.at(1) << pairstring.at(2);
cout << "pair: " << pair1;
pair1 >> pairint1;
pair2 << pairstring.at(4) << pairstring.at(5);
//cout << "pair: " << pair2;
pair2 >> pairint2;
Any better ways to do this?
Something like this:
char c1, c2, c3;
int first, second;
std::istringstream iss(str);
if (iss >> c1 >> first >> c2 >> second >> c3
&& c1 == '[' && c2 == ',' && c3 == ']' )
{
// success
}
You might want to throw in an additional check to see if there are more characters after the closing bracket:
if ((iss >> std::ws).peek() != EOF) {
//^^^^^^^^^^^^^^
// eats whitespace chars and returns reference to iss
/* there are redundant charactes */
}
Try this using C++11 std::stoi:
char c_str[] = "[51,53]";
std::string s(c_str);
int num1 = std::stoi(s.substr(1, 2));
int num2 = std::stoi(s.substr(4, 2));
If you know the numbers will be outside the range 10-99 then use this instead:
char c_str[] = "[5156789,5]";
std::string s(c_str);
s.assign(s.substr(1, s.size() - 2)); // Trim away '[' and ']'
std::string::size_type middle = s.find(','); // Find position of ','
int num1 = std::stoi(s.substr(0, middle));
int num2 = std::stoi(s.substr(middle + 1, s.size() - (middle + 1)));
The function stoi will throw std::invalid_argument if number can't be parsed.
Edit:
For a more rubust solution that will only parse base-10 numbers, you should use something like this:
char c_str[] = "[51,0324]";
int num1, num2;
try {
std::string s(c_str);
s.assign(s.substr(1, s.size() - 2));
std::string::size_type middle = s.find(',');
std::unique_ptr<std::size_t> pos{new std::size_t};
std::string numText1 = s.substr(0, middle);
num1 = std::stoi(numText1, pos.get()); // Try parsing first number.
if (*pos < numText1.size()) {
throw std::invalid_argument{{numText1.at(*pos)}};
}
std::string numText2 = s.substr(middle + 1, s.size() - (middle + 1));
num2 = std::stoi(numText2, pos.get()); // Try parsing second number.
if (*pos < numText2.size()) {
throw std::invalid_argument{{numText2.at(*pos)}};
}
} catch (const std::invalid_argument& e) {
std::cerr << "Could not parse number" << std::endl;
std::exit(EXIT_FAILURE);
}
It will throw std::invalid_argument when trying to parse strings as "1337h4x0r" and such, unlike when using std::istringstream. Se this for more info
For a number of reasons, I'd recommend a two-step process rather than trying to do it all at once.
Also, some of this depends heavily on what kinds of assumptions you can safely make.
1) Just tokenize. If you know that it will start with a "[" and you know there's going to be a comma, and you know you'll only get two numbers, search the string for a "[," then use substr() to get everything between it and the comma. That's one token. Then do something similar from the comma to the "]" to get the second token.
2) Once you have two strings, it's relatively trivial to convert them to integers, and there are a number of ways to do it.
A few answers have been added while I've been typing, but I still recommend a two-step approach.
If you're reasonably confident about the format of the string, you could put the whole thing into a stringstream, and then use the extraction operator; e.g.:
stringstream strm(pairstring);
int pairint1 = 0, pairint2 = 0;
char c = 0;
strm >> c >> pairint1 >> c >> pairint2 >> c;
In reality you'd want some error checking in there too, but hopefully that's enough of an idea to get you started.
Try to split your string using strtok function.
For convertation use atoi.
I have a c_str which contains [51,53]
No, c_str() is not a data structure, it's a method of std::basic_string that's returning a constant C string with data equivalent to those stored in the std::string.
string pairstring = buffertm.c_str(); is cumbersome, just do string pairstring = buffertm; or use directly buffertm.
Secondly, you're using your stringstream in the wrong way, you should use an istringstream (here you are using it as an ostringstream :
int i, j;
istringstream iss(buffertm);
iss.get()
iss >> i;
iss.get()
iss >> j;
iss.get().
Related
I am writing a program that reads data from a file.
the .txt file looks like this:
Text, numbers, : 5,3,5
Text, numbers, : 1,3,7,8
I was successful in extracting the "Text" and "Numbers", However when I came across the numbers after the : "5,3,5", I was really stuck.
I need to change these numbers to ints and add them to a vector of int, so I got rid of the commas, then tried using stoi to convert them to ints, however, stoi was only "extracting" the first number, in this case, only 5 and 1, this is what I've tried:
while(getline(file, line)){
stringstream ss(line);
getline(ss, text, ',');
getline (ss, nums, ':');
getline (ss, numList, ',' );
replace(numList.begin(), numList.end(), ',' , ' ');
vec.push_back(stoi(numList));
randomStruct str = {text, nums, numList};
randomStructVec.push_back(str);
}
I need the output to look like this when printing the elements of the vector:
5 3 5
1 3 7 8
what I'm getting is :
5
1
and other times I get duplicate numbers as well:
5
1111
or
5555
11
I need a way to make the stoi function convert all the numbers on that one line of string to ints, and store them in a vec of ints.
Any help, would be greatly appreciated.
Check out my solution at How do I tokenize a string that uses the String Toolkit Library
Here is a paired down version for your case:
#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp> //String Toolkit Library
const char *whitespace = " \t\r\n\f";
const char *whitespace_and_punctuation = " \t\r\n\f;,=";
int main()
{
// parsing a string into a vector of integers with separators
// of spaces and punctuation
std::string s("3; 4, 4, 8,-1");
std::vector<int> values;
if( strtk::parse( s, whitespace_and_punctuation, values ) )
{
for(size_t i = 0; i < values.size(); ++i )
std::cout << values[i] << std::endl;
}
return 0;
}
You will notice the conversion of the values into a vector of integers.
The library is header only. The library is VERY fast and can handle most anything you need to do with a string and parsing.
There is too main problem in your code.
First of all getline (ss, numList, ',' ); will stop on the first value of the list. In fact, when your list is 5,3,5, getline (ss, numList, ','); will read 5 then , so it will stop. At this point, numValue == "5"
This is quite simple to fix : Just remove the delimiter char, so getline(ss, numList);. Using this, numValue == "5,3,5"
Alright, now you have all your value. You replace ',' by ' ' in order to separate your numbers. Good, numList = "5 3 5".
And then is your second error : vec.push_back(stoi(numList));. stoi(numList) return an int and is not able to get through space characters. So it will only convert the first 5 and return it. You will never get the other numbers, as you don't even do a loop.
Here is my solution : convert your string to a stringstream and use >> operator
std::stringstream numStream(numList);
int value;
while(numList >> value)
vec.push_back(value);
So we end up with your final code (I removed stds, as it seems that you wrote using namespace std somewhere in your code)
struct randomStruct
{
string txt,
string nb,
vector<int> data
}
// -------
while(getline(file, line)){
stringstream ss(line);
getline(ss, text, ',');
getline (ss, nums, ':');
getline (ss, numList);
replace(numList.begin(), numList.end(), ',' , ' ');
stringstream numStream(numList);
int value;
while(numStream >> value)
vec.push_back(value);
randomStruct str = {text, nums, vec};
randomStructVec.push_back(str);
vec.clear();
}
// Accessing and printing data
for (auto str : randomStructVec)
{
for (auto i : str.data)
{
cout << i << " ";
}
cout << endl;
}
Use this function stoi_() in this function, I am converting a string into a number if that string contains the characters in range 0-9 otherwise create a new string and repeat this process until you reach the end of string. To handle negative numbers you have to add one more condition.
vector<int> stoi_(string s){
vector<int>ans;
int i = 0;
int n =s.length();
while(i<n){
string temp = ""; // current number
if(s[i]=='-' && (i+1<n && (s[i+1]>='0' && s[i+1]<='9'))){ // handle -ve numbers
temp = temp + s[i];
i++;
}
while(i<n && s[i]>='0' && s[i]<='9'){ // if current character is number append it into cur number
temp = temp + s[i];
i++;
}
if(temp.length()>0){
ans.push_back(stoi(temp)); // here using stoi() for simplicity
}
else{
i++;
}
}
return ans;
}
It's not giving me any output and I don't know why.
I have tried switching for a while loop.
cin >> input;
for (z=0; z > input.size(); z++) {
input[z]=(int)input[z];
cout << input; }
Expected result:
Input = abc
output = 979899
No Error message.
With the subscript operator [] you can only access one element from the string and you need to write more than one digit to the string ('A' -> "97"). To do that you need to convert the char value to a literal with std::to_string().
The simples solution is to use a second string as output, then you don't get in trouble with the indexing of the input string when you need to resize the string.
std::string str = "abc";
std::string out;
for(auto a : str )
{
out.append(std::to_string((unsigned int)a));
}
std::cout << out << std::endl;
Beginner programmer here,
Say I want to obtain initial starting coordinates in the form (x,y), so I ask the user to enter in a point using the specific form "(x,y)". Is there a way that I could recognize the format and parse the string so that I could obtain the x and y values?
Read a line of text using:
char line[200]; // Make it large enough.
int x;
int y;
fgets(line, sizeof(line), stdin);
Then, use sscanf to read the numbers from the line of text.
if ( sscanf(line, "(%d,%d)", &x, &y) != 2 )
{
// Deal with error.
}
else
{
// Got the numbers.
// Use them.
}
If you want to use iostreams instead of stdio, use getline instead of fgets.
You can use regex to find the matching sequence anywhere in the input.
#include <iostream>
#include <string>
#include <regex>
int main() {
std::string line;
std::getline(std::cin, line);
std::regex e(R"R(\(([-+]?(?:\d*[.])?\d+)\s*,\s*([-+]?(?:\d*[.])?\d+)\))R");
std::smatch sm;
if (std::regex_search(line, sm, e)) {
auto x = std::stod(sm[1]);
auto y = std::stod(sm[2]);
std::cout << "Numbers are: " << x << ", " << y << std::endl;
}
return 0;
}
In order to parse stuff you need a parser. There are many ways to write a parser, but generally parsers read tokens and decide what to do next based on which token it is. (Emphasized words are important, look them up).
In your case you don't have to explicitly introduce a separate token entity. Reading elements from the input stream with the >> operator will do.
You need to:
read a character; verify it's a '('
read a number
read a character; verify it's a ','
read a number
read a character; verify it's a ')'
If any step fails, the entire parsing fails.
You can see the same basic step is done three times, so you can write a function for it.
bool expect_char(std::istream& is, char what)
{
char ch;
return is >> ch && ch == what;
}
This works because is >> ch returns the stream after the read operation, and the stream can be viewed as a boolean value: true if the last operation succeeded, false otherwise.
Now you can compose your parser:
bool get_vector (std::istream& is, int& x, int& y)
{
return expect_char(is, '(') &&
is >> x &&
expect_char(is, ',') &&
is >> y &&
expect_char(is, ')');
}
This method has a nice property that blanks are allowed between the numbers and the symbols.
Now this may look like a lot of stuff to type compared to the solution that uses sscanf:
bool get_numbers2 (std::istream& is, int& x, int& y)
{
std::string s;
return std::getline(in, s) &&
(std::sscanf(s.c_str(), "(%d,%d)", &x, &y) == 2);
}
But sscanf is:
dangerous (there is no typechecking of its arguments)
less powerful (the input format is very rigid)
not generic (doesn't work in a template)
It's OK to use the scanf functions family where appropriate, but I don't recommend it for new C++ programmers.
Hi I'd like to ask how to parse multiple floats, separated by "/" and spaces, from a string.
The text format from the file is "f 1/1/1 2/2/2 3/3/3 4/4/4"
I need to parse every integer from this line of text into several int variables, which are then used to construct a "face" object(see below).
int a(0),b(0),c(0),d(0),e(0);
int t[4]={0,0,0,0};
//parsing code goes here
faces.push_back(new face(b,a,c,d,e,t[0],t[1],t[2],t[3],currentMaterial));
I could do it with sscanf(), but I've been warn away from that by my uni lecturer, so I am looking for an alternative. I am also not allowed other 3rd party libraries, including boost.
Regular expressions and parsing with stringstream() have been mentioned, but I don't really know much about either, and would appreciate some advice.
If you're reading the file with std::ifstream, there's no need for std::istringstream in the first place (although using the two is very similar because they inherit from the same base class). Here's how to do it with std::ifstream:
ifstream ifs("Your file.txt");
vector<int> numbers;
while (ifs)
{
while (ifs.peek() == ' ' || ifs.peek() == '/')
ifs.get();
int number;
if (ifs >> number)
numbers.push_back(number);
}
Taking into account your example f 1/1/1 2/2/2 3/3/3 4/4/4 what you need to read is: char int char int char int int char int char int int char int char int
To do this:
istringstream is(str);
char f, c;
int d[12];
bool success = (is >> f) && (f == 'f')
&& (is >> d[0]) && (is >> c) && (c == '/')
&& (is >> d[1]) && (is >> c) && (c == '/') &&
..... && (is >> d[11]);
The way I would do this is to change the interpretation of space to include the other separators. If I were to get fancy I would use different std::ostream objects, each with a std::ctype<char> facet set up to deal with one separator, and use a shared std::streambuf.
If you want to make the use of separators explicit you could instead use a suitable manipulator to skip the separator or, if it absent, indicate failure:
template <char Sep>
std::istream& sep(std::istream& in) {
if ((in >> std::ws).peek() != std::to_int_type(Sep)) {
in.setstate(std::ios_base::failbit);
}
else {
in.ignore();
}
return in;
}
std::istream& (* const slash)(std::istream&) = Sep<'/'>;
The code isn't tested and type on a mobile device, i.e., probably contains small errors. You'd read data like this:
if (in >> v1 >> v2 >> slash >> v3 /*...*/) {
deal_with_input(v1, v2, v3);
}
Note: the above use assumes input as
1.0 2.0/3.0
i.e. a space after the first value and a slash after the second value.
You can use boost::split.
Sample example is:
string line("test\ttest2\ttest3");
vector<string> strs;
boost::split(strs,line,boost::is_any_of("\t"));
cout << "* size of the vector: " << strs.size() << endl;
for (size_t i = 0; i < strs.size(); i++)
cout << strs[i] << endl;
more information here:
http://www.boost.org/doc/libs/1_51_0/doc/html/string_algo.html
and also related:
Splitting the string using boost::algorithm::split
I am trying to read this .csv file and here is an example of the data:
1.33286E+12 0 -20.790001 -4.49 -0.762739 -3.364226 -8.962189
1.33286E+12 0 -21.059999 -4.46 -0.721878 -3.255263 -8.989429
The problem is with the first column row 1 and 2. In the excel file it says the numbers in the cells are displayed as 1.33286E+12 and when you click on the cell it says they are 1332856031313 and 1332856031328 but the program is reading them as 1.33286E+12 but I need the whole number 1332856031313 and 1332856031328.
The code:
inputfile.open(word1.c_str());
while (getline (inputfile, line)) //while line reads good
{
istringstream linestream(line); //allows manipulation of string
string str;
while (getline (linestream, item, ',')) //extract character and store in item until ','
{
char * cstr, *p;
cstr = new char [item.size()+1];
strcpy(cstr, item.c_str());
p = strtok(cstr, " ");
while (p!=NULL) //while not at the end loop
{ // double e = atof(p); // coverts p to double
value++;
if( value == 1)
{ double e = atof(p); // coverts p to double
if(m ==1)
cout << time[0]<<"\n";
ostringstream str1;
str1 << e;
str = str1.str();
string str2;
str2.append(str.begin(), str.end());
const char * convert = str2.c_str();
e = atof(convert);
time[m] = e*0.001;
m++;
//if(m >=192542)
//cout << time[m-1]<<"\n";
}
p = strtok(NULL, " ");
}
delete[] cstr; //delete cstr to free up space.
}
count ++;
value = 0;
}
inputfile.close();
If the number 1332856031313 is being serialised as 1.33286E+12, there is no way to get it back in the deserialisation process. Information in the form of those 6 extra significant digits is gone forever. You need to make sure that when the CSV file is generated, it is saved at full precision. I don't know how you might do this with Excel.
Also, your use of atof and const char* isn't very C++-esque. Consider using code like
double a, b, c, d;
linestream >> a >> b >> c >> d;
instead.
Rook has beaten me to it, but I will make one suggestion. Use a loop for decoding, testing on the stringstream status. OK, two suggestions: break your code into functions. Putting it all in one big lump makes it harder to understand.
void DecodeLine(const std::string &sLine,
std::vector<double> &vResults)
{
std::istringstream istr(sLine);
double d = 0;
istr >> d;
while (!istr.fail())
{
vResults.push_back(d);
istr >> d;
}
}