C++字符串处理小结

C++中的字符串类型

常用的C++的字符串类型主要是std::string。它是模板std::basic_string的一个实例化。另外还有三个实例化std::wstringstd::u16stringstd::u32string,不过不是很常用。

std::basic_string<T>
std::string            std::basic_string<char>
std::wstring           std::basic_string<wchar_t>
std::u16string         std::basic_string<char16_t>
std::u32string         std::basic_string<char32_t>

具体可以参考:http://en.cppreference.com/w/cpp/string/basic_string

std::string

标准库中,std::string的成员函数和相关的算法特别多,从上面给出的链接里的内容,粗略计算一下,包括所有的重载函数,也有百余个了。但是在实际的工作使用中,很多时候,总是会感觉,C++对字符串的处理支持实在是弱爆了……感觉这个具有百余个方法的“巨”类用起来总是捉襟见肘。

std::string中的很多操作都是基于迭代器的——这样的话,很多操作,我们都需要先调用find或者直接遍历字符串拿到操作区间的迭代器,然后再进行实际的操作。成员函数中:inserterasereplace都是基于迭代器的操作。

同时,std::string也没有提供一些常用的字符串处理的方法,比如:简单的大小写转换,字符串连接,字符串分割等。

C++11中,提供了std::string的数字和字符串相互转换的算法:

  • 字符串==>数字
    stoi string to int
    stol string to long
    stoll string to long long
    stoul string to unsigned long
    stoull string to unsigned long long
    stof string to float
    stod string to double
    stold string to long double
  • 数字==>字符串
    to_string
    to_wstring

Boost中的字符串处理

Boost库通过算法的形式,提供了一些处理C++字符串的函数,虽然比起Java或者其它一些动态语言还是略显不足,但也算在一定程度上方便了我们对C++的字符串处理。

除了普通的字符串处理算法,Boost库还提供了一个正则表达式的函数库Boost.Regex。Boost.Regex已经被纳入到C++11的标准之中,但是我们常用的g++4.8.x(比如ubuntu14.04默认的g++版本就是4.8.x,公司的g++版本也是4.8.x)的C++标准库还没有实现正则表达式。

实际上,g++4.8.x已经定义了标准库正则表达式的类型和接口,但是只是占了个坑,并没有真正实现……结果可以编译通过,但是运行一直抛出异常。gcc4.9才真正实现了标准库的正则表达式

下面通过例子介绍一个Boost提供的字符串处理算法以及Boost.Regex的用法。

Boost的字符串算法

  • 头文件:#include <boost/algorithm/string.hpp>
  • Boost的很多修改字符串的算法都提供了直接修改传入字符串,名字不带copy返回一个新的字符串,名字带copy两个版本。

字符串大小写转换

C++标准库竟然连一个字符串大小写的转换函数都没有提供。

  • boost::algorithm::to_upper(), boost::algorithm::to_lower()直接修改传入的字符串,将其转换为对应字符串的大写或小写。
  • boost::algorithm::to_upper_copy(), boost::algorithm::to_lower_copy()返回一个新的大写或小写字符串。
  • 例子:
#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
using namespace std;
int main()
{
    std::string s("AbCdefG123 HijkLmn");
    cout << boost::algorithm::to_upper_copy(s) << endl;
    boost::algorithm::to_lower(s);
    cout << s << endl;
}
输出结果:
ABCDEFG123 HIJKLMN
abcdefg123 hijklmn

子串删除。

  • std::string提供了几个erase成员函数,都是基于“位置(下标或迭代器)”的删除:
basic_string& erase(size_type index = 0, size_type count = npos);
iterator erase(iterator position);
iterator erase(const_iterator position);
iterator erase(iterator first, iterator last);
iterator erase(const_iterator first, const_iterator last);
  • STL提供的remove系列的算法,由于其需要与其他容器通用,其删除时的比较函数只能是一个字符之间的比较(std::string中的一个字符相当于vector中的一个元素)。
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value);
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p);
OutputIt remove_copy(InputIt first, InputIt last, OutputIt d_first, const T& value);
OutputIt remove_copy_if(InputIt first, InputIt last, OutputIt d_first, UnaryPredicate p);
  • Boost提供了删除字符串子串的算法。
erase_all()删除主串中所有相等的子串。
erase_first()删除主串中第一个相等的子串。
erase_nth()删除主串中的第n个子串。**注意这里的n是从0开始的。**
erase_head()删除主串的前n个字符。
erase_tail()删除组成的后n个字符。
erase系列的copy版本

例子:

