65.9K
CodeProject 正在变化。 阅读更多。
Home

C++17: string_view 转换为整数类型

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.94/5 (7投票s)

2016 年 9 月 19 日

CPOL

3分钟阅读

viewsIcon

34892

downloadIcon

461

使用 Boost Spirit Qi v2 实现 string_view 到整数类型的转换

目录

原理

在开始讨论 string_view 之前,我们必须重新审视一个 C API:strtok(),其目的是将 char 数组拆分为标记。

char * strtok ( char * str, const char * delimiters );
#include <cstdio>
#include <algorithm>

int main()
{
    using namespace std;

    char str[] = "Apple,Orange,Mango";
    /* dup_arr will contains str later */
    char dup_arr[sizeof(str)];
    const char s[2] = ",";
    char *token;

    /* display str address */
    printf("str address:%p\n", str);

    /* get the first token */
    token = strtok(str, s);

    /* walk through other tokens */
    while (token != NULL)
    {
        /* display token */
        printf("%s\n", token);
        /* display token address */
        printf("token address:%p\n", token);

        /* copy str into dup_arr */
        memcpy(dup_arr, str, sizeof(dup_arr));
        /* replace the null char with \x35 which is # */
        std::replace(begin(dup_arr), end(dup_arr)-1, 0, 35);
        /* display dup_arr */
        printf("%s\n", dup_arr);

        /* get next token */
        token = strtok(NULL, s);
    }

    return(0);
}

输出如下所示。 我添加了 ^ 来指示 tokenstr 中的指向位置。 我使用 # 表示空终止符,因为它是一个不可打印的字符。

str address:008FF704

Apple
token address:008FF704
Apple#Orange,Mango
^

Orange
token address:008FF70A
Apple#Orange#Mango
      ^

Mango
token address:008FF711
Apple#Orange#Mango
             ^

strtok 有两个问题:它会修改 str 参数,但它的优点是它非常快,因为它不必为标记分配字符串。 另一个问题是它无法拆分带有空标记的字符串:例如:",,",因为它会变成 "##",并导致 strtok 返回 null,这会过早地向客户端代码发出信号,表明它已到达 str 的末尾。 这就是 C++17 string_view 拯救的地方:string_view 包含一个 char 指针和一个 size 作为数据成员,并包含许多 std::string 拥有的有用成员函数。 它的长度不包括空终止符,这意味着 string_view 不必以空值结尾,这使其成为编写 C++17 constness 正确 strtok 的理想候选者。 但本文不是关于编写 string_view 版本的 strtok 的。

这种原地字符串修改广泛用于快速 XML DOM 解析器,例如 RapidXMLPugixmlRapidJSON 是一个 JSON 解析器,它也使用了这个特性。

// original xml text
<Fruit name="Orange" type="Citrus" />
// mutated xml text
<Fruit#name#"Orange# type#"Citrus# />
 ^     ^     ^       ^     ^

如果文本是严格不可变的并且需要进行转义(如下所示),或者文本被修改为更长,则 XML 解析器无法完全避免字符串分配。

<Food name="Ben & Jerry" type="Ice Cream" />

"Ben & Jerry" needs to be unescaped to "Ben & Jerry"

转换为浮点数和整数

对于转换,我们使用 Boost Spirit Qi。 str_to_value 是一个重载的模板函数,它适用于 std::stringstring_viewchar 数组(不是 char 指针)。 为了演示目的,我们使用 Boost string_ref,因为 Visual C++ 尚不可用 string_view。 为了简单起见,未显示其他重载。 读者可以在 str_to_value.h 中查看它们。 支持 floatshortlonglong long 以及它们的 unsigned 对应物。

#include <string>
#include <iostream>
#include <boost/utility/string_ref.hpp> 
#include <boost/spirit/include/qi.hpp>

template<typename string_type>
inline bool str_to_value(const string_type& src, double& dest)
{
    namespace qi = boost::spirit::qi;

    return qi::parse(std::cbegin(src), std::cend(src), qi::double_, dest);
}

template<typename string_type>
inline bool str_to_value(const string_type& src, int& dest)
{
    namespace qi = boost::spirit::qi;

    return qi::parse(std::cbegin(src), std::cend(src), qi::int_, dest);
}

int main(int argc, char *argv [])
{
    boost::string_ref srd("123.456");
    double d = 0.0;
    if (str_to_value(srd, d))
    {
        std::cout << d << std::endl; // display 123.456
    }

    boost::string_ref srn("123");
    int n = 0;
    if (str_to_value(srn, n))
    {
        std::cout << n << std::endl; // display 123
    }

    return 0;
}

Boost Spirit Qi 基准测试

C++ 字符串到 Double 基准测试(循环 100 万次)

1.1.1 版 double 基准测试

最新的 double 基准测试,它修复了 crack_atof 科学计数法转换问题,并将性能提高了 10%,使其与 fast_atof 相当。

               atof:  100ms
       lexical_cast:  648ms
 std::istringstream:  677ms <== Probably unfair comparison since istringstream instaniate a string
          std::stod:  109ms
        std::strtod:   96ms
         crack_atof:    7ms
          fast_atof:    7ms <== do not use this one because conversion is not correct.
       boost_spirit:   17ms <== reported to be inaccurate in some case
       google_dconv:   38ms
    std::from_chars:   71ms

C++ 字符串到整数基准测试(循环 1000 万次)

               atol:  243ms
       lexical_cast:  952ms
 std::istringstream: 5338ms
         std::stoll:  383ms
        simple_atol:   74ms
         sse4i_atol:   72ms
       boost_spirit:   78ms
    std::from_chars:   59ms

摘要

  • 最初,string_view 在 Boost 中被称为 string_ref
  • String view 未以 null 结尾,因此无法使用 atoi()
  • 使用 Boost Spirit Qi
    • 可用于 std::string、char 数组。 不支持 char ptr
    • 或者任何具有 cbegin()cend() 的类
    • 警告:有人报告 Boost Spirit Qi 浮点转换不准确

相关的源代码存储库如下。

历史

  • 2016 年 10 月 1 日:首次发布
  • 2017 年 4 月 5 日:使用 strtodGoogle double 转换 更新了 string-to-float 基准测试
  • 2018 年 6 月 6 日:上传了 floatbench 1.1.0,它修复了 crack_atof() 科学计数法转换问题,其性能提高了 10%。 感谢 Tian Bo。
  • 2018 年 6 月 7 日:上传了 intbench 1.1.0,其中包含基准测试中的 std::from_charsstd::from_chars 需要 C++17 支持,并且 VC++ 在编译浮点转换时存在问题,因此目前仅在 intbench 中添加。
  • 2018 年 10 月 27 日:上传了 floatbench 1.1.2,其中包含来自 VS 2018 Update 15.8 的 std::from_chars 进入基准测试。
© . All rights reserved.