简体   繁体   中英

C++ Split decimal string into two integers

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.

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;

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM