boost I 字符串与文本处理

目录

一、lexical_cast

与C语言、C++的对比

二、format

1.format类

2.格式化语法

三、string_ref

1.背景

2.boost::string_ref

3.remove_prefix( )和remove_suffix( )

四、string_algo

1.大小写转换

2.字符串判断

a.函数

b.函数对象

3.分类函数

4.修剪

5.查找

6.替换与删除

7.分割

8.合并

9.查找分割迭代器

五、xpressive


一、lexical_cast

        lexical_cast库可以进行字符串与整数\浮点数之间的互相转换。

        lexical_cast的标准形式有两个模板参数 template Target需要我们手动指定,是转换后的目标类型,通常是数字类型或 std:: string;而第二个参数Source则不必写出,因为它可以由函数参数推导出来。

        字符串转数字时,字符串中只能有数字和小数点,不能出现字母或其他非数字字符 (表示指数的e\E 除外) 。

        lexical_cast可以将整型或字符串的0、1转换为bool类型,注意不能使用true\false字面值。

        当lexical_cast无法执行转换操作时会抛出异常 bad_lexical_cast,它是std::bad_cast的派生类。我们可以使用try/catch块来保护代码。当然,我们可以使用try_lexical_convert( )安全的转换字面值,避免抛出异常,它以bool返回值来表示是否转换成功。

        lexical_cast内部使用了标准库的流操作,因此,对于它的转换对象有以下要求,标准容器和其他自定义类型需满足这些条件,否则不能使用 lexical_cast 。

  • 转换起点对象是可流输出的,即定义了 opreator< < 。
  • 转换终点对象是可流输入的,即定义了 opreator> >。
  • 转换终点对象必须是可默认构造、可拷贝构造的。
#include 
void TestLexical_Cast()
{
    std::string str = boost::lexical_cast(0x96);
    std::cout << str << std::endl;                  //150

    std::string str1 = boost::lexical_cast(30);
    std::cout << str1 << std::endl;                 //30

    float pi = boost::lexical_cast("3.141592653");
    std::cout << pi << std::endl;                   //3.14159

    //只支持整型、字符串的0、1转bool
    bool bo = boost::lexical_cast("0");
    std::cout << bo << std::endl;

    try {
        //错误,待转换的字符串中只能有数字和小数点及指数的e/E,不能有其他的字符
        int num = boost::lexical_cast("0x96");
        std::cout << num << std::endl;
    }
    catch (boost::bad_lexical_cast e)
    {
        std::cout << e.what() << std::endl;         //bad lexical cast: source type value could not be interpreted as target
    }

    //使用try_lexical_convert安全的转换字面值,避免抛出异常
    int num;
    bool is_success = boost::conversion::try_lexical_convert("0x96", num);
    std::cout << "is_success:" << is_success << ",num:" << num << std::endl;//0,96
}

与C语言、C++的对比

        C 语言中的 atoi ( )和 atof( )系列的函数是不对称的,仅可以把字符串转换成数值,不存在把数值转换为字符串的转换。

        c++11 增强了字符串与数字的互操作性,提供stoX ( ) 和 to_string ( ) 函数实现了字符串与数字之间的转换。它无需写模板参数,而且允许字符串里出现非数字字符——它们会忽略起始的空格,直到遇到无法转换的字符为止。但如果字符串不是以空格、数字开头,或者超出了数字类型的范围,那么这些函数会抛出 std::invalid_argument 或 std::out_of_range 异常。

    int a = 10;
    std::string a_str = std::to_string(a);
    std::cout << a_str << std::endl;                //10
    int b = std::stoi("12ss");
    std::cout << b << std::endl;                    //12

二、format

        C 语言中经典的 printf ( )使用了 C 语言里的可变参数,缺乏类型安全检查(速度快),但它的语法简单高效,并且被广泛地接受和使用,影响深远。

        boost.format 库 " 扬弃 " 了 printf ( ),实现了类似的格式化对象,可以把参数格式化到一个字符串,而且此操作是完全类型安全的(速度慢)。format 模仿了流操作符 "<<"  ,重载了二元操作符operator%作为参数输入符,它同样可以串联任意数量的参数。它已经被收入C ++20 标准。

