C++中 字符串的常见操作

字符串分割

在一些比较流行的语言中,字符串分割是一个比较重要的方法,不论是在python,java这样的系统级语言还是js这样的前端脚本都会在用到字符串的分割,然而在c++中却没有这样的方法用来调用。但是在boost中却提供分割方法。

使用vector实现

下面是用vector实现的一个简单的split函数,借助string::find函数查找匹配子串的位置,然后截取剩下的字符串之后继续处理,实现对原字符串的完整处理。

本文福利, 免费领取C++学习资料包、技术视频/代码,内容包括(C++基础,网络编程,数据库,中间件,后端开发,音视频开发,Qt开发)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

vector<string> split(string &s, string &delim, bool removeEmpty = false, bool fullMatch = false) {

    vector<string> result;

    string::size_type start = 0, skip = 1;

 

    // 使用整个字符串进行查找,之后会直接跳过整个子字符串

    if (fullMatch) {

        skip = delim.length();

    }

 

    while (start != string::npos) {

        // 从start位置开始查找子字符串delim第一次出现的位置

        string::size_type finsh = s.find(delim, start);

 

        if (skip == 0) {

            finsh = string::npos;

        }

 

        // 从start开始到(finsh - start)处获得子字符串

        // 获得匹配字符串之前的字符串

        string token = s.substr(start, finsh - start);

 

        // 对token进行判断并决定是否移除空

        if (!(removeEmpty && token.empty())) {

            // 将匹配字符串之前的字符串放进向量中

            result.push_back(token);

        }

 

        // 判断此时是否到了原字符串的末尾

        if ((start = finsh) != string::npos) {

            // 将子字符串放进向量中,是为了保证原字符串的字符不丢失

            result.push_back(delim);

            // 将查找位置向前移动skip个位置

            start = start + skip;

        }

    }

 

    return result;

}

关于字符串的输入方法: std::cin , 多个单词使用函数std::getline(std::cin, s)

这个函数接受两个参数:一个输入流对象和一个 string 对象。getline 函数从输入流的下一行读取,并保存读取的内容到不包括换行符。和输入操作符不一样的是,getline 并不忽略行开头的换行符。只要 getline 遇到换行符,即便它是输入的第一个字符,getline 也将停止读入并返回。如果第一个字符就是换行符,则 string 参数将被置为空 string。

由于getline函数返回时丢弃换行符,换行符将不会存储在string对象中。

String的操作方法

s.empty()

Returns true if s is empty; otherwise returns false

如果 s 为空串,则返回 true,否则返回 false。

s.size()

Returns number of characters in s

返回 s 中字符的个数

s[n]

Returns the character at position n in s; positions start at 0.

返回 s 中位置为 n 的字符, 位置从 0 开始计数

【注意:1、引用下标时如果超出下标作用范围就会引起溢出错误。同样不会报错!2、索引的实际数据类型是类型 unsigned 类型string::size_type。】

#include <iostream>

#include <string>

 

int main()

{

std::string s = "hello world";

std::cout<<s<<std::endl;

for (std::string::size_type ix = 0; ix != s.size(); ++ix)

s[ix] = '*';

    std::cout<<"Now s is:"<<s<<std::endl;

    std::cout<<"s's len is:"<<s.size()<<", s[12]="<<s[100]<<std::endl;

    return 0;

}

注意:循环中使用了 std::string::size_type ix = 0;请使用string内置类型size_type来操作。 因为int型可能不够string的长度,所以内置类型size_type(实际可以认为是 unsigned)被创建,保证各机器的兼容性, 避免溢出(和下标溢出可不是一回事)。 任何存储 string 的 size 操作结果的变量必须为 string::size_type 类型。特别重要的是,还要把 size 的返回值赋给一个 int 变量。

s1 + s2
Returns a string equal to the concatenation of s1 and s2

把 s1 和s2 连接成一个新字符串,返回新生成的字符串

【备注: 可以连续加,和Python类似。string s3 = s1 + ", " + s2 + "\n";。注意:当进行 string 对象和字符串字面值混合连接操作时,+ 操作符的左右操作数必须至少有一个是 string 类型的【想象下级联也就知道这确实是有道理的】。----1、也就是说+连接必须保证前两个有一个为string类型!2、字符串字面值不能直接相加,字符串字面值和string是不同类型的,字符串里面没有空字符'\0'。】

