Reputation: 53119
I'm stuck at designing this function:
//Turns "[0-9]+,[0-9]+" into two integers. Turns "[0-9]+" in two *equal* integers
static void parseRange(const std::string, int&, int&);
I don't have access to regular expressions (which would require either C++11 or Boost library). I need to somehow find out if the string contains 2 integers and split it, then get each integer.
I guess I'd need strstr
version that uses std::string
to find out if there's a comma and where. I could, probably, operate with std::string::c_str
value. Extensive searching led me to this (but I want to use std::string
, not C string):
void Generator::parseRange(const std::string str, int& min, int& max) {
const char* cstr = str.c_str();
const char* comma_pos;
//There's a comma
if((comma_pos=strstr(cstr, ","))!=NULL) { //(http://en.cppreference.com/w/cpp/string/byte/strstr)
//The distance between begining of string and the comma???
//Can I do this thing with pointers???
//Is 1 unit of pointer really 1 character???
unsigned int num_len = (comma_pos-cstr);
//Create new C string and copy the first part to it (http://stackoverflow.com/q/8164000/607407)
char* first_number=(char *)malloc((num_len+1)*sizeof(char));//+1 for \0 character
//Make sure it ends with \0
first_number[num_len] = 0;
//Copy the other string to it
memcpy(first_number, cstr, num_len*sizeof(char));
//Use atoi
min = atoi(first_number);
max = atoi(comma_pos+1);
//free memory - thanks @Christophe
free(first_number);
}
//Else just convert string to int. Easy as long as there's no messed up input
else {
min = atoi(cstr); //(http://www.cplusplus.com/reference/cstdlib/atoi/)
max = atoi(cstr);
}
}
I Googled a lot. You can't really say I didn't try. The function above works, but I'd prefer some less naive implementation, because what you see above is hardcore C code from the old times. And it all relies on fact that nobody messes up with input.
Upvotes: 1
Views: 1363
Reputation: 8641
No need for std::whatever, it will only consume more memory for a no less unreadable code.
Try this circa 1980 C code, it should do the trick:
void generator::parse_range (const std::string input, int & min, int & max)
{
const char * scan = input.c_str();
min = (int) strtol (scan, &scan, 0);
max = (*scan == ',') ? (int)strtol (scan+1, &scan, 0) : min;
if (errno || *scan != '\0') panic ("you call that numbers?");
}
This will accept hex or octal inputs, though you can fix the base with the 3rd parameter.
You could also check errno after first conversion or test for long integer overflow, but I assume this is not the worst part of your problem :)
Upvotes: 1
Reputation: 20063
You can accomplish this by using the built in search facilities provided by std::string
along with std::atoi
without making copies or the need to use malloc
or new
to store parts of the string.
#include <cstdlib>
#include <string>
void Generator::parseRange(const std::string &str, int& min, int& max)
{
// Get the first integer
min = std::atoi(&str[0]);
// Check if there's a command and proces the second integer if there is one
std::string::size_type comma_pos = str.find(',');
if (comma_pos != std::string::npos)
{
max = std::atoi(&str[comma_pos + 1]);
}
// No comma, min and max are the same
else
{
max = min;
}
}
Alternatively as others have pointed out you can use std::istringstream
to handle the integer parsing. This will allow you to do additional input validation when parsing the integer values
#include <sstream>
#include <string>
bool Generator::parseRange(const std::string& str, int& min, int& max)
{
std::istringstream sst(str);
// Read in the first integer
if (!(sst >> min))
{
return false;
}
// Check for comma. Could also check and error out if additional invalid input is
// in the stream
if (sst.get() != ',')
{
max = min;
return true;
}
// Read in the second integer
if (!(sst >> max))
{
return false;
}
return true;
}
Upvotes: 2
Reputation: 490048
You can do all the searching and separating pretty easily with std::string
functionality.
int pos = str.find(',');
assert(pos != std::string::npos);
std::string first = str.substr(0, pos);
std::string second = str.substr(pos+1, -1);
Alternatively, you can pretty easily do the parsing with a stringstream
. For example:
std::istringstream s(str);
int one, two;
char ch;
s >> one >> ch >> two;
assert(ch == ',');
Note that this also makes it easy to combine separating the strings and converting the individual pieces into numbers.
Upvotes: 1
Reputation: 73366
What with this more native version:
void Generator::parseRange(const std::string str, int& min, int& max) {
stringstream sst(str);
if (!(sst>>min && sst.get()==',' && sst>>max))
cerr<<"String has an invalid format\n";
}
Upvotes: 1