1.format类

        format并不是一个真正的类,而是一个 typedef ,其真正的实现是 basic_format 。

        成员函数 str( ) 返回 format 对象内部已经格式化好的字符串(不清空),如果没有得到所有格式化字符串要求的参数则会抛出异常。 format 库还提供一个同名的自由函数 str( ),它位于 boost 名字空间,返回 format 对象内部已格式化好的字符串。

        成员函数 size( ) 可以获得已格式化好的字符串的长度,如果没有得到所有格式化字符串要求的参数则会抛出异常。

        成员函数 parse ( ) 清空 format 对象的内部缓存,并改用一个新的格式化字符串。如果仅仅想清空缓存,则可以使用 clear ( ),它把 format 对象恢复到初始化状态。这两个函数执行后再调用 str ( )或size( )则会抛出异常,因为此时没有输入格式化参数。

2.格式化语法

        format基本继承了 printf 的格式化语法,如%05d、%-8.3f等。除经典的 printf 格式化外,format 还增加了新的格式:

  • %|spec| 增加竖线分割,更好地区分格式化选项与普通字符
  • %N% 标记第N个参数,相当于占位符,不带任何其他的格式化选项
#include 
void TestFormat()
{
    std::cout << boost::format("%s:%d+%d=%d\n") % "sum" % 1 % 2 % (1 + 2); //sum:1+2=3
    //对比printf
    printf("%s:%d+%d=%d\n", "sum", 1, 2, 1 + 2);                           //sum:1+2=3

    //%N% 标记第N个参数,相当于占位符,不带任何其他的格式化选项
    boost::format fmt("(%1%+%2%)*%3%=%4%");                                //预先创建一个format格式化对象
    fmt % 4 % 6 % 2;                                                       //分多次输入被格式化的参数
    fmt % ((4 + 6) * 2);
    std::cout << "格式化字符串:" << fmt.str() << ",字符串长度:" << fmt.size();//格式化字符串:(4+6)*2=20,字符串长度:10                                               //(4+6)*2=20
    fmt.clear();
    fmt % 3 % 6 % 4 % ((3 + 6) * 4);
    std::cout << "格式化字符串:" << fmt.str() << ",字符串长度:" << fmt.size();//格式化字符串:(3+6)*4=36,字符串长度:10
    //对比printf
    printf("(%d+%d)*%d=%d", 4, 6, 2, (4 + 6) * 2);                         //(4+6)*2=20

    //%|spec| 增加竖线分割,更好地区分格式化选项与普通字符
    boost::format fmt1("%|05d|-%|-8.3f|-%|10s|-%|05X|");
    fmt1 % 10 % 3.14%"hello" % 150;
    std::cout << fmt1.str() << std::endl;//00010-3.140   -     hello-00096
}

三、string_ref

1.背景

        在C ++中处理字符串的基本工具是标准字符串std::string,但构造一个 std::string 成本较高, 因为它必须完全持有字符串的内容,极端的时候会有很高的内存拷贝代价,影响程序效率。使用const std::string& 可以避免一些问题,但它在处理 C 字符串、提取子串时却无能为力。总而言之, std::string 显得有些 " 重 ",我们需要一种更 " 轻 " 的字符串工具——boost::string_ref。

2.boost::string_ref

        boost::string_ref它只持有字符串的引用,没有内存拷贝代价,所以运行效率很高,是更好的 const std::string &。 它已经被收入 C ++17 标准 ( 但更名为string_view)。

        string_ref库定义了 basic_string_ref,它不拷贝字符串,所以也不分配内存,只用两个成员变量 ptr_ 和len_ 标记字符串的起始位置和长度,这样就实现了对字符串的表示。basic_string_ref是一个字符串的“常量视图”,大部分成员函数都是由 const 修饰的,我们只能像const std::string & 那样去观察字符串而无法修改字符串。

        由于 string_ref 的接口与 string 完全相同,所以它的一个重要用途是代替 const std::string & 类型作为函数参数或返回值(必须保证被引用的字符串对象可用,尽量避免长期持有或延后使用。在确实需要持有或修改字符串的时候,可以调用成员函数 to_string ( )获得一个拷贝副本来保证安全。),可以完全避免字符串拷贝代价,提高字符串的处理效率。

