C++ Split decimal string into two integers - c++

Given a decimal values (seconds and fractions thereof) as a string such as
std::string span = "ssss.nnnn" // ssss is value in seconds, nnnn is fractional seconds
What is the best way to convert it to a timeval structure (val.ts_sec and val.ts_usec) or a timespec structure (tv_sec and tv_nsec).
Most of the answers discuss converting values or are not C++. Some answers get very complex or set up classes which really is too much for this usage.
Obviously sscanf or istringstream can be used if the two values are separated by white space. However, is there a simple way of doing this if they are separated by a "." without looping over the character buffer searching for the "."

EDIT: As Borgleader rightly mentioned, simply reading into a double can incur precision loss if the timestamp becomes sufficiently large (larger than a million, give or take). A numerically stable way is
timeval v;
time_t seconds;
double fraction;
std::istringstream parser(span);
if(parser >> seconds >> std::noskipws >> fraction) {
v.tv_sec = seconds;
v.tv_usec = static_cast<suseconds_t>(fraction * 1e6);
}
Since now the fraction part is guaranteed to be small enough that the mantissa of an ieee-754 double will cover more than 9 decimal digits after the comma. One possible addition is
v.tv_usec = static_cast<suseconds_t>(fraction * 1e6 + 0.5); // rounding to nearest instead of down
depending on your use case.

If you decide to use string class and its functions If the number is always decimal, then I would suggest the following solution:
string span = "1234.123";
span += "000000";
size_t pos = span.find('.');
struct timeval val;
val.tv_sec = stol(span.substr(0,pos));
val.tv_usec = stol(span.substr(pos+1,6));
If the string may also get integer value without the dot '.' character then use
string span = "1234";
size_t pos = span.find('.');
struct timeval val;
val.tv_sec = stol( (pos!=string::npos)? span.substr(0,pos):span );
val.tv_usec = (pos!=string::npos)? stol((span+"000000").substr(pos+1,6)):0;
This solution also uses some c++11.

You can use strtok_s to split a string based off a delimiter. In your case would be "."
#include <iostream>
#include <string>
int main()
{
std::string span = "ssss.nnnn";
char * span1 = (char *)span.c_str();
char * pch = NULL;
char * context;
pch = strtok_s(span1, " .", &context);
while (pch != NULL)
{
printf("%s\n", pch);
pch = strtok_s(NULL, " .", &context);
}
return 0;
}
Output:
ssss
nnnn

I just found this as a possible answer. I would still like to find something else as well.
Parse (split) a string in C++ using string delimiter (standard C++)
strtok allows you to pass in multiple chars as delimiters. I bet if
you passed in ">=" your example string would be split correctly (even
though the > and = are counted as individual delimiters).
EDIT if you don't want to use c_str() to convert from string to char*,
you can use substr and find_first_of to tokenize.
string token, mystring("scott>=tiger");
while(token != mystring){
token = mystring.substr(0,mystring.find_first_of(">="));
mystring = mystring.substr(mystring.find_first_of(">=") + 1);
printf("%s ",token.c_str());
}
Update:
#Wintermute pointed out that the following code snippet would not work because of the possibility of leading zeros.
string span;
int sec;
int usec;
timeval inTime;
sscanf(span.c_str(), "%d.%d", &sec, &usec);
inTime.tv_sec = sec;
inTime.tv_usec = usec;

Related

Is there an easy way to parse a float from a string in C++ GNU ARM embedded?