s1 = s2
Replaces characters in s1 by a copy of s2

把 s1 内容替换为 s2 的副本

【备注:。它必须先把 s1 占用的相关内存释放掉,然后再分配给 s2 足够存放 s2 副本的内存空间,最后把 s2 中的所有字符复制到新分配的内存空间。】

v1 == v2

Returns true if v1 and v2 are equal; false otherwise

比较 v1 与 v2 的内容,相等则返回 true,否则返回 false

!=, <, <=, >, and >=

Have their normal meanings

保持这些操作符惯有的含义

cctype Functions

我们经常要对 string 对象中的单个字符进行处理,例如,通常需要知道某个特殊字符是否为空白字符、字母或数字。以下 列出了各种字符操作函数,适用于 string 对象的字符(或其他任何 char 值)。这些函数都在cctype 头文件中定义。

isalnum(c)

True if c is a letter or a digit.如果 c 是字母或数字,则为 True。

isalpha(c)

true if c is a letter.如果 c 是字母,则为 true。

iscntrl(c)

true if c is a control character.如果 c 是控制字符,则为 true

isdigit(c)

true if c is a digit.如果 c 是数字,则为 true。

isgraph(c)

true if c is not a space but is printable.如果 c 不是空格,但可打印,则为 true。

islower(c)

true if c is a lowercase letter.如果 c 是小写字母,则为 true。

isprint(c)

True if c is a printable character.如果 c 是可打印的字符,则为 true。

【注意:可打印的字符是指那些可以表示的字符】

ispunct(c)

True if c is a punctuation character.如果 c 是标点符号,则 true。

【注意:标点符号则是除了数字、字母或(可打印的)空白字符(如空格)以外的其他可打印字符】

isspace(c)

true if c is whitespace.如果 c 是空白字符,则为 true。

【注意:空白字符则是空格、制表符、垂直制表符、回车符、换行符和进纸符中的任意一种】

isupper(c)

True if c is an uppercase letter.如果 c 是大写字母,则 true。

isxdigit(c)

true if c is a hexadecimal digit.如果是 c 十六进制数,则为 true。

tolower(c)

If c is an uppercase letter, returns its lowercase equivalent; otherwise returns c unchanged.如果 c 大写字母,返回其小写字母形式,否则直接返回 c。

toupper(c)

If c is a lowercase letter, returns its uppercase equivalent; otherwise returns c unchanged.如果 c 是小写字母,则返回其大写字母形式,否则直接返回 c。

【注意:ctype.h是定义在C标准库中的头文件,cctype 其实就是利用了 C 标准库函数。C 标准库头文件命名形式为 name 而 C++ 版本则命名为 cname ,少了后缀,.h而在头文件名前加了 c 表示这个头文件源自 C 标准库。因此,cctype 与 ctype.h 文件的内容是一样的,只是采用了更适合 C++程序的形式。特别地,cname 头文件中定义的名字都定义在命名空间 std 内,而 .h 版本中的名字却不是这样。通常,C++ 程序中应采用 cname 这种头文件的版本,而不采用 name.h 版本,这样,标准库中的名字在命名空间 std 中保持一致。使用 .h 版本会给程序员带来负担,因为他们必须记得哪些标准库名字是从 C 继承来的,而哪些是 C++ 所特有的。】

字符串操作

其中:s和str是字符串string,ca是一个字符数组,str_ca是一个字符串或者一个字符数组,str_ca_ch是一个字符串、字符数组或一个字符,ch是一个字符,n、n1、n2、pos1、pos2是整数。

长度操作

s.capacity()

返回s获取的存储容量;

s.size()/s.length()

返回s的长度;

s.empty()

如果s没有包含字符则返回true,否则返回false;

s.max_size()

返回s可能的最大长度;

编辑操作

s.append(str_ca)

将str_ca添加到s的结尾,返回s;

s.append(ca, n)

将ca的前n个字符添加到s的结尾,返回s;

s.append(n, ch)

将ch的n份拷贝添加到s的结尾,返回s;