3.remove_prefix( )和remove_suffix( )

        虽然 string_ref 不能直接改变原字符串,但它可以使用remove_prefix( )和remove_suffix( ) 这两个函数调整 string _ ref 内部的字符串指针和长度,达到变动字符串引用的目的——但原始字符串仍然没有被修改。

#include 
void TestStringRef()
{
    const char* ch = "Study C++ in library";
    std::string str(ch);                            //标准字符串,有拷贝成本

    boost::string_ref str_ref(ch);                  //零拷贝
    if (str_ref == str)
    {
        std::cout << "两个字符串相等" << std::endl;   //两个字符串相等
    }
    std::cout << "第一个字符:" << str_ref.front() << std::endl;              //第一个字符:S
    std::cout << "最后一个字符:" << str_ref[str_ref.length()-1] << std::endl;//最后一个字符:y
    int index = str_ref.find('+');
    std::cout << "+索引" << index << std::endl;     //+索引7

    boost::string_ref substr = str_ref.substr(6, 3);
    std::cout << substr << std::endl;              //C++

    std::string str2 = str_ref.to_string();
    if (str2 == str&& str_ref==str2)
    {
        std::cout << "三个字符串相等" << std::endl;  //三个字符串相等
    }

    str_ref.remove_prefix(6);
    std::cout <<"移除前6个字符:"<< str_ref << std::endl;  //移除前6个字符:C++ in library
    str_ref.remove_suffix(8);
    std::cout << "移除后8个字符:" << str_ref << std::endl;//移除后8个字符:C++ in
}

四、string_algo

        string_algo 库的出现改变了这个局面。它是一个非常全面的字符串算法库,提供了大量的字符串操作函数,如大小写转换、字符串判断、去空格、查找替换、分割与合并等, string_algo 库可以在不使用正则表达式的情况下处理大多数字符串的相关问题。

        string_algo 库中的算法命名遵循了标准库的惯例,算法名均为小写形式,并使用不同的词缀来区分不同的版本,命名规则如下。

  • 前缀 i :大小写不敏感(忽略大小写);否则是大小写敏感的。
  • 后缀 _copy :不变动输入,返回处理结果的拷贝;否则原地处理,输入即输出。
  • 后缀 _if :需要一个作为判断式的谓词函数对象;否则使用默认的判断准则。

1.大小写转换

  • to_upper() 、to_upper_copy()
  • to_lower()、to_lower_copy()
#include 

std::string str("c://test/readme.json");
//大小写转换
std::cout << boost::to_upper_copy(str) << std::endl; //结果 C://TEST/README.JSON

2.字符串判断

a.函数

  • lexicographical_compare、ilexicographical_compare :根据字典顺序检测一个字符串是否小于另一个字符串。
  • starts_with、istarts_with:检测字符串是否以另一个字符串为前缀。
  • ends_with、iends_with:检测字符串是否以另一个字符串为后缀。
  • contains、icontains:检测字符串是否包含另一个字符串。
  • equals、iequals :检测两个字符串是否相等。
  • all :检测字符串是否满足指定的判断式。
    std::string str("c://test/readme.json");
    if (boost::ends_with(str, "json"))
    {
        std::cout << "是个json文件" << std::endl;         //是个json文件
    }
    if (boost::icontains(str, "C://"))//忽略大小写
    {
        std::cout << "文件在C盘中" << std::endl;          //文件在C盘中
    }
    if (boost::all(str.substr(9, 6), boost::is_lower()))
    {
        std::cout << "文件名是小写" << std::endl;         //文件名是小写
    }

