字符串 in C/C++

文章首次写于:2023-04-19

更新日志:

  • 20230419:编写了字符串-C、字符串-C++的输入输出

  • 20230916:添加了< String >库、< cctype >库、字符串流 < sstream >库


输入输出

char*

输入

gets(c*)(于C++11取消)

读取至'\n'EOF停止,换行符不会复制,返回自动添加\0

fgets(c*,Num,Stream)

读取num-1'\n'EOF停止,**\n复制到字符串中**,第n个字符不读取,返回自动添加\0

fgets()gets()完全不同,fgets()包含任何结束换行符

默认标准输入流:stdin

getchar()

返回读取字符,常用于抵消'\n',与getc(stdin)等价

输出

puts()

puts(c*)输出时在末尾添加'\n'

fputs()

fputs(c*,stdout)输出直至'\0',不会写入额外字符;

string

输入

std::cin

1
cin>>s;

读到任意空白符('\n','\t','\0',' ')停止

getline()

getline(cin,string,delim)定界字符delim默认'\n'

定界字符提取但不储存,下次读取跳过。

输出

cout<<

1
cout<<s;

C++ String 特性

< String >库

常用操作包括:拼接、索引、substr()字串、字典序、长度/大小;

C++除了面向对象语言外,还提供了一些操作符 += 、 [] 等(包括STL通有接口size())

声明

1
2
string s;
string ss[10];

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
string s;                       // 默认初始化,一个空白的字符串
string s1(s); // s1是s的副本
string s2(10, '4'); // s2初始化
string s3 = string(10, 'c'); // 对象语言

char cs[] = "12345";
string s4(cs, 3); // 复制字符串cs的前三个字符到s当中

string s5 = "abcde";
string s6(s8, 2);

string s7 = "asdsfasdgf";
string s8(s10, 3, 4); // s8是s10标s开始4个字符的拷贝,超出s10.size出现未定义

string类型的读入

1
2
3
string s;
cin >> s; //不能读入空格,以空格,制表符,回车符作为结束标志
getline(cin, s); //可以读入空格和制表符,以回车符作为结束的标志

字符串末尾添加字符

可以用+号和append()函数在函数的末尾添加字符。

1
2
3
string s;
s += 'a';
s.append("a");

sting类型变量的访问

string字符串变量访问可以采用at, operator[]来访问指定index对应的字符。其中at有越界检查,如果index越界,无论Debug还是在Release编译的环境下,程序异常跳出执行;operator[]无越界检查,如果index越界,则会取得不可预知的字符。

1
2
string s("abcd");
cout << s[0] << s.at(2) << endl;

string类型的长度

1
2
3
string s = "Hello, world!"
int len = s.size();
int len = s.length(); //这两种方式是等价的

查找字符串的子字符串

1
2
string s = abcdefg, subs = "efg";
int pos = s.find(subs); // 如果找到子字符串则返回首次匹配的位置,否则返回string::npos

指向首字符和末尾字符的迭代器(begin,end)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
string s("abcdefg");
for(string::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it;
}

//逆向迭代器
for (string::iterator it = s.rbegin(); it != s.rend(); it++)
   { cout << *it;  }

//采用auto实现迭代器

for(auto itr : s)
{
cout << itr << endl;
}

img

empty(), clear()

empty()可以用来检查字符串是否为空,clear()用来清空字符串。

1
2
3
4
5
string s1 = "012345";
if(!s1.empty()){
cout << s1.length << endl;
s1.clear();
}

insert()在指定index处插入字符或字符串

1
2
3
4
5
// insert原型函数,在index插入count个字符ch。
// insert(size_type index, size_type count, char ch)

string s1 = "abc";
s1.insert(1, 2, 'D') // "aDDbc"

erase()删除字符

erase()函数有三个原型:

1
2
3
string& erase(size_t pos = 0, size_t n = npos);
iterator erase(iterator position);
iterator erase(iterator first, iterator last);

因此erase()总共有三种用法:

1
2
3
erase(pos, n);      //删除从pos开始的n个字符,比如erase(0, 1)就是删除第一个字符
erase(position); //删除从position处的一个字符(position是个string类型的迭代器)
erase(first, last); //删除从first到last之间的字符(first和last都是迭代器)

应用实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string str ("This is an example phrase.");
string::iterator it;

// 第(1)种用法
str.erase (10,8);
cout << str << endl; // "This is an phrase."

// 第(2)种用法
it=str.begin()+9;
str.erase (it);
cout << str << endl; // "This is a phrase."

// 第(3)种用法
str.erase (str.begin()+5, str.end()-7);
cout << str << endl; // "This phrase."

获取子字符串substr()

  • 函数原型: string substr(size_t pos = 0, size_t len = npos) const;

  • 功能: 从子字符串中获取想要的子字符串

  • 参数:

    • pos: 想要获取的子字符串的第一个字符的位置,如果pos等于字符串长度,则该函数返回一个空字符串,如果等于字符串长度,则该函数返回一个空的字符串,如果该长度大于字符串长度,则抛出一个out_of_range。 注:第一个字符的下标从0开始。
    • len: 子字符串中要包含的字符数, string::npos的值表示知道字符串末尾的所有字符。
  • 返回值:带有对象子字符串的字符串对象。

应用实例:

1
2
3
string s1 = "Hello World"
string s2 = s1.substr(3, 5);
string s3 = s1.substr(3, str::npos); //截取从下标3到结束的子字符串

比较

1
2
3
4
s1 == s2
!= < >
<= >=
bool ans = s1.compare(s2);