s.insert(pos, str)

将str的拷贝插入到s的pos位置,返回s;

s.insert(pos1, str, pos2, n)

将str中从pos2位置开始的n个字符插入到s的pos1位置,返回s;【如果n大于str的长度,不会有问题,没有溢出错误,只会复制到str的末尾】

s.insert(pos, ca, n)

将ca的前n个字符插入到s的pos位置,如果n被省略,则插入ca中所有的字符到pos位置,返回s;

s.insert(pos, n, ch)

将字符ch的n个拷贝插入到s的pos位置,返回s;

s.erase(pos, n)

删除s中从pos开始的n个字符(默认pos为0),返回s;

s.replace(pos1, n1, str)

将s中pos1位置开始的长度为n1的字符串替换为str【如果n1太大,从pos到s结尾的所有字符将被替换】,返回s;

s.replace(pos1, n1, ca, n2)

和上面一样,只不过取ca的前n2个字符,返回s;

s.swap(str)/swap(s, str)

交换s和str的内容分,返回为void;

复制操作

支持+ +=操作符。

s.assign(str_ca)

将str_ca的一份拷贝赋予s,返回s;

s.assign(ca, n)

将ca的前n个字符构成的字符串赋予s,返回s;

s.assign(n, ch)

将n个ch组成的字符串赋予s,返回s;

s.substr(pos, n)

返回s中从pos(默认为0)开始的,有n个字符组成的s的子串的拷贝;

查找操作

s.find(str_ca_ch, pos)

返回s中第一个大于等于pos的位置,并且从这个位置的下一个字符开始s中字符和str_ca_ch中相应字符匹配。如果没有这样的位置则返回npos,pos默认为0

s.find_first_of(str_ca_ch, pos)

返回s中大于等于pos的第一个和str_ca_ch中任意字符匹配的字符的位置,如果没有这样的位置则返回npos,pos的默认值为0

s.find_first_not_of(str_ca_ch, pos)

返回s中大于等于pos的第一个和str_ca_ch中任意字符都不匹配的字符的位置,如果没有这样的位置则返回npos,pos的默认值为0

s.find_last_of(str_ca_ch, pos)

返回s中小于等于pos的最大的一个和str_ca_ch中任意字符匹配的字符的位置,如果没有这样的位置则返回npos,pos的默认值为0

s.find_last_not(str_ca_ch, pos)

返回s中小于等于pos的最大的一个和str_ca_ch中任意字符都不匹配的字符的位置,如果没有这样的位置则返回npos,pos的默认值为0

s.rfind(str_ca_ch, pos)

返回s中最后一个小于等于pos的位置,并且从这个位置开始的str_ca_ch.size()个字符和str_ca_ch中相应的字符匹配。如果没有这样的位置则返回npos,pos默认为npos

比较操作

支持上述 <、<=、>、>=、==、!=操作符。

s.compare(str_ca)

返回值为正、0、负

string和C风格字符串的转换

s.c_str()

返回一个常字符数组,这个数组包含存储在s中字符,以一个空字符结束;

s.data()

返回一个常字符数组,这个数组包含存储在s中字符,但没有以空字符结束;

s.copy(charArray, pos, n)

将charArray替换为s中从pos开始的n个字符,如果pos被省略了,即从0开始,如果n太大,那么拷贝字符直到s结束,返回最终拷贝的字符个数。

备注:data()和c_str()都可以被用来从一个文件名中提取open操作所需要的字符数组。

#include <iostream>

#include <string>

#include <vector>

using std::cin; using std::cout; using std::endl; using std::string; using std::vector;

 

string deal_word(string word)

{

// 使用c++11 auto 语句 以及range for 语句

for(auto &c : word)

{

if (not ispunct(c))

{

c = toupper(c); //连接非标点字符到字符串

}

else

{

word.erase(word.size()-1, 1); //只能删除最后一个标点符号。有局限性!

}

}

return word;

}

 

string deal_word2(string word)

{

// 使用下标及c++11 decltype

    for (decltype(word.size()) index = 0; index != word.size(); ++index)

    {

    if (not ispunct(word[index]))

{

    word[index] = toupper(word[index]);

}

    else

    {

    word.erase(index, 1); // 删除指定位置上的某一个字符,在此为标点

    index -= 1; //保证下标不越界!重要!

    }

    }

    return word;

}

 