b.函数对象

  • is_equal :与 equals 算法类似,比较两个对象是否相等。
  • is_less :比较两个对象是否具有小于关系。
  • is_not_greater :比较两个对象是否具有不大于关系。
    if (boost::is_equal()("hello", "hello"))
    {
        std::cout << "两个字符串相同" << std::endl;       ///两个字符串相同
    }
    if (boost::is_less()("hello", "world"))
    {
        std::cout << "hello小于world" << std::endl;     ///hello小于world
    }
    if (boost::is_not_greater()("hello", "Hello"))
    {
        std::cout << "两个字符串具有不大于关系" << std::endl;///两个字符串具有不大于关系
    }

        注意函数对象名称后的两个括号,第一个括号调用了函数对象的构造函数,产生了一个临时对象,第二个括号才是真正的函数调用操作符operator ( ) 。

3.分类函数

        string_algo 提供一组分类函数,可以用于检测一个字符是否符合某种特性,主要用于搭配其它算法 ,如字符串判断中的 all 算法。string_algo 库的分类函数有下列数种。 

        需要注意的是这些函数并不真正地检测字符,而是返回一个类型为 detail::is_classifiedF 的函数对象,这个函数对象的 operator ( )才是真正的分类函数(因此,这些函数都属于工厂函数)。函数对象 is_classifiedF 重载了逻辑运算符||、&&和!,可以使用逻辑运算符把它们组合成逻辑表达式,以实现更复杂的条件判断。

  • is_space : 字符是否为空格或制表符 ( tab )。
  • is alnum : 字符是否为字母和数字字符。
  • is_alpha : 字符是否为字母。
  • is_cntrl : 字符是否为控制字符。
  • is_digit : 字符是否为十进制数字。
  • is_graph : 字符是否为图形字符。
  • is_lower :字符是否为小写字符。
  • is_print :字符是否为可打印字符。
  • is_punct :字符是否为标点符号字符。
  • is_upper :字符是否为大写字符。
  • is_xdigit :字符是否为十六进制数字。
  • is_any_of :字符是否是参数字符序列中的任意字符。
  • if_from_range :字符是否位于指定区间内,即from < = ch < = to 。       

4.修剪

修剪算法可以删除字符串开头或结尾部分的空格:

  • trim()、trim_if()、trim_copy()、trim_copy_if()
  • trim_left()、trim_left_if()、trim_left_copy()、trim_left_copy_if()
  • trim_right()、trim_right_if()、trim_right_copy()、trim_right_copy_if()
std::string str2("  hello world | ");
boost::trim_if(str2, boost::is_space() || boost::is_punct());//修建开头、结尾部分的空格、标点符号
std::cout << str2 << std::endl;                    ///hello world

std::string str3("  hello world | ");
boost::trim(str3);//修建开头、结尾部分的空格
std::cout << str3 << std::endl;                   ///hello world |

5.查找

string_algo 提供的查找算法包括下列几种:

  • find_first、ifind_first:查找字符串在输入中第一次出现的位置。
  • find_last、ifind_last:查找字符串在输入中最后一次出现的位置。
  • find_nth、ifind_nth:查找字符串在输入中的第 N 次 ( 从 0 开始计数 ) 出现的位置。
  • find_head:取一个字符串开头 N 个字符的子串,相当于 substr (0,n)。
  • find_tail:取一个字符串末尾 N 个字符的子串。
    std::string str4("D://tal/ABC.txt");    
   
    boost::iterator_range range = boost::find_first(str4, "tal");    
    boost::format fmt("%1%.pos=%2%");
    fmt %range % (range.begin() - str4.begin());
    std::cout << fmt.str() << std::endl;//tal.pos=4

    std::cout << str4.substr(str4.find_last_of('/') + 1, str4.find_last_of('.') - str4.find_last_of('/') - 1) << std::endl;///ABC 

6.替换与删除

        替换、删除操作与查找算法非常接近,是在查找到结果后再对字符串进行处理,所以它们的算法名称很相似,具体如下。

  • replace/erase_first :替换 / 删除字符串在输入中的第一次出现。(支持i_、 _copy)
  • replace/erase_last :替换 / 删除字符串在输入中的最后一次出现。(支持i_、 _copy)
  • replace/erase_nth :替换 / 删除字符串在输入中的第 n 次出现。(支持i_、 _copy)
  • replace/erase_all :替换 / 删除字符串在输入中的所有出现。(支持i_、 _copy)
  • replace/erase_head :替换 / 删除输入的开头。(支持 _copy)
  • replace/erase_tail :替换 / 删除输入的末尾。(支持 _copy)
    std::string str("c://test/readme.json");
    boost::replace_first(str, "readme", "test");
    std::cout << str << std::endl;                       ///C://Test/test.json

    boost::ierase_first(str, "test");
    std::cout << str << std::endl;                       ///c:///test.json
    std::cout << boost::erase_first_copy(str, ".json") << std::endl;///c:///test