< cctype >库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int isalnum(int c); //检查字符是否为字母数字
int isalpha(int c); //检查字符是否为字母
int isblank(int c); //检查字符是否为空
int iscntrl(int c); //检查字符是否是控制字符
int isdigit(int c); //检查字符是否为十进制数字
int isgraph(int c); //检查字符是否具有图形表示
int islower(int c); //检查字符是否为小写字母
int isprint(int c); //检查字符是否可打印
int ispunct(int c); //检查字符是否是标点符号
int isspace(int c); //检查字符是否为空格
int isupper(int c); //检查字符是否为大写字母
int isxdigit(int c);//查字符是否为十六进制数字
int tolower(int c); //将大写字母转换为小写
int toupper(int c); //将小写字母转换为大写(

字符串流 < sstream >

引用声明:以下关于stringstream的部分来自文章C++字符串流stringstream与string知识介绍与用法小结

stringstream是 C++ 提供的一个字符串流(stream),和之前学过的iostream、fstream有类似的操作方式,要使用stringstream,必须包含其头文件:

1
2
3
#include <sstream>
using namespace std;
stringstream ss;

< sstream > 库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本。一般情况下使用stringstream就足够,因为字符串要频繁的涉及到输入输出。

< sstream > 使用string对象来代替字符数组,这样可以避免缓冲区溢出的危险。而且,传入参数和目标对象的类型被自动推导出来,即便使用了不正确的格式化符也没有危险。

与文件流fstream类似,通过插入器(<<)和析取器(>>)这两个运算符可以直接对stringstream上的数据输入输出,而将stringstream中的全部数据输出则是使用成员函数str(),其有两种形式: 1、void str() //无参形式,用于将stringstream流中的数据以string字符串的形式输出 2、void str (const string& s)//以字符串为参数,用以覆盖stringstream流中的数据 特别需要注意的是:

1
2
// 字符串流清零,将流中的数据全部清除
ss.str("");

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{

stringstream ss;
ss << "hello ";
ss << "world!";

std::cout << ss.str() << std::endl;
// 对stringstream而言,operator<< 是一直往字符串流中写字符
// 而不是覆盖之前输入的字符,这一点不同于str()成员函数方法,str()函数创建的是一个临时的string对象
//这个string对象在函数str()语句结束就会被销毁,因而一般使用时应先声明一个string对象s,将str()赋值给s
//const string s=ss.str();//这样会有一个string拷贝的过程
/*又或者是const string& s=ss.str();这样就是用s直接引用了ss.str()这个临时变量,就“延长了临时变量ss.str()的 生命周期”,使得ss.str()生命结束时刻和s一样*/
return 0;
}

ss.clear()成员函数

同文件流fstream中的clear()函数类似,通过clear()成员函数可以清除流的错误状态,主要用在stringstream重复使用时或者多个stringstream对象构造时清空,不然之前的缓冲就停留在输入输出流中。

1
2
3
4
5
6
ss.setstate(std::ios::eofbit);//设置流的状态标志位
std::cout << ss.rdstate() << std::endl;//获取当前流的状态标志位
// 结果为1
ss.clear();
std::cout << ss.rdstate() << std::endl;
// 结果为0

在对同一个stringstream对象重复赋值,就需要先对流使用clear()函数清空流的状态,此时流占用的内存没有改变,会一直增加(stringstream不主动释放内存),若想改变内存(一般是清除内存,减少内存消耗),需要再配合使用str(“”)清空stringstream的缓存。

1
2
3
4
5
6
7
8
9
10
11
stringstream stream;
int a,b;
ss<<"80";//向流输出数据(写入)
ss>>a;//从流输入数据到a
cout<<"Size of ss = "<<ss.str().length()<<endl;//ss.str()返回一个string对象,再调用其成员函数length()
ss.clear();//清空流
ss.str("");//清空流缓存
cout<<"Size of ss = "<<ss.str().length()<<endl;
ss<<"90";//重新赋值
ss>>b;
cout<<"Size of ss = "<<ss.str().length()<<endl;

运行结果:

1
2
3
4
5
Size of ss = 2
Size of ss = 0
Size of ss = 2
80
90

stringstream与fstream

通过重载的<<和>>运算符可以将文件流中的数据输出到C++字符串中,它们之间的媒介是缓冲区streambuf,可由流的成员函数rdbuf()读取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <sstream>//stringstream流的头文件
#include <fstream>
using namespace std;
int main()
{

string str;
ifstream in;
in.open("Hello.txt");
//读取文件的缓冲内容到数据流中
stringstream ss;
ss << in.rdbuf();
in.close();//关闭文件
str = ss.str();//将stringstream流中的数据赋值给string类型字符串
const char* p = str.c_str();//将字符串内容转化为C_string类型
return 0;
}

stringstream通常是用来做数据转换的,用于字符串与其他变量类型的转换,相比c库的转换,它更加安全,自动和直接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{

string str1("How are you? 123 1 4.368");
stringstream ss(str1);//构造函数初始化
cout<<ss.str()<<endl;
string str2;
for(int i=0;i<3;i++)
{

ss>>str2;
cout<<str2<<" ";
}
cout<<endl; //先换行
int a;
ss>>a;
cout<<a<<endl;
bool b;
ss>>b;
cout<<b<<endl;
float c;
ss>>c;
cout<<c<<endl;
ss.clear();
ss.str("I am fine!");//重新赋值
while(ss>>str2)//不断读取
{

cout<<str2<<" ";
}
}

运行结果:

1
2
3
4
5
6
How are you? 123 1 4.368
How are you?
123
1
4.368
I am fine!

由上面的代码可知,从stringstream流中的数据输入字符串到一个变量里,是以遇到空格跳到下一个字符串的这样的形式连续读取的。