String的类型
C++ 提供了以下两种类型的字符串表示形式:
- C 风格字符串
- C++ 引入的 string 类类型
C 风格字符串
字符串实际上是使用 null 字符 \0 终止的一维字符数组。
初始化字符串
char site[7] = {'C', 'S', 'D', 'N', '\0'};
依据数组初始化规则,也可以把写成以下形式
char site[] = "CSDN";
其实C++ 编译器会在初始化数组时,自动把 \0 放在字符串的末尾,无需末尾再加入\0
C++引入的string类类型
C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。
初始化字符串
#include <iostream>
#include <string> //导入头文件
using namespace std;
int main ()
{
string str = "CSDN"; //格式
return 0;
}
一系列输入问题
参考文献:https://blog.csdn.net/Flag_ing/article/details/125209588
cin的原理
cin / cout 是C++在头文件<iostream>中封装的类对象,cin 是 istream类对象,cout是ostream类对象,其输入输出操作符 cin>> 和 cout<< 也是通过重载机制赋予的输入输出功能。
使用 cin 执行输入操作是有一个输入缓冲区的,如果是从键盘获取的输入数据,则在按下 Enter 键(即回车键)之后输入的数据(包括回车键)才会被送到输入缓冲区中执行实际的 I/O 操作。
输入:1[空格]2[空格]3[空格]4[空格]5[回车]
则实际上送入缓冲区的是: 1 2 3 4 5'\n' ,共10个字符。
为什么设置输入输出缓冲区?
1、如果每次读入一个字符都直接处理,由于从磁盘文件中读取一个字符需要大量硬件活动,在读取量较大时,这是很耗时间的(硬盘的读写速度远小于内存和CPU)。缓冲的方式是每次从硬盘中读取大量信息放在内存中,然后每次从缓冲区读取字节仅从操作,由于内存的读写速度远高于硬盘,因此这样可以大大提升读写效率。
2、由于C++程序通常在用户键入回车后刷新缓冲区,因此对键盘输入进行缓冲可以再将输入传给程序之前返回并更正。也就是说在你按下回车键前,你输入的数据都是可以删除并重新输入(修改)的。
字符数组的输入方式
cin 使用空白字符作为一次输入的结尾,并忽略该空字符。
int main(void)
{
char line[10];
cout << "input what you want:";
cin >> line; // 可以尝试输入 "123 456"
cout << "what you input is:" << line << endl; // 只能输出 "123"
//一次只能完成一个单词的输入,其后的单词都会被忽略掉
return 0;
}
/* 测试用例
input what you want:123 456
what you input is:123
*/
使用 getline() / get() 函数完成面向行的输入
getline() / get() 函数都可以接收一行字符串输入,并以回车作为输入的结尾。区别就是 getline 会在输入结束后丢弃最后的换行符,而 get 则会在输入队列中保留最后的换行符。
1.cin.getline()
语法:cin.getline(line, nums, (optional)delim)
line:目标数组
nums:数组长度
这样来避免超越数组的边界。从输入队列中获取 nums-1 个字符到 line 中,因为字符串的最后一个字符是 '\0'。如果输入的长度超出 nums-1 个,则 getline 和 get 将把余下的字符留在输入队列中,另外 getline 还会设置失效位,并关闭后面的输入(即后面的所有输入将不可用),此时可以用 cin.clear() 清除标准位就可以恢复后面的输入。
int main(void)
{
char line1[10];
char line2[10];
char line3[10];
cout << "input what you want:" << endl;
cin.getline(line1, 10); // 可以尝试输入 "123 456 789" ('\n') 含有11个字符
// cin.clear();
cin.getline(line2, 10); // 不可用,直接退出
cin.getline(line3, 10); // 不可用,直接退出
cout << "what you input is:" << endl;
cout << "line1:" << line1 << endl; // 输出 "123 456 7"
cout << "line2:" << line2 << endl; // 输出 空
cout << "line3:" << line3 << endl; // 输出 空
return 0;
}
/*
input what you want:
123 456 789
what you input is:
line1:123 456 7
line2:
line3:
*/
// 使用 clear
int main(void)
{
char line1[10];
char line2[10];
char line3[10];
cout << "input what you want:" << endl;
cin.getline(line1, 10); // 可以尝试输入 "123 456 789" ('\n') 含有11个字符
cin.clear(); // 清除标志位
cin.getline(line2, 10); // 可用,输出 "89",这是上一步输入队列中的字符
cin.getline(line3, 10); // 可用,你输入啥输出啥
cout << "what you input is:" << endl;
cout << "line1:" << line1 << endl; // 输出 "123 456 7"
cout << "line2:" << line2 << endl; // 输出 空
cout << "line3:" << line3 << endl; // 输出 空
return 0;
}
/*
input what you want:
123 456 789
123
what you input is:
line1:123 456 7
line2:89
line3:123
*/
cin.getline 还有第三个参数 delim(全称应该是 delimiter,中文意思:定界符/分隔符),作用是指定用作分界符的字符(没有这个参数的版本将以换行符为分界符)
遇到分界符后,当前字符串的输入将停止,即使还未读取最大数目的字符。因此在默认情况下,这种分界符的方式或者到达输入的指定数目都将停止读取输入。和默认情况一样 get() 将分界符留在输入队列中,而 getline() 不保留。
cin.getline(line, nums, delim),也就是以 delim 指定的字符作为一次字符串输入的结束,其后的在 nums-1 范围内的输入会保留在输入队列中作为下一个字符串的开始。
int main(void)
{
char line1[10];
char line2[10];
cout << "input what you want:" << endl;
cin.getline(line1, 10, 's'); // 可以尝试输入 "123s456" ('\n')
cin.getline(line2, 10); //
cout << "what you input is:" << endl;
cout << "line1:" << line1 << endl; // 输出 "123"
cout << "line2:" << line2 << endl; // 输出 "456"
return 0;
}
/* 测试用例
input what you want:
123s456
what you input is:
line1:123
line2:456
*/
getline 函数每次读取一行,它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串时他用空字符 '\0' 来替换换行符。
2.cin.get()
语法:cin.get(line, nums, (optional)delim)
get 函数会在输入队列中保留最后的换行符。注意是输入队列,也就是在一个 get()
函数完成输入后,会在输入缓存中保留组后的换行符,如果不做处理在下次运行 get() 时会出问题。
int main(void)
{
char line1[10];
char line2[10];
cout << "input what you want:" << endl;
cin.get(line1, 10); // 可以尝试输入 "123" ('\n'), 此时回车符将会留在输入队列中
cin.get(line2, 10); // 可以尝试输入 "456",可以发现 line1 输入完后,这步直接被“跳过了”
cout << "line1:" << line1 << endl; // 输出 "123"
cout << "line2:" << line2 << endl; // 输出空字符,由于 cin.get(line2, 10); 读入的是上一个字符输入结束时留输入队列中的回车符
return 0;
}
/* 测试用例
input what you want:
123
what you input is:
line1:123
line2:
*/
解决措施:
cin.get(line2, 10).get();
\\ 或者
cin.get(line2, 10);
get();
数字和字符串的混合输入问题
使用 cin 输入一个整数后,最后的回车符会留在输入队列中,类似于get,这会对后续的 getline 和 get 输入产生影响,因为 getline / get 看到换行符后会认为是一个空行,然后会把空字符赋给后面的字符串
int main(void)
{
int a, b; // 输入 123 ('\n') 456 ('\n')
char s[5];
cin >> a;
cin >> b;
cin.getline(s, 5); // 得到换行符,直接赋空值
cout << a << " " << b << " " << s << endl;
}
/* 测试用例
123
456
123 456
*/
解决措施:在后面添加 cin.get() 或者 (cin >> b).get()
int main(void)
{
int a, b; // 输入 123 ('\n') 456 ('\n')
char s[7];
cin >> a;
// cin >> b;
// cin.get();
(cin >> b).get();
cin.getline(s, 7); // 输入 "string" ('\n')
cout << a << " " << b << " " << s << endl;
}
/* 测试用例
123
456
string
123 456 string
*/
String对象的输入
使用 cin<<str 的语法来将输入存储到 string 对象中与字符数组的操作是一样的。不同的是当每次读取一行而不是一个单词时使用的语法是不同的。
int main(void)
{
string str;
cout << "input what you want:" << endl;
getline(cin, str);
//将 cin 作为参数,指出到哪里去查找输入。
//另外也没有指出字符串长度的参数,因为 string 对象将根据字符串的长度自动调整自己的大小。
cout << "what you input is:" << endl;
cout << str << endl;
}
/* 测试用例
input what you want:
123 456 789
what you input is:
123 456 789
*/
常见操作及函数
1.定义
string s1; // 初始化一个空字符串
string s2 = s1; // 初始化s2,并用s1初始化
string s3(s2); // 作用同上
string s4 = "hello world"; // 用 "hello world" 初始化 s4,除了最后的空字符外其他都拷贝到s4中
string s5("hello world"); // 作用同上
string s6(6,'a'); // 初始化s6为:aaaaaa
string s7(s6, 3); // s7 是从 s6 的下标 3 开始的字符拷贝
string s8(s6, pos, len); // s7 是从 s6 的下标 pos 开始的 len 个字符的拷贝
2.查询字符串信息、索引
#include <iostream>
#include <string>
using namespace std;
int main(void)
{
string s1 = "abc"; // 初始化一个字符串
cout << s1.empty() << endl; // s 为空返回 true,否则返回 false
cout << s1.size() << endl; // 返回 s 中字符个数,不包含空字符
cout << s1.length() << endl; // 作用同上
cout << s1[1] << endl; // 字符串本质是字符数组
cout << s1[3] << endl; // 空字符还是存在的
return 0;
}
// 运行结果 //
0
3
3
b
3.拼接、比较等操作
s1+s2 // 返回 s1 和 s2 拼接后的结果。加号两边至少有一个 string 对象,不能都是字面值
s1 == s2 // 如果 s1 和 s2 中的元素完全相等则它们相等,区分大小写
s1 != s2
<, <=, >, >= // 利用字符的字典序进行比较,区分大小写
4.for 循环遍历
可以使用 c++11 标准的 for(declaration: expression) 形式循环遍历,例子如下:
(如果想要改变 string 对象中的值,必须把循环变量定义为引用类型)
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main(void)
{
string s1 = "nice to meet you~"; // 初始化一个空字符串
// 如果想要改变 string 对象中的值,必须把循环变量定义为引用类型。引用只是个别名,相当于对原始数据进行操作
for(auto &c : s1)
c = toupper(c);
cout << s1 << endl; // 输出
return 0;
}
// 运行结果 //
NICE TO MEET YOU~
5.修改 string 的操作
pos是一个下标或者迭代器。接受下标的版本返回一个指向 s 的引用;接受迭代器的版本返回一个指向第一个插入字符的迭代器
在 pos 之前插入 args 指定的字符。
s.insert(pos, args)
// 在 s 的位置 0 之前插入 s2 的拷贝
s.insert(0, s2)
删除从 pos 开始的 len 个字符。如果 len 省略,则删除 pos 开始的后面所有字符。返回一个指向 s 的引用。
s.erase(pos, len)
将 s 中的字符替换为 args 指定的字符。返回一个指向 s 的引用。
s.assign(args)
replace()函数
#include <iostream>
#include <string>
using namespace std;
int main()
{
string line = "this@ is@ a test string!";
line = line.replace(line.find("@"), 1, ""); //从第一个@位置替换第一个@为空
cout<<line<<endl;
return 0;
}
将 args 追加到 s 。返回一个指向 s 的引用。args 不能是单引号字符,若是单个字符则必须用双引号表示。如,可以是 s.append("A") 但不能是 s.append('A')
s.append(args)
将 s 中范围为 range 内的字符替换为 args 指定的字符。range 或者是一个下标或长度,或者是一对指向 s 的迭代器。返回一个指向 s 的引用。
s.replace(range, args)
// 从位置 3 开始,删除 6 个字符,并插入 "aaa".删除插入的字符数量不必相等
s.replace(3, 6, "aaa")
字符串反转,使用 <algorithm> 头文件中的 reverse() 方法:
string s2 = "12345"; // 初始化一个字符串
reverse(s2.begin(), s2.end()); // 反转 string 定义的字符串 s2
cout << s2 << endl; // 输出 54321
交换字符串:swap()函数
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1,str2;
cin>>str1>>str2;
cout<<"str1:"<<str1<<endl;
cout<<"str2:"<<str2<<endl;
swap(str1, str2);
cout<<"str1:"<<str1<<endl;
cout<<"str2:"<<str2<<endl;
}
字符串比较函数:compare()
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1,str2;
cin>>str1>>str2;
cout<<str1.compare(str2)<<endl;
return 0;
}
6.string搜索操作
搜索操作返回指定字符出现的下标,如果未找到返回 npos
s.find(args) // 查找 s 中 args 第一次出现的位置
s.rfind(args) // 查找 s 中 args 最后一次出现的位置
在 s 中查找 args 中任何一个字符 最早/最晚 出现的位置
s.find_first_of(args) // 在 s 中查找 args 中任何一个字符最早出现的位置
s.find_last_of(args) // 在 s 中查找 args 中任何一个字符最晚出现的位置
例如:
string s1 = "nice to meet you~";
cout << s1.find_first_of("mey") << endl; // 输出结果为 3,'e' 出现的最早
在 s 中查找 第一个/最后一个 不在 args 中的字符的位置
s.find_first_not_of(args) // 查找 s 中 第一个不在 args 中的字符的位置
s.find_last_not_of(args) // 查找 s 中 最后一个不在 args 中的字符的位置
例如:
string s1 = "nice to meet you~";
cout << s1.find_first_not_of("nop") << endl; // 输出结果为 1 ,'i' 不在 "nop" 里
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cin>>str;
//主要字符串是从0开始计数的
cout<<"ab在str中的位置:"<<str.find("ab")<<endl;
//返回第一次出现ab的位置,没有则返回一串乱码
cout<<"ab在str[2]到str[n-1]中的位置:"<<str.find("ab",2)<<endl;
//返回第一次从str[2]开始出现ab的位置,没有则返回一串乱码
cout<<"ab在str[0]到str[2]中的位置:"<<str.rfind("ab",2)<<endl;
//返回ab在str[0]到str[2]中的位置,没有则返回一串乱码
return 0;
}
子串substr()函数
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cin>>str;
cout<<"返回str[3]及其以后的子串:"<<str.substr(3)<<endl;
//若小于限制长度则报错
cout<<"从ste[2]开始由四个字符组成的子串:"<<str.substr(2,4)<<endl;
//若小于限制长度则报错
return 0;
}
7.string、char 型与数值的转换
将数值 val 转换为 string 。val 可以是任何算术类型(int、浮点型等)
string s = to_string(val)
转换为整数并返回。返回类型分别是 int、long、unsigned long、long long、unsigned long long。b 表示转换所用的进制数,默认为10,即将字符串当作几进制的数转换,最终结果仍然是十进制的表示形式 。p 是 size_t 指针,用来保存 s 中第一个非数值字符的下标,默认为0,即函数不保存下标,该参数也可以是空指针,在这种情况下不使用。
stoi(s)
// 函数原型 int stoi (const string& str, size_t* idx = 0, int base = 10);
stoi(s, p, b)
stol(s, p, b)
stoul(s, p, b)
stoll(s, p, b)
stoull(s, p, b)
// 例如
string s1 = "11"; // 初始化一个空字符串
int a1 = stoi(s1);
cout << a1 << endl; // 输出 11
int a2 = stoi(s1, nullptr, 8);
cout << a2 << endl; // 输出 9
int a3 = stoi(s1, nullptr, 2);
cout << a3 << endl; // 输出 3
转换为浮点数并返回。返回类型分别是 float、double、long double 。p 是 size_t 指针,用来保存 s 中第一个非数值字符的下标,默认为0,即函数不保存下标,该参数也可以是空指针,在这种情况下不使用。
stof(s)
stof(s, p)
stod(s, p)
stold(s, p)
char 型转数值。注意传入的参数是指针类型,即要对字符取地址
atoi(c)
// 函数原型 int atoi(const char *_Str)
atol(c)
atoll(c)
atof(c)