I have this string in my C++ GNU ARM embedded system:
char* TempStr = "pressure 0.85";
I need the number value stored as a separate float variable called presmax.
Up until now the number to be parsed has always been an integer, so I could use sscanf without any issues. However, as I have read about extensively on the web (and experienced first hand), sscanf doesn't typically work on floats in embedded systems (without some extensive configuration/loss of flash space).
I'm wondering if anyone has any suggestions. Perhaps I could parse the "0.85" as a char array? I'm not quite sure how to do that, though it would allow me to use atof() to turn it into a float, as I've done elsewhere in the program.
I realize the other option is to write a function, however I'm quite an amateur programmer so if there's a more robust/time effective solution I'd best take it.
UPDATE:
In case it helps, TempStr is a string copied from a .txt file on an SD card using FatFs. Here's the full code that reads two lines and stores the results in TempStr each time. I parse the string into its respective variable each time TempStr is stored:
FILINFO fno;
FIL fsrc;
int FileEnd = 0;
int CurrentLine = 0;
int pressmax = 0;
int timemax = 0;
char* TempStr;
WCHAR CharBuffer[100];
res = f_stat("config.txt", &fno); //check for config.txt file
res = f_open(&fsrc, "config.txt", FA_READ | FA_OPEN_ALWAYS); //open config.txt file
//first line
TempStr = f_gets((char*)CharBuffer, sizeof(fsrc), &fsrc);
CurrentLine ++;
FileEnd = FileEnd + strlen(TempStr) + 1;
//sscanf(TempStr, "%*s %i", &presmax); //what I did when presmax was an int
//second line
while ((f_eof(&fsrc) == 0)){
TempStr = f_gets((char*)CharBuffer, sizeof(fsrc), &fsrc);
CurrentLine ++;
FileEnd = FileEnd + strlen(TempStr) + 1;
}
//sscanf(TempStr, "%*s %i", &timemax);
Using GNU ARM Build tools on an STM32L w/Eclipse.
If you are guaranteed that your input will be in the form of
text floating_point_number
then once you have TempStr you can advance a pointer through it until you reach the space, and then go one position further to get to the floating point part of the string. Then you pass that pointer to atof to get the value out of the remainder of string. That would look like
char* fp = TempStr;
while (*fp != ' ') ++fp; // get to the space
double value = atof(++fp); // advance to floating point part and pass to atof
If you don't need TempStr after you get the value then you can get rid of fp and just use
while (*TempStr != ' ') ++TempStr ; // get to the space
double value = atof(++TempStr); // advance to floating point part and pass to atof
It can be done as follows:
float presmax = (float)atof( strchr( TempStr, ' ' ) ;
The cast is only necessary because your question specifically asks for float and atof() returns a double. The cast would be implicit in any event, so:
float presmax = atof( strchr( TempStr, ' ' ) ;
is also acceptable.
strchr() returns a pointer to the first space character, and atof() ignores any leading space. If your config file might use a TAB character, then:
float presmax = (float)atof( strpbrk( TempStr, " \t" ) ;
It will fail if either strchr() or strpbrk() return NULL when the delimiter is not found, so you might make it more robust thus:
#define PRESMAX_DEFAULT 1.0f
const char* prestr = strchr( TempStr ) ;
float presmax = prestr == NULL ? PRESMAX_DEFAULT : (float)atof( prestr ) ;

Need a constant length in strings [duplicate]

I have some numbers of different length (like 1, 999, 76492, so on) and I want to convert them all to strings with a common length (for example, if the length is 6, then those strings will be: '000001', '000999', '076492').
In other words, I need to add correct amount of leading zeros to the number.
int n = 999;
string str = some_function(n,6);
//str = '000999'
Is there a function like this in C++?
or using the stringstreams:
#include <sstream>
#include <iomanip>
std::stringstream ss;
ss << std::setw(10) << std::setfill('0') << i;
std::string s = ss.str();
I compiled the information I found on arachnoid.com because I like the type-safe way of iostreams more. Besides, you can equally use this code on any other output stream.
char str[7];
snprintf (str, 7, "%06d", n);
See snprintf
One thing that you may want to be aware of is the potential locking that may go on when you use the stringstream approach. In the STL that ships with Visual Studio 2008, at least, there are many locks taken out and released as various locale information is used during formatting. This may, or may not, be an issue for you depending on how many threads you have that might be concurrently converting numbers to strings...
The sprintf version doesn't take any locks (at least according to the lock monitoring tool that I'm developing at the moment...) and so might be 'better' for use in concurrent situations.
I only noticed this because my tool recently spat out the 'locale' locks as being amongst the most contended for locks in my server system; it came as a bit of a surprise and may cause me to revise the approach that I've been taking (i.e. move back towards sprintf from stringstream)...
There are many ways of doing this. The simplest would be:
int n = 999;
char buffer[256]; sprintf(buffer, "%06d", n);
string str(buffer);
This method doesn't use streams nor sprintf. Other than having locking problems, streams incur a performance overhead and is really an overkill. For streams the overhead comes from the need to construct the steam and stream buffer. For sprintf, the overhead comes from needing to interpret the format string. This works even when n is negative or when the string representation of n is longer than len. This is the FASTEST solution.
inline string some_function(int n, int len)
{
string result(len--, '0');
for (int val=(n<0)?-n:n; len>=0&&val!=0; --len,val/=10)
result[len]='0'+val%10;
if (len>=0&&n<0) result[0]='-';
return result;
}
stringstream will do (as xtofl pointed out). Boost format is a more convenient replacement for snprintf.
This is an old thread, but as fmt might make it into the standard, here is an additional solution:
#include <fmt/format.h>
int n = 999;
const auto str = fmt::format("{:0>{}}", n, 6);
Note that the fmt::format("{:0>6}", n) works equally well when the desired width is known at compile time. Another option is abseil:
#include <absl/strings/str_format.h>
int n = 999;
const auto str = absl::StrFormat("%0*d", 6, n);
Again, abs::StrFormat("%06d", n) is possible. boost format is another tool for this problem:
#include <boost/format.hpp>
int n = 999;
const auto str = boost::str(boost::format("%06d") % n);
Unfortunately, variable width specifier as arguments chained with the % operator are unsupported, this requires a format string setup (e.g. const std::string fmt = "%0" + std::to_string(6) + "d";).
In terms of performance, abseil and fmt claim to be very attractive and faster than boost. In any case, all three solutions should be more efficient than std::stringstream approaches, and other than the std::*printf family, they do not sacrifice type safety.
sprintf is the C-like way of doing this, which also works in C++.
In C++, a combination of a stringstream and stream output formatting (see http://www.arachnoid.com/cpptutor/student3.html ) will do the job.
From C++ 11, you can do:
string to_string(unsigned int number, int length) {
string num_str = std::to_string(number);
if(num_str.length() >= length) return num_str;
string leading_zeros(length - num_str.length(), '0');
return leading_zeros + num_str;
}
If you also need to handle negative numbers, you can rewrite the function as below:
string to_string(int number, int length) {
string num_str = std::to_string(number);
if(num_str.length() >= length) return num_str;
string leading_zeros(length - num_str.length(), '0');
//for negative numbers swap the leading zero with the leading negative sign
if(num_str[0] == '-') {
num_str[0] = '0';
leading_zeros[0] = '-';
}
return leading_zeros + num_str;
}

C++: Convert std::string to UINT64

I need to convert a (decimal, if it matters) string representation of a number input from a text file to a UINT64 to pass to my data object.
size_t startpos = num.find_first_not_of(" ");
size_t endpos = num.find_last_not_of(" ");
num = num.substr(startpos, endpos-startpos+1);
UINT64 input;
//convert num to input required here
Is there any way to convert an std::string to a UINT64 in a similar way to atoi()?
Thanks!
Edit:
Working code below.
size_t startpos = num.find_first_not_of(" ");
size_t endpos = num.find_last_not_of(" ");
num = num.substr(startpos, endpos-startpos+1);
UINT64 input; //= std::strtoull(num.cstr(), NULL, 0);
std::istringstream stream (num);
stream >> input;
Use strtoull or _strtoui64().
Example:
std::string s = "1123.45";
__int64 n = std::strtoull(s.c_str(),NULL,0);
There are at least two ways to do this:
Construct a std::istringstream, and use our old friend, the >> operator.
Just convert it yourself. Parse all the digits, one at a time, converting them to a single integer. This is a very good exercise. And since this is an unsigned value, there isn't even a negative number to worry about. I would think that this would be a standard homework assignment in any introductory computer science class. At least it was, back in my days.
you can use stoull:
char s[25] = "12345678901234567890"; // or: string s = "12345678901234567890";
uint64_t a = stoull(s);

How to capture length of sscanf'd string?

I'm parsing a string that follows a predictable pattern:
1 character
an integer (one or more digits)
1 colon
a string, whose length came from #2
For example:
s5:stuff
I can see easily how to parse this with PCRE or the like, but I'd rather stick to plain string ops for the sake of speed.
I know I'll need to do it in 2 steps because I can't allocate the destination string until I know its length. My problem is gracefully getting the offset for the start of said string. Some code:
unsigned start = 0;
char type = serialized[start++]; // get the type tag
int len = 0;
char* dest = NULL;
char format[20];
//...
switch (type) {
//...
case 's':
// Figure out the length of the target string...
sscanf(serialized + start, "%d", &len);
// <code type='graceful'>
// increment start by the STRING LENGTH of whatever %d was
// </code>
// Don't forget to skip over the colon...
++start;
// Build a format string which accounts for length...
sprintf(format, "%%%ds", len);
// Finally, grab the target string...
sscanf(serialized + start, format, string);
break;
//...
}
That code is roughly taken from what I have (which isn't complete because of the issue at hand) but it should get the point across. Maybe I'm taking the wrong approach entirely. What's the most graceful way to do this? The solution can either C or C++ (and I'd actually like to see the competing methods if there are enough responses).
You can use the %n conversion specifier, which doesn't consume any input - instead, it expects an int * parameter, and writes the number of characters consumed from the input into it:
int consumed;
sscanf(serialized + start, "%d%n", &len, &consumed);
start += consumed;
(But don't forget to check that sscanf() returned > 0!)
Use the %n format specifier to write the number of characters read so far to an integer argument.
Here's a C++ solution, it could be better, and is hard-coded specifically to deal with your example input, but shouldn't require much modification to get working.
std::stringstream ss;
char type;
unsigned length;
char dummy;
std::string value;
ss << "s5:Helloxxxxxxxxxxx";
ss >> type;
ss >> length;
ss >> dummy;
ss.width(length);
ss >> value;
std::cout << value << std::endl;
Disclaimer:
I'm a noob at C++.
You can probably just use atoi which will ignore the colon.
e.g. len = atoi(serialized + start);
The only thing with atoi is that if it returns zero it could mean either the conversion failed, or that the length was truly zero. So it's not always the most appropriate function.
if you replace you colon with a space scanf will stop on it and you can get the size malloc the size then run another scanf to get the rest of the string`
int main (int argc, const char * argv[]) {
char foo[20];
char *test;
scanf("%s",foo); //"hello world"
printf("foo = %s\n", foo);//prints hello
//get size
test = malloc(sizeof(char)* 10);//replace 10 with your string size
scanf("%s", test);
printf("test = %s\n", test);//prints world
return 0;
}
`
Seems like the format is overspecified... (using a variable length field to specify the length of a variable length field).
If you're using GCC, I'd suggest
if (sscanf(serialized,"%c%d:%as",&type,&len,&dest)<3) return -1;
/* use type, dest; ignore len */
free(dest);
return 0;

Convert a number to a string with specified length in C++

I have some numbers of different length (like 1, 999, 76492, so on) and I want to convert them all to strings with a common length (for example, if the length is 6, then those strings will be: '000001', '000999', '076492').
In other words, I need to add correct amount of leading zeros to the number.
int n = 999;
string str = some_function(n,6);
//str = '000999'
Is there a function like this in C++?
or using the stringstreams:
#include <sstream>
#include <iomanip>
std::stringstream ss;
ss << std::setw(10) << std::setfill('0') << i;
std::string s = ss.str();
I compiled the information I found on arachnoid.com because I like the type-safe way of iostreams more. Besides, you can equally use this code on any other output stream.
char str[7];
snprintf (str, 7, "%06d", n);
See snprintf
One thing that you may want to be aware of is the potential locking that may go on when you use the stringstream approach. In the STL that ships with Visual Studio 2008, at least, there are many locks taken out and released as various locale information is used during formatting. This may, or may not, be an issue for you depending on how many threads you have that might be concurrently converting numbers to strings...
The sprintf version doesn't take any locks (at least according to the lock monitoring tool that I'm developing at the moment...) and so might be 'better' for use in concurrent situations.
I only noticed this because my tool recently spat out the 'locale' locks as being amongst the most contended for locks in my server system; it came as a bit of a surprise and may cause me to revise the approach that I've been taking (i.e. move back towards sprintf from stringstream)...
There are many ways of doing this. The simplest would be:
int n = 999;
char buffer[256]; sprintf(buffer, "%06d", n);
string str(buffer);
This method doesn't use streams nor sprintf. Other than having locking problems, streams incur a performance overhead and is really an overkill. For streams the overhead comes from the need to construct the steam and stream buffer. For sprintf, the overhead comes from needing to interpret the format string. This works even when n is negative or when the string representation of n is longer than len. This is the FASTEST solution.
inline string some_function(int n, int len)
{
string result(len--, '0');
for (int val=(n<0)?-n:n; len>=0&&val!=0; --len,val/=10)
result[len]='0'+val%10;
if (len>=0&&n<0) result[0]='-';
return result;
}
stringstream will do (as xtofl pointed out). Boost format is a more convenient replacement for snprintf.
This is an old thread, but as fmt might make it into the standard, here is an additional solution:
#include <fmt/format.h>
int n = 999;
const auto str = fmt::format("{:0>{}}", n, 6);
Note that the fmt::format("{:0>6}", n) works equally well when the desired width is known at compile time. Another option is abseil:
#include <absl/strings/str_format.h>
int n = 999;
const auto str = absl::StrFormat("%0*d", 6, n);
Again, abs::StrFormat("%06d", n) is possible. boost format is another tool for this problem:
#include <boost/format.hpp>
int n = 999;
const auto str = boost::str(boost::format("%06d") % n);
Unfortunately, variable width specifier as arguments chained with the % operator are unsupported, this requires a format string setup (e.g. const std::string fmt = "%0" + std::to_string(6) + "d";).
In terms of performance, abseil and fmt claim to be very attractive and faster than boost. In any case, all three solutions should be more efficient than std::stringstream approaches, and other than the std::*printf family, they do not sacrifice type safety.
sprintf is the C-like way of doing this, which also works in C++.
In C++, a combination of a stringstream and stream output formatting (see http://www.arachnoid.com/cpptutor/student3.html ) will do the job.
From C++ 11, you can do:
string to_string(unsigned int number, int length) {
string num_str = std::to_string(number);
if(num_str.length() >= length) return num_str;
string leading_zeros(length - num_str.length(), '0');
return leading_zeros + num_str;
}
If you also need to handle negative numbers, you can rewrite the function as below:
string to_string(int number, int length) {
string num_str = std::to_string(number);
if(num_str.length() >= length) return num_str;
string leading_zeros(length - num_str.length(), '0');
//for negative numbers swap the leading zero with the leading negative sign
if(num_str[0] == '-') {
num_str[0] = '0';
leading_zeros[0] = '-';
}
return leading_zeros + num_str;
}