int main()

{

string word; // 缓存输入的单词

vector<string> text; // empty vector

cout<<"Please input the text:"<<endl; //提示输入

while (std::cin >> word and word != "INPUTOVER") // INPUTOVER 用于标示输入结束,也可以ctrl + z停止输入

{

        word = deal_word(word); // 单词处理

text.push_back(word); // append word to text

}

for(std::vector<int>::size_type ix =0, j = 0; ix != text.size(); ++ix, ++j)

{

if (j==8) // 8个单词一行

{

cout<<endl; //换行

j = 0; //重新计数

}

    cout<<text[ix]<<" "; //加空格!

}

    return 0;

}

改写了两种处理单词的方法。使用了c++11中的新特性!

str.erase()方法:

1、erase(pos, n); 删除从pos开始的n个字符,比如erase(0,1)就是删除第一个字符

2、erase(position); 删除position处的一个字符(position是个string类型的迭代器)

3、erase(first, last); 删除从first到last之间的字符(first和last都是迭代器)

c++中c_str()的用法详解

//标准库的string类提供了三个成员函数来从一个string得到c类型的字符数组
//主要介绍c_str
//c_str():生成一个const char*指针,指向以空字符终止的数组。
//这个数组应该是string类内部的数组
#include <iostream>
//需要包含cstring的字符串
#include <cstring>
using namespace std;
 
int main()
{
    //string-->char*
    //c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同
 
    //这个数组的数据是临时的,当有一个改变这些数据的成员函数被调用后,其中的数据就会失效。
    //因此要么现用先转换,要么把它的数据复制到用户自己可以管理的内存中
    const char *c;
    string s = "1234";
    c = s.c_str();
    cout<<c<<endl;
    s = "abcde";
    cout<<c<<endl;
}

结果是:

 

上面如果继续用c指针的话,导致的错误将是不可想象的。就如:1234变为abcde

其实上面的c = s.c_str(); 不是一个好习惯。既然c指针指向的内容容易失效,我们就应该按照上面的方法,那怎么把数据复制出来呢?这就要用到strcpy等函数(推荐)。

//标准库的string类提供了三个成员函数来从一个string得到c类型的字符数组
//主要介绍c_str
//c_str():生成一个const char*指针,指向以空字符终止的数组。
//这个数组应该是string类内部的数组
#include <iostream>
//需要包含cstring的字符串
#include <cstring>
using namespace std;
 
int main()
{
    //更好的方法是将string数组中的内容复制出来 所以会用到strcpy()这个函数
    char *c = new char[20];
    string s = "1234";
    // c_str()返回一个客户程序可读不可改的指向字符数组的指针,不需要手动释放或删除这个指针。
    strcpy(c,s.c_str());
    cout<<c<<endl;
    s = "abcd";
    cout<<c<<endl;
  
}

vector<string> split(string &s, string &delim, bool removeEmpty = false, bool fullMatch = false) {

vector<string> result;

string::size_type start = 0, skip = 1; // 使用整个字符串进行查找,之后会直接跳过整个子字符串

if (fullMatch) { skip = delim.length(); }

while (start != string::npos) { // 从start位置开始查找子字符串delim第一次出现的位置

string::size_type finsh = s.find(delim, start);

if (skip == 0) { finsh = string::npos; } // 从start开始到(finsh - start)处获得子字符串// 获得匹配字符串之前的字符串

string token = s.substr(start, finsh - start); // 对token进行判断并决定是否移除空

if (!(removeEmpty && token.empty())) { // 将匹配字符串之前的字符串放进向量中

result.push_back(token); } // 判断此时是否到了原字符串的末尾

if ((start = finsh) != string::npos) { // 将子字符串放进向量中,是为了保证原字符串的字符不丢失

result.push_back(delim); // 将查找位置向前移动skip个位置

start = start + skip; } }

return result;

}

本文福利, 免费领取C++学习资料包、技术视频/代码,内容包括(C++基础,网络编程,数据库,中间件,后端开发,音视频开发,Qt开发)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值