C++字符串自制常用工具函数(格式化组装、各类型转字符串、拆分数组、替换子串、去除字符、大小写转换)

字符串格式化组装通用函数

C++对字符串组装没有一个很直接好用的函数,这里利用C的snprintf()函数,提供一个可用的函数:

template<typename ... Args>
std::string stringFormat(const std::string& format, Args ... args ) {
    size_t size = (size_t)snprintf( NULL, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
#ifdef C11
    std::unique_ptr<char> buf(new char[size]);
#else
    std::auto_ptr<char> buf(new char[size]);
#endif
    snprintf( buf.get(), size, format.c_str(), args ... );
    return std::string(buf.get(), size - 1); // We don't want the '\0' inside
}

这里的stringFormat函数是一个模板函数,可以接受多种形式的格式化组装,也就是可以拼接int、float、long、string等各种类型的变量。

之所以这里的模板参数和函数的最后一个参数都是省略号,是C允许的一种参数表示形式,必须放在最后一个,且必须前面有确定的参数,它表示后续的参数个数不定。这里配合模板,也就是参数的个数和类型都不定了。所以我们可以用来组装任何类型的变量。

snprintf()也是C的一个函数,用法如下:

int snprintf(char *str, int n, char * format [, argument, ...]);

参数中:

  • str:目的地址,用来存组装后的char数组地址;
  • n:保留的字符个数(不包含最后的'\0'),这里需要注意,不管后面组装了多少字符,最终结果只会保留这里的n个字符,再在结尾加上一个'\0'表示结束;
  • format:格式char数组,也就是我们常用的类似“hello %s”这样的待组装格式了;
  • argument...:不定个数的参数,用来适配格式char数组需要的变量。

返回值:返回组装后的本应有的char数组长度,不包括最后的'\0'。注意并不是n的数值,否则这个返回没有意义,这里返回的是本应有的char数组长度,也就是format组装好变量后的全长,而n相当于是设置要截取前面的多少个字符赋给str。

这样就清楚了,这里我们的目的地址放了NULL,保留的字符个数又是0,所以没有要截取保留的str,只是单纯计算一下组装所需要的长度,因为函数返回不包括'\0',所以这里要加一。

然后我们创建一个char类型的数组,用算好的长度去初始化。根据编译器的C++版本不同,使用唯一指针或者自动指针。唯一指针是C++11的特性,同一对象只能被一个unique_ptr来拥有,禁止进行拷贝构造和赋值构造操作。当unique_ptr指针对象离开其作用域时,生命期结束,自动使用内部给定的删除器(deleter)delete所指向的对象。所以函数结束后,其申请的资源会自动删除。

创建好char数组后,我们就进行实际的组装,再次使用snprintf函数,这次我们知道了需要的长度就是我们前面计算出来的长度,将前面创建的char数组放到目的char数组的参数位置,进行组装。前面要计算一次长度的原因就是因为我们并不知道实际使用的时候会组装多长的字符串,如果随意创建一个长度的char数组,要么浪费,要么不够。

最后,我们用组装后的结果char数组来初始化字符串,并返回,这里只要前面的实际字符,不要最后的'\0'。

数值类型转字符串

C++11以前没有直接的数值类型转字符串的函数,这里提供一些:

std::string itoString(int i) {
    char buf[30] = {0};
    sprintf(buf, "%d", i);
    return std::string(buf);
}

std::string ltoString(long i) {
    char buf[30] = {0};
    sprintf(buf, "%ld", i);
    return std::string(buf);
}

std::string lltoString(long long i) {
    char buf[30] = {0};
    sprintf(buf, "%lld", i);
    return std::string(buf);
}

其实都是利用sprintf函数来做格式化,将数值类型转为char数组,再转为string类型返回。

各类型转String

还有一种更通用的转String 的方法:

template <class T>
static string ToString(const T& tmp)
{
    stringstream ss;
    ss << tmp;
    return ss.str();
}

做成模板函数,利用stringstream,来接收各种类型的参数,返回字符串。

字符串根据特定字符拆分成数组通用函数

split是其他语言中将字符串转化为数组的常用函数,C++中却没有,这里提供一个通用函数,可以将字符串根据特定字符拆分成数组:

#include <string>
#include <vector>

using std::string;
using std::vector;

vector<string> split(const string &str, const string &separtor) {
    size_t begin = 0, end = 0;
    vector<string> result;

    while (true) {
        end = str.find(separtor, begin);
        if (end == string::npos) {
            result.push_back(str.substr(begin));
            break;
        } else {
            result.push_back(str.substr(begin, end-begin));
            begin = end + separtor.size();
        }
    }
    return result;
}

函数接收要拆分的字符串和指定的分隔符字符串,都是const形式,因为不想对其做更改。返回拆分好的数组,也就是string类型的vector。

初始化需要的变量后,在无限循环中,使用string的find函数来找分隔符出现的位置,第二个参数是指开始找的位置,这里一开始是0。find函数会返回第一次找到的位置,如果找不到,会返回string::npos,这里的npos一般是一个size_t的最大值,在字符串中就是字符串的最后位置。

所以下面如果是string::npos,那就表示在begin位置后找不到了,直接从begin开始截取子串直到字符串的最后位置,放到数组中去。

如果不是,说明找到了,因此从begin开始截取需要的长度,长度由end-begin计算出来。substr函数接受截取的开始位置和长度,长度默认为最大值,也就是到直到字符串末尾。截取完后,再更新begin到分隔符后的位置,方便下一次寻找。

最后返回分割后的字符串。

替换字符串中某个子串

将字符串中某个子串全部替换为另一个子串:

std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length();
     }
     return str;
}

做法就是不断在字符串中找到要被替换的子串,得到位置后,用replace函数替换成目的子串,直到找不到为止。注意该函数并没有改变源字符串,而是复制了实参,修改后返回。

去空格(或其他字符)

去除字符串中的空格,这个用上面的函数也能实现,不过这相当于是去除某种字符的通用函数了:

std::string Trim(const std::string& str, const char target = ' ') {
    string retStr = "";
    for (size_t i = 0, n = str.length(); i < n; ++i) {
        if (str[i] != target) {
            retStr += str[i];
        }
    }
    return retStr;
}

简单的遍历判断,默认为去除空格,也可以去除其他的字符。

大小写转换

将字符串中的字母全部转为大写或者全部转为小写:

void toUpperCase(string &s) {
    for (string::iterator it = s.begin(); it != s.end(); it++) {
        char c = (char)std::toupper(*it);
        *it = c;
    }
}

void toLowerCase(string &s) {
    for (string::iterator it = s.begin(); it != s.end(); it++) {
        char c = (char)std::tolower(*it);
        *it = c;
    }
}

利用toupper/tolower函数,用迭代器遍历每个字符,进行修改。这里改的是原字符串,不需要返回新字符串。

toupper/tolower函数源码本身只会对属于字母的字符进行修改,非字母字符会原样返回,所以不需要担心字符串中包含非字母的字符。


查看作者首页

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