[C++ Primer] 第3章: 字符串, 向量和数组
标准库类型string
string初始化
string s2(s1);
string s2 = s1;
string s3("value");
string s3 = "value";
string s4(n, 'c'); // n个连续的c组成的字符串
读写string对象
读写未知数量的string对象
int main()
{
string word;
while(cin >> word)
cout << word << endl;
return 0;
}
使用getline读取一整行
int main()
{
string line;
while(getline(cin, line)) // line不含换行符, 第二个参数必须是string类型
cout << line << endl;
return 0;
}
size()返回类型为string::size_type, 推荐使用auto或者decltype推断变量的类型
auto len = line.size();
decltype(line.size()) len;
处理string中的字符
cctype头文件中的函数
函数 | 说明 |
---|---|
isalnum(int c) | 测试字符是否为英文或数字,在标准c中相当于使用isalpha(c)或 isdigit(c) |
isalpha(int c) | 检查参数c是否为英文字母 ,在标准c中相当于使用isupper(c)或islower(c) |
isascii(int c) | 检查参数c是否为ASCII码字符,也就是判断c的范围是否在0到127之间。 |
iscntrl(int c) | 检查参数c是否为ASCII控制码,也就是判断c的范围是否在0到30之间。 |
isdigit(int c) | 检查参数c是否为阿拉伯数字0到9。 |
isgraph(int c) | 检查参数c是否为可打印字符,若c所对映的ASCII码可打印,且非空格字符则返回TRUE。 |
islower(int c) | 检查参数c是否为小写英文字母。 |
isprint(int c) | 检查参数c是否为可打印字符,若c所对映的ASCII码可打印,其中包含空格字符,则返回TRUE。 |
isspace(int c) | 检查参数c是否为空格字符,也就是判断是否为空格('')、定位字符('\t')、CR('\r')、换行('\n')、垂直定位字符('\v')或翻页('\f')的情况。 |
ispunct(int c) | 检查参数c是否为标点符号或特殊符号。返回TRUE也就是代表参数c为非空格、非数字和非英文字母。 |
isupper(int c) | 检查参数c是否为大写英文字母。 |
isxdigit(int c) | 检查参数c是否为16进制数字,只要c为下列其中一个情况则返回TRUE。16进制数字:0123456789ABCDEF。 |
tolower(int c) | 如果c是大写字母则转成小写, 否则原样输出c |
toupper(int c) | 如果c是小写字母则转成大写, 否则原样输出c |
使用范围for循环处理每个字符
string s("Hello World!");
for(auto &c : s)
c = toupper(c); // c是一个引用, 因此赋值语句可以改变s中字符的值, 若无需改变s中的字符可以不使用引用
cout << s << endl;
注意: 当对字符串s使用下标操作单个字符时, 一般需要先判断s.empty()
标准库类型vector
定义和初始化
vector<T> v1;
vector<T> v2 = v1;
vector<T> v2(v1);
vector<T> v3(n, val); // 创建制定数量的元素, 包含n个重复元素, 每个元素的值都为val
vector<T> v4(n); // 包含n个重复的执行值初始化, 内置类型如int初始化为0
vector<T> v5{a, b, c};// C++11支持
vector<T> v5 = {a, b, c};
重要操作
v.empty()
v.size() // 返回类型为 std::vector<T>::size_type
v.push_back()
v1 = v2; // 用v2元素拷贝替换v1中元素
v1 = {a, b, c...}; // 用列表中元素拷贝替换v1中元素
v1 == v2; v1 != v2; // 其他逻辑运算符同理
注意: 只能对已存在的元素执行下标操作, 尽量使用范围for循环可以有效确保下标合法
迭代器介绍
所有标准库容器都可以使用迭代器(string对象不属于容器类型, 但也支持迭代器). 通过begin和end成员获取迭代器.
标准容器迭代器的运算符
*iter // 解引用, 返回迭代器所指元素的引用
iter->mem // 等价(*iter).mem
++iter
--iter
iter1 == iter2
iter1 != iter2
所有标准库容器的迭代器都定义了==和!=, 但是他们中的大多数都没有定义<运算符. 因此要养成使用迭代器和!=的习惯, 就不用太在意用的到底是那种容器.
迭代器类型: 使用iterator和const_iterator表示迭代器类型
vector<int>::iterator it; // 可读写
string::iterator it2;
vector<int>::const_iterator it3; // 只读
string::const_iterator it4;
若对象是常量, 则begin和end返回const_iterator, 否则返回iterator. 无论对象本身是否是常量, cbegin和cend都返回const_iterator.
注意: 凡是使用了迭代器的循环体, 都不要向迭代器所属的容器添加元素.
vector和string迭代器额外支持的运算符
iter + n, iter - n
iter += n, iter -= n
iter1 - iter2 // 类型为difference_type的带符号整数
>, >=, <, <=
数组
使用数据下标的时候, 通常将其定义为size_t类型. size_t类型是一种与机器相关的如符号类型, 它被设计的足够大以便能表示内存中任意对象的大小. 在cstddef头文件中定义了size_t类型.
指针也是迭代器
vector和string的迭代器支持的运算, 数组的指针全都支持, 为了获取尾后指针, C++11新标准引入两个名为begin和end的函数, 这两个函数与容器中的两个同名函数类似. 这两个函数定义在 iterator头文件中.
int ia[] = {1, 2, 3, 4};
int *beg = begin(ia); // 首元素的指针
int *end = end(ia); // 尾后元素指针, 不能执行解引用和递增操作
auto n = end(ia) - begin(ia); // 数组ia中的元素数量, n的类型为ptrdiff_t带符号类型, 该类型定义在头文件cstddef头文件中.
C风格字符串
C标准库String函数, 定义在ctring头文件中
strlen(p) // 返回p的长度, 不含'\n'
strcmp(p1, p2) // 比较p1, p2相等性, ==返回0, < 返回负值, > 返回正值
strcat(p1, p2) // 将p2附加到p1之后, 返回p1
strcpy(p1, p2) // 将p2拷贝给p1, 返回p1
混用string对象和C风格字符串
string s("Hello World");
const char *str = s.c_str(); // 如果后续操作改变了s, 可能导致str失效, 如果执行完c_str()函数后程序想一直使用返回的数组, 最好将该数组重新拷贝一份.
使用数组初始化vector对象
int iarr[] = {1, 2, 3, 4};
vector<int> ivec(begin(iarr), end(iarr));
vector<int> subVec(iarr + 1, iarr + 3); // 拷贝iarr[1], iarr[2]
多维数组
使用范围for循环处理多维数组, 除了最内层的循环外, 其他所有循环的控制变量都应该是引用类型.
int ia[3][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}};
// 使用范围for循环
for(auto &raw : ia) // 使用引用类型是为了防止数组名自动转为指针类型
for(auto col : raw) // 如果想要修改数组元素也需要使用引用类型, 单纯输出的话可以不使用引用类型
printf("%d ", col);
// 使用标准库函数
for(auto p = begin(ia); p != end(ia); ++p)
for(auto q = begin(*p); q != end(*p); ++q)
printf("%d ", *q);
// 使用指针
for(int (*p)[4] = ia; p != ia + 3; ++p)
for(int *q = *p; q != *p + 4; ++q)
printf("%d ", *q);