7.分割

  • find_all()、ifind_all()
  • split() :只支持以字符分割,不支持以字符串进行分割

        boost中find_all因为其功能而被归类为分割算法,但经过我测试,它并不能分割,而是查找所有匹配的字符串并将其加入容器。

        split()支持四个参数,最后一个参数token_compress_mode_type eCompress 是个枚举对象,可以取值为token_compress_on(常用,当两个分隔符连续出现时,她们将被视为一个分隔符)、token_compress_off(默认,两个连续的分隔符标记了一个空字符串)

    std::string split_str("textLily,Mary,Sarisy,text chinese food");
    std::vector name_vector;
    boost::find_all(name_vector, split_str, "text");
    for (auto a : name_vector)
    {
        std::cout << a << std::endl;//name_vector:两个text
    }
    name_vector.clear();

    boost::split(name_vector, split_str, boost::is_any_of(","));
    for (auto a : name_vector)
    {
        std::cout << a << std::endl;//name_vector:四个元素:textLily、Mary、Sarisy、text chinese food
    }

8.合并

        合并算法 join 是分割算法的逆运算,它把存储在容器中的字符串连接成一个新的字符串,并且可以指定连接的分隔符。join()、join_if()

    std::vector join_str{ "I", "am","a","chinese" };
    std::string joinStr = boost::join(join_str, " ");
    std::cout << joinStr << std::endl;//I am a chinese

9.查找分割迭代器

        除通用的 find_all 或 split 之外, string_algo 库还提供了两个查找迭代器 find_iterator 和
split_iterator ,它们可以在字符串中像迭代器那样遍历匹配,执行查找或分割,无须使用容器来容
纳。这里要特别注意分割迭代器的运用,它可以以任意长度的字符串作为分隔符进行分割,而普通的 split算法则只能以字符作为分隔符。

        使用查找迭代器首先要声明迭代器对象find_iterator 或 split_iterator ,它们的模板类型参数是一个迭代器类型,如 string::iterator 或char * 。

        为了获得迭代器的起始位置,我们需要先调用first_finder函数,它用于判断匹配的对象,再用 make_find_iterator 或 make_split_iterator 来真正地创建迭代器。同族的查找函数还有last_finder 、 nth_finder 、 token_finder 等,它们的含义与查找算法类似,从不同的位置开始查找返回迭代器。

        初始化工作完成后,我们就可以像使用标准迭代器或指针那样,不断地遍历迭代器对象,使用解引用操作符获取查找的内容,直到找不到匹配的对象。

    std::string sss = "I||am||a||chinese,I||love||China";

    boost::find_iterator pos = boost::make_find_iterator(sss, boost::first_finder("||"));
    for (pos; !pos.eof(); ++pos)
    {
        std::cout << *pos << std::endl;
    }
    boost::split_iterator pos1, end1;
    for (pos1 = boost::make_split_iterator(sss, boost::first_finder("||", boost::is_iequal())); pos1 != end1; ++pos1)
    {
        std::cout << *pos1 << std::endl;
    }
//结果
||
||
||
||
||
I
am
a
chinese,I
love
China

五、xpressive

        xpressive是一个灵活且功能强大的正则表达式解析库,它可以构建小型语法,同时提供动态方式与静态方式两种用法。借助正则表达式的强大能力,xpressive能够轻松解决文本处理领域绝大多数的问题,它基本可以替代 string_algo 库。

        但正则表达式本身是一门复杂的技术,要考虑它的调试和维护成本。解决同样的问题, string_algo 可能速度更快,编写的代码也易于理解,所以只有当它们确实无法解决问题时才应该使用正则表达式。

你可能感兴趣的:(boost,boost)