【问题标题】:C++ Split decimal string into two integersC ++将十进制字符串拆分为两个整数
【发布时间】:2015-03-07 23:12:06
【问题描述】:

给定一个十进制值(秒及其小数)作为字符串,例如

std::string span = "ssss.nnnn"  // ssss is value in seconds, nnnn is fractional seconds

将其转换为 timeval 结构(val.ts_sec 和 val.ts_usec)或 timespec 结构(tv_sec 和 tv_nsec)的最佳方法是什么。

大多数答案都讨论了转换值或者不是 C++。有些答案变得非常复杂,或者设置的类对于这种用法来说实在是太多了。

如果两个值用空格分隔,显然可以使用 sscanf 或 istringstream。但是,如果它们用“。”分隔,有没有一种简单的方法可以做到这一点。无需在字符缓冲区中循环搜索“。”

【问题讨论】:

  • find_first_of 可能吗?
  • 可能重复:Splitting string in C++,更具体地说是第二个答案。
  • getline 采用可选分隔符。
  • 你如何决定是 123.5678 还是 123.005678?
  • @Wintermute 啊,好点子。

标签: c++ string converter


【解决方案1】:

编辑:正如 Borgleader 正确提到的,如果时间戳变得足够大(大于一百万,给予或接受),简单地读入双精度可能会导致精度损失。一种数值稳定的方法是

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);
}

从现在起,fraction 部分保证足够小,以至于 ieee-754 double 的尾数将覆盖逗号后超过 9 个十进制数字。一种可能的补充是

  v.tv_usec = static_cast<suseconds_t>(fraction * 1e6 + 0.5); // rounding to nearest instead of down

取决于您的用例。

【讨论】:

  • 这不会导致精度下降吗?
  • 嗯...这是一个不错的观点。如果 t 变得大于大约一百万,它将开始泄漏纳秒,我认为时间戳可能会发生这种情况。我会在关键的情况下编辑一些内容。
  • @Wintermute 这就是为什么我希望有一种方法可以将字符串分成两部分,然后将它们分别转换为 timeval 或 timespec 结构。
  • 查看编辑。这样就有足够的空间来吞下任何错误。基本方法是一样的;没有必要在这里胡闹。
  • @Wintermute 我刚刚意识到使用“%d.%d”格式的 sscanf 可以将值的两个部分作为整数获取,而不必担心乘法或缺少空格.
【解决方案2】:

如果您决定使用string 类及其函数如果数字始终为十进制,那么我建议以下解决方案:

  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));

如果string 也可能得到不带点“.”的整数值字符然后使用

  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;

此解决方案还使用了一些 c++11。

【讨论】:

  • 这基本上是我会做的 (+1),因为它已经在 std::string 中,并且我避免使用双精度数来表示整数类型。但是,您不需要添加零,并且您确实需要更好地验证。 pos!=npos, pos+1
  • @KennyOstrom 您对pos!=npos 的看法是正确的。我只是尽量保持简单,我假设点 '.'一直存在。我会添加你的建议。关于零, tv_usec 存储微秒数。如果我们不添加零,那么我们将有 123 微秒而不是 123000。解决方案是在末尾附加足够多的零 (6),并使用前 6 个字符截断字符串。
【解决方案3】:

您可以使用 strtok_s 根据分隔符拆分字符串。在你的情况下是“。”

#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;
}

输出:

ssss

nnn

【讨论】:

  • 来自问题:“大多数答案都讨论转换值或不是 C++” 这正是您在这里所做的。
【解决方案4】:

我刚刚发现这是一个可能的答案。我还是想找点别的。

Parse (split) a string in C++ using string delimiter (standard C++)

strtok 允许您传入多个字符作为分隔符。我打赌如果 您传入 ">=" 您的示例字符串将被正确拆分(甚至 虽然 > 和 = 被视为单独的分隔符)。

如果您不想使用 c_str() 从字符串转换为 char*,请编辑, 您可以使用 substr 和 find_first_of 进行标记。

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());
}

更新:

@Wintermute 指出以下代码 sn-p 将不起作用,因为前导零的可能性。

string span;
int sec;
int usec;
timeval inTime;

sscanf(span.c_str(), "%d.%d", &sec, &usec);
inTime.tv_sec = sec;
inTime.tv_usec = usec;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-17
    • 2015-11-29
    • 2017-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多