#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
using namespace std;
int main()
{
    std::string s("AbCdefG123 HijkLmn");
    s += s;
    s += s;
    string s0 = s;
    cout << "Input String: " << s0 << endl;
    cout << boost::algorithm::erase_all_copy(s0, "AbC") << endl;
    cout << boost::algorithm::ierase_all_copy(s0, "ABC") << endl;
    cout << boost::algorithm::erase_first_copy(s0, "defG123") << endl;
    cout << boost::algorithm::ierase_first_copy(s0, "DEFG123") << endl;
    cout << boost::algorithm::erase_nth_copy(s0, "HijkLmn", 1) << endl;
    cout << boost::algorithm::ierase_nth_copy(s0, "HIJKLMN", 1) << endl;
    cout << boost::algorithm::erase_head_copy(s0, 3) << endl;
    cout << boost::algorithm::erase_tail_copy(s0, 5) << endl;
}
输出结果:
Input String: AbCdefG123 HijkLmnAbCdefG123 HijkLmnAbCdefG123 HijkLmnAbCdefG123 HijkLmn
defG123 HijkLmndefG123 HijkLmndefG123 HijkLmndefG123 HijkLmn
defG123 HijkLmndefG123 HijkLmndefG123 HijkLmndefG123 HijkLmn
AbC HijkLmnAbCdefG123 HijkLmnAbCdefG123 HijkLmnAbCdefG123 HijkLmn
AbC HijkLmnAbCdefG123 HijkLmnAbCdefG123 HijkLmnAbCdefG123 HijkLmn
AbCdefG123 HijkLmnAbCdefG123 AbCdefG123 HijkLmnAbCdefG123 HijkLmn
AbCdefG123 HijkLmnAbCdefG123 AbCdefG123 HijkLmnAbCdefG123 HijkLmn
defG123 HijkLmnAbCdefG123 HijkLmnAbCdefG123 HijkLmnAbCdefG123 HijkLmn
AbCdefG123 HijkLmnAbCdefG123 HijkLmnAbCdefG123 HijkLmnAbCdefG123 Hi

子串查找

  • Boost的这些字符串的find算法的返回值都是boost::iterator_range类型的一对迭代器。
  • find_first() 查找第一个匹配的子串。std::string::find能实现一样的功能。(find_first的实现应该是封装了这个成员函数,不过个人感觉这个算法用起来更方便。)
  • find_last() 查找最后一个匹配的子串。std::string::rfind能实现一样的功能。
  • find_nth() 查找第n(n>=0)个匹配的字符串。
  • find_head(s, n) 返回字符串的前n个字符。
  • find_tail(s, n) 返回字符串的最后n个字符。

例子:

#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
using namespace std;
int main()
{
    std::string s("AbCdefG123 HijkLmn");
    s += s;
    s += s;
    cout << "Input String: " << s << endl;
    boost::iterator_range<std::string::iterator> itRange = boost::algorithm::find_first(s, "123");
    cout << itRange << endl;
    cout << itRange.begin() - s.begin() << endl;
    cout << itRange.end() - s.begin() << endl;
    itRange = boost::algorithm::find_last(s, "123");
    cout << itRange << endl;
    cout << itRange.begin() - s.begin() << endl;
    cout << itRange.end() - s.begin() << endl;
    itRange = boost::algorithm::find_nth(s, "123", 1);
    cout << itRange << endl;
    cout << itRange.begin() - s.begin() << endl;
    cout << itRange.end() - s.begin() << endl;
    itRange = boost::algorithm::find_head(s, 5);
    cout << itRange << endl;
    cout << itRange.begin() - s.begin() << endl;
    cout << itRange.end() - s.begin() << endl;
    itRange = boost::algorithm::find_tail(s, 5);
    cout << itRange << endl;
    cout << itRange.begin() - s.begin() << endl;
    cout << itRange.end() - s.begin() << endl;
}

输出结果:
123
7
10
123
61
64
123
25
28
AbCde
0
5
jkLmn
67
72

连接字符串

  • Boost库提供了join()算法接受一个字符串容器作为第一个参数,根据第二个参数将这些字符串连接起来。

例子:

#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<string> sVec{"ABC", "def", "GHIJK", "123456"};
    cout << boost::algorithm::join(sVec, "+**+") << endl;
}
输出结果:
ABC+**+def+**+GHIJK+**+123456

替换字符串

replace_first()替换第一个匹配的字符串。
replace_nth()替换第n(n>=0)个匹配的字符串。
replace_last()替换最后一个匹配的字符串。
replace_all()替换所有匹配的字符串。
replace系列的copy版本。

例子:

#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
using namespace std;
int main()
{
    string s("AbcDeFGHIJklmn");
    s += s;
    s += s;
    cout << "Input String: " << s << endl;
    cout << boost::algorithm::replace_all_copy(s, "AbcD", "**") << endl;
    cout << boost::algorithm::replace_first_copy(s, "AbcD", "**") << endl;
    cout << boost::algorithm::replace_last_copy(s, "AbcD", "**") << endl;
    cout << boost::algorithm::replace_nth_copy(s, "AbcD", 1, "**") << endl;
}
输出结果:
Input String: AbcDeFGHIJklmnAbcDeFGHIJklmnAbcDeFGHIJklmnAbcDeFGHIJklmn
**eFGHIJklmn**eFGHIJklmn**eFGHIJklmn**eFGHIJklmn
**eFGHIJklmnAbcDeFGHIJklmnAbcDeFGHIJklmnAbcDeFGHIJklmn
AbcDeFGHIJklmnAbcDeFGHIJklmnAbcDeFGHIJklmn**eFGHIJklmn
AbcDeFGHIJklmn**eFGHIJklmnAbcDeFGHIJklmnAbcDeFGHIJklmn

消除字符串两端的特殊字符

  • 很多时候,我们会希望删除字符左右两边的空白字符。Boost提供了几个算法来实现这个功能。
trim_left()删除字符串左边的空白。
trim_right()删除字符串右边的空白。
trim()删除字符串左右两边的空白。
trim系列的copy版本。
  • 有时候,我们想要删除的不仅仅是字符串左右两边的空白,而是其它一下特定的字符。
trim_left_if()
trim_right_if()
trim_if()
trim_if系列的copy版本,如果`trim_left_copy_if`...
  • Boost库的if系列算法通常传入一个"谓词参数", 如:
is_any_of
is_space 是否是空白字符。
is_alnum是否是字母或数字。
is_alpha是否时字母。
is_cntrl是否控制字符。
is_digit是否十进制数字。
is_graph是否图形字符。
is_lower是否小写字母。
is_print是否可打印字符。
is_punct是否标点符号。
is_upper是否大写字符。
is_xdigit是否十六进制数字。
is_from_range(from, to)是否from <= ch <= to。

例子:

#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
using namespace std;
int main()
{
    string s("   AbcDeF GHIJklmn      ");
    cout << "Input String: <" << s << '>' << endl;
    cout << '<' << boost::algorithm::trim_left_copy(s) << '>' << endl;
    cout << '<' << boost::algorithm::trim_right_copy(s) << '>' << endl;
    cout << '<' << boost::algorithm::trim_copy(s) << '>' << endl;
    cout << endl;
    string s1("==ABCD Efgh=IJK==-==   ");
    cout << "Input String: <" << s1 << '>' << endl;
    cout << '<' << boost::algorithm::trim_copy_if(s, boost::algorithm::is_any_of(" -=")) << '>' << endl;
}
    输出结果:
    Input String: <   AbcDeF GHIJklmn      >
    <AbcDeF GHIJklmn      >
    <   AbcDeF GHIJklmn>
    <AbcDeF GHIJklmn>
    Input String: <==ABCD Efgh=IJK==-==   >
    <AbcDeF GHIJklmn>

匹配比较

starts_with(s, sub) s是否以sub开头, 即前缀。
ends_with(s, sub) s是否以sub结尾, 即后缀。
contains(s, sub) s是否包含sub。

例子:

#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
using namespace std;
int main()
{
    string s("abcdefGHIJKLMN");
    cout << boost::algorithm::starts_with(s, "abcd") << endl;
    cout << boost::algorithm::starts_with(s, "abcD") << endl;
    cout << boost::algorithm::ends_with(s, "MN") << endl;
    cout << boost::algorithm::ends_with(s, "mn") << endl;
    cout << boost::algorithm::contains(s, "efG") << endl;
    cout << boost::algorithm::contains(s, "WWW") << endl;
}
输出结果:
1
0
1
0
1
0

分割字符串

  • Boost库提供了split算法,根据指定的字符集合对字符串进行分割。

例子:

#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    string s("abc 123 cde");
    vector<string> sVec;
    boost::algorithm::split(sVec, s, boost::algorithm::is_space());
    for (auto& str : sVec)
    {
        cout << str << endl;
    }
    cout << "--------分割线--------" << endl;
    s = " abc 123 cde   ";
    boost::algorithm::split(sVec, s, boost::algorithm::is_space());
    for (auto& str : sVec)
    {
        cout << str << endl;
    }
    cout << "--------分割线--------" << endl;
    s = "--abc 123--cde-";
    boost::algorithm::split(sVec, s, boost::algorithm::is_any_of(" -"));
    for (auto& str : sVec)
    {
        cout << str << endl;
    }
}
输出结果:
abc
123
cde
--------分割线--------
//空行
abc
123
cde
//空行
//空行
//空行
--------分割线--------
//空行
//空行
abc
123
//空行
cde
//空行
  • 注: boost的很多(但不是全部)字符串算法都带有忽略大小写的版本,相差只是以'i'开头。

正则表达式

简介

简单地说,Boost提供了三个类型三个算法来处理正则表达式:

  • 三个类型
    • 正则表达式使用boost::regex来表示。
    • 正则表达式的匹配的子串结果使用boost::smatchboost::sub_match来表示。
  • 三个算法
    • 判断整个字符串是否与正则表达式匹配:boost::regex_match()
    • 在字符串中搜索与正则表达式匹配的子串:boost::regex_search()
    • 替换掉字符串中所有与正则表达式匹配的字串:boost::regex_replace()

关于正则表达式的学习,可以参考这篇文章

例子

** 下面通过例子和注释简单说明其用法。**

#include <boost/regex.hpp>
#include <iostream>
#include <string>
using namespace std;
int main()
{
    boost::regex rgx("(\\w+)\\s(\\w+)"); 
    string s("abcd efgh");
    // boost::regex_match() 当字符串和正则表达式<完全匹配>的时候返回true,
    // 否则返回false。
     cout << boost::regex_match(s, rgx) << endl; 
     cout << "========分割线========" << endl;
     // boost::regex_search() 找到第一个和正则表达式匹配的子串则返回true,
     // 具体匹配子串的信息存放在boost::smatch类型的参数里。否则返回false。
     // boost::smatch实际上是持有boost::sub_match的元素的容器。
     // boost::sub_match继承自类std::pair,
     // 对应的匹配子串由first和second成员表示:[first, second)。
     boost::smatch result;
     if (boost::regex_search(s, result, rgx))
     {
         for (size_t i = 0; i < result.size(); ++i)
        {
            //result[0] 正则表达式的匹配结果。
            //result[1] 第一个分组的匹配结果。
            //result[2] 第二个分组的匹配结果。
            cout << result[i] << endl;
         }
     }
     cout << "========分割线========" << endl;
 
     rgx = "(\\w+)\\s\\w+";
     if (boost::regex_search(s, result, rgx))
     {
         for (size_t i = 0; i < result.size(); ++i)
         {
             //result[0] 正则表达式的匹配结果
             //result[1] 分组的匹配结果
             cout << result[i] << endl;
         }
     }
     cout << "========分割线========" << endl;
 
     rgx = "\\w+\\s(\\w+)";
     if (boost::regex_search(s, result, rgx))
     {
         for (size_t i = 0; i < result.size(); ++i)
         {
            cout << result[i] << endl;
         }
     }
     cout << "========分割线========" << endl;
     rgx = "\\w+\\s\\w+";
     if (boost::regex_search(s, result, rgx))
     {
         for (size_t i = 0; i < result.size(); ++i)
         {
              cout << result[i] << endl;
          }
      }
      cout << "========分割线========" << endl;
      rgx = "(\\d+)\\s(\\w+)";
      if (boost::regex_search(s, result, rgx))
      {
         for (size_t i = 0; i < result.size(); ++i)
         {
             cout << result[i] << endl;
         }
      }
      cout << "========分割线========" << endl;
      // 遍历正则匹配整个字符串。
      s = "abcd efgh ijk www";
      rgx = "\\w+\\s\\w+";
      auto begin = s.cbegin();
      auto end = s.cend();
      while (boost::regex_search(begin, end, result, rgx))
      {
           cout << result[0] << endl;
           begin = result[0].second;
      }
      cout << "========分割线========" << endl;

    // boost::regex_replace() 替换掉字符串中<所有>匹配的子串。

    //结果输出到一个Output Iterator。
     boost::regex_replace(std::ostreambuf_iterator<char>(std::cout), s.cbegin(), s.cend(), rgx, "666666");
     cout << endl;
      //直接返回结果
     cout << boost::regex_replace(s, rgx, "2233333") << endl; //每一个匹配
}

输出结果:
1
========分割线========
abcd efgh
abcd
efgh
========分割线========
abcd efgh
abcd
========分割线========
abcd efgh
efgh
========分割线========
abcd efgh
========分割线========
========分割线========
abcd efgh
ijk www
========分割线========
666666 666666
2233333 2233333
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容