std::string and erase() to get data I need from string

Started by
5 comments, last by fastcall22 11 years, 3 months ago

I have a string as follows

640x480x32

I want to grab each of the three values separately, so I can put them into a int variable.

I am not sure what std lib can do this for me?

Thanks!

Advertisement

You can use sscanf from stdio. It's like scanf, except it's used on a char* instead of stdin.


sscanf("640x480x32", "%dx%dx%d", &num1, &num2, &num3);

You can also write your own function to parse it.

But I have a feeling that it is possible to parse it with just string stream and a few << operators.

Yeah, if you already have the "640x480x32" in a string, you will have to perform tokenization in order to split it up into "Tokens". After that you can extract them as integers. Here is some code to do that:


#include <sstream>
#include <string>
#include <iostream>
#include <vector>

using namespace std;

int main()
{
   string u_numbers = "640x480x32";  //unformatted numbers
   string f_numbers; //formatted numbers
   vector <int> numbers; //final numbers in integer form
   stringstream ss;
   for(int i = 0; i < u_numbers.size(); i++)
   {
      if(u_numbers.at(i) == 'x') //check if next character is the delimiter
      {
         ss.put(' ');
      }
      else
      {
         ss.put(u_numbers.at(i)); //Output the unformatted string to stringstream
      }
   }

   std::getline(ss, f_numbers); //Get all the data we just put into the stream and put it into another string

   std::istringstream iss(f_numbers); //create an integer string stream, fill the stream with the string of formatted numbers
   int n;

   while (iss >> n)
   {
      numbers.push_back(n); //pull all the data from the iss and store it as integers in our vector
   }

   for (int i = 0; i < 3; i++) //a little test to see if it works
   {
      cout << numbers[i] << ' ';
   }


return 0;
}

If you can rely on your input being formatted like that, just dump it in a stringstream and read it back.


const string res = "640x480x32";
int height, width, depth;
char delimiter;  


stringstream stream(res);
stream >> height >> delimiter >> width >> delimiter >> depth;

You could use getline and define 'x' as a delimiter, but then you get each number as a string and still need to convert to int.

You could use .ignore(1) instead of the dummy character, but it will look horrible to read.

f@dzhttp://festini.device-zero.de

Thanks all! Love to see so many different approachs!

Another approach (with regular expressions and boost lexical cast):


#include <regex>
#include <boost/lexical_cast.hpp>

std::cmatch matches;
std::regex regExp("\\s{0,}([0-9]+)\\s{0,}x([0-9]+)\\s{0,}x\\s{0,}([0-9]+)\\s{0,}", std::regex_constants::icase);

if (std::regex_match("100X200 x 300", matches, regExp)) {
  unsigned int width = boost::lexical_cast<unsigned int>(matches[1].str());
  unsigned int height = boost::lexical_cast<unsigned int>(matches[2].str());
  unsigned int depth = boost::lexical_cast<unsigned int>(matches[3].str());
}
With error checking:

struct ScreenResolution {
	int width, height, depth;
};

istream& operator >> ( istream& stream, ScreenResolution& r ) {
	ScreenResolution temp;
	array<char,2> delim;
	
	auto pos = stream.tellg();
	if ( (stream >> temp.width >> delim[0] >> temp.height >> delim[1] >> temp.depth) && count(delim.begin(),delim.end(),'x') == 2 )
		r = temp;
	else {
		stream.clear( stream.rdstate() | ios::failbit );
		stream.seekg( pos );
	}
	return stream;
}

int main() {
	istringstream stream("800x600x32");
	ScreenResolution r;
	if ( stream >> r )
		cout << r.width << 'x' << r.height << 'x' << r.depth << endl;
	else 
		cout << "error" << endl;
}

This topic is closed to new replies.

Advertisement