[c++] String and her STL
标准模板库de字符串
字符串
一、字符串在STL中的角色
Code from <C++ Primer Plus>, my recent study into c++.
Code is everything, code is the best teacher!
The Standard Template Library (STL) is a software library for the C++ programming language that influenced many parts of the C++ Standard Library. It provides four components called algorithms, containers, functional, and iterators. (From wik.)
在C++标准中,STL被组织为下面的13个头文件:
<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack>、<utility>
二、String 常见操作
初始化
(1) 初始化,(2) 若干相同字符构成de初始化,(3) 字符串合并,(4) 字符串截断。
// str1.cpp -- introducing the string class #include <iostream> #include <string> int main() { /*--------------------------*/ using namespace std; string one("Lottery Winner!"); // one = "string" 括号初始化 cout << one << endl; string three(one); cout << three << endl /*--------------------------*/ string two(20, '$'); // 20 X char 若干相同字符构成 cout << two << endl; one += " Oops!"; // Append: overloaded 字符串合并 cout << one << endl; /*--------------------------*/ char alls[] = "All's well that ends well"; string five(alls, 20); // Cut head 字符串截断 cout << five << "!\n"; /*--------------------------*/ string six(alls+6, alls + 10); // Cut subString 字符串截断(自定义区间) cout << six << ", "; return 0; }
读文件
// strfile.cpp -- read strings from a file #include <iostream> #include <fstream> #include <string> #include <cstdlib> int main() { using namespace std; /******************************/ ifstream fin; // In from File fin.open("tobuy.txt"); // fin.open( filename.c_str() ); 这里需是 c语言形式的字符串 if (fin.is_open() == false) { cerr << "Can't open file. Bye.\n"; exit(EXIT_FAILURE); }
/*****************************/ string item; getline(fin, item, ':'); while (fin) // while input is good { ++count; cout << count <<": " << item << endl; getline(fin, item,':'); } fin.close(); return 0; }
查找
Reprinted from http://blog.csdn.net/hunter8777/article/details/6213635
被重载
由于查找是使用最为频繁的功能之一,string 提供了非常丰富的查找函数。
以上函数都是被重载了4次,也就是说总共有24个函数。
函数名 | 描述 |
find | 查找 |
rfind | 反向查找 |
find_first_of | 查找包含子串中的任何字符,返回第一个位置 |
find_first_not_of | 查找不包含子串中的任何字符,返回第一个位置 |
find_last_of | 查找包含子串中的任何字符,返回最后一个位置 |
find_last_not_of | 查找不包含子串中的任何字符,返回最后一个位置 |
以下是以find_first_of 函数为例说明他们的参数,其他函数和其参数一样。
size_type find_first_of(const basic_string& s, size_type pos = 0) size_type find_first_of(const charT* s, size_type pos, size_type n) size_type find_first_of(const charT* s, size_type pos = 0) size_type find_first_of(charT c, size_type pos = 0)
返回值
所有的查找函数都返回一个size_type类型,
-
- 这个返回值一般都是所找到字符串的位置,
- 如果没有找到,则返回string::npos。
有一点需要特别注意,所有和string::npos的比较一定要用string::size_type来使用,不要直接使用int 或者unsigned int等类型。其实string::npos表示的是-1, 看看头文件:
template <class _CharT, class _Traits, class _Alloc> const basic_string<_CharT,_Traits,_Alloc>::size_type basic_string<_CharT,_Traits,_Alloc>::npos = basic_string<_CharT,_Traits,_Alloc>::size_type) -1;
如何使用
(a) find 和 rfind 都还比较容易理解,一个是正向匹配,一个是逆向匹配,后面的参数pos都是用来指定起始查找位置。
(b) 对于find_first_of 和 find_last_of 就不是那么好理解。
find_first_of 是给定一个要查找的字符集,找到这个字符集中任何一个字符所在字符串中第一个位置。或许看一个例子更容易明白。
有这样一个需求:过滤一行开头和结尾的所有非英文字符。看看用string 如何实现:
1 #include <string> 2 #include <iostream> 3 using namespace std;
4 int main(){ 5 string strinfo=" //*---Hello Word!......------"; 6 string strset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 7 int first = strinfo.find_first_of(strset); 8 if(first == string::npos) { 9 cout<<"not find any characters"<<endl; 10 return -1; 11 } 12 int last = strinfo.find_last_of(strset); 13 if(last == string::npos) { 14 cout<<"not find any characters"<<endl; 15 return -1; 16 } 17 cout << strinfo.substr(first, last - first + 1)<<endl; 18 return 0; 19 }
这里把所有的英文字母大小写作为了需要查找的字符集,先查找第一个英文字母的位置,然后查找最后一个英文字母的位置,然后用substr 来的到中间的一部分,用于输出结果。下面就是其结果:
Hello Word
前面的符号和后面的符号都没有了。像这种用法可以用来查找分隔符,从而把一个连续的字符串分割成为几部分,达到 shell 命令中的 awk 的用法。特别是当分隔符有多个的时候,可以一次指定。
容器 Container
"顺序性" 容器
一、"顺序性" 容器的共性
-
顺序容器类型
The sequence is an important refinement because of six of the STL container types.
The deque, list, queue, priority_queue, stack, and vector template classes are all models of the sequence concept.
Sequences (Arrays/Linked Lists): ordered collections | |
---|---|
vector | a dynamic array, like C array (i.e., capable of random access) with the ability to resize itself automatically when inserting or erasing an object. Inserting and removing an element to/from back of the vector at the end takes amortized constant time. Inserting and erasing at the beginning or in the middle is linear in time.
A specialization for type bool exists, which optimizes for space by storing bool values as bits. |
list | a doubly linked list; elements are not stored in contiguous memory. Opposite performance from a vector. Slow lookup and access (linear time), but once a position has been found, quick insertion and deletion (constant time). |
deque | a vector with insertion/erase at the beginning or end in amortized constant time, however lacking some guarantees on iterator validity after altering the deque. |
Container adaptors | |
queue | Provides FIFO queue interface in terms of push/pop/front/back operations.
Any sequence supporting operations front(), back(), push_back(), and pop_front() can be used to instantiate queue (e.g. list and deque). |
priority_queue | Provides priority queue interface in terms of push/pop/top operations (the element with the highest priority is on top).
Any random-access sequence supporting operations front(), push_back(), and pop_back() can be used to instantiate priority_queue (e.g. vector and deque). Elements should additionally support comparison (to determine which element has a higher priority and should be popped first). |
stack | Provides LIFO stack interface in terms of push/pop/top operations (the last-inserted element is on top).
Any sequence supporting operations back(), push_back(), and pop_back() can be used to instantiate stack (e.g. vector, list, and deque). |
-
共性方法、操作
They all support the operators in Table 16.6.
Table 16.7 lists these Additional operations.
算法 Algorithm
排序算法
一、对象数组
-
字符串数组
// vect1.cpp -- introducing the vector template #include <iostream> #include <string> #include <vector> using namespace std; const int NUM = 5; int main() { vector<int> ratings(NUM); vector<string> titles(NUM); int i; for (i = 0; i < NUM; i++) { cout << "Enter title #" << i + 1 << ": "; getline(cin, titles[i]); cout << "Enter your rating (0-10): "; cin >> ratings[i]; cin.get(); // Delete ENTER. } cout << "Thank you. You entered the following:\n" << "Rating\tBook\n"; for (i = 0; i < NUM; i++) { cout << ratings[i] << "\t" << titles[i] << endl; } return 0; }
-
对象数组
涉及的一些方法:swap, insert, erase, Copy constructor, size, push_back。
// vect2.cpp -- methods and iterators #include <iostream> #include <string> #include <vector> struct Review { std::string title; int rating; }; bool FillReview(Review & rr); void ShowReview(const Review & rr); int main() { using std::cout; using std::vector; vector<Review> books; Review temp; while (FillReview(temp)) books.push_back(temp); int num = books.size(); if (num > 0) { cout << "Thank you. You entered the following:\n" << "Rating\tBook\n"; for (int i = 0; i < num; i++) ShowReview(books[i]); cout << "Reprising:\n" << "Rating\tBook\n"; vector<Review>::iterator pr; for (pr = books.begin(); pr != books.end(); pr++) ShowReview(*pr); vector <Review> oldlist(books); // Copy constructor used if (num > 3) { // remove 2 items books.erase(books.begin() + 1, books.begin() + 3); // insert 1 item books.insert(books.begin(), oldlist.begin() + 1, oldlist.begin() + 2); } books.swap(oldlist); } else cout << "Nothing entered, nothing gained.\n"; return 0; } bool FillReview(Review & rr) { std::cout << "Enter book title (quit to quit): "; std::getline(std::cin,rr.title); if (rr.title == "quit") return false; std::cout << "Enter book rating: "; std::cin >> rr.rating; if (!std::cin) return false; std::cin.get(); return true; } void ShowReview(const Review & rr) { std::cout << rr.rating << "\t" << rr.title << std::endl; }
二、排序方法
-
数组排序
排序的两种比较策略:
(a) 把比较对象赋予比较的能力,也即是operator
(b) 自定义对象比较的细节。
// vect3.cpp -- using STL functions #include <iostream> #include <string> #include <vector> #include <algorithm> struct Review { std::string title; int rating; }; bool operator<(const Review & r1, const Review & r2); bool worseThan(const Review & r1, const Review & r2); bool FillReview(Review & rr); void ShowReview(const Review & rr);
int main() { using namespace std; vector<Review> books; Review temp; while (FillReview(temp)) books.push_back(temp); cout << "Thank you. You entered the following " << books.size() << " ratings:\n" << "Rating\tBook\n"; for_each(books.begin(), books.end(), ShowReview); // Traverse a chain of element. sort(books.begin(), books.end()); // (a) 可以直接比较,因为operator了. cout << "Sorted by title:\nRating\tBook\n"; for_each(books.begin(), books.end(), ShowReview); sort(books.begin(), books.end(), worseThan); // (b) 如果没有operator,则需要自定义细节 cout << "Sorted by rating:\nRating\tBook\n"; for_each(books.begin(), books.end(), ShowReview); random_shuffle(books.begin(), books.end()); cout << "After shuffling:\nRating\tBook\n"; for_each(books.begin(), books.end(), ShowReview); return 0; } bool operator < (const Review & r1, const Review & r2) { if (r1.title < r2.title) return true; else if (r1.title == r2.title && r1.rating < r2.rating) return true; else return false; } bool worseThan(const Review & r1, const Review & r2) { if (r1.rating < r2.rating) return true; else return false; }
//--------------------------重点在上面------------------------------
// 设置
bool FillReview(Review & rr) { std::cout << "Enter book title (quit to quit): "; std::getline(std::cin,rr.title); if (rr.title == "quit") return false; std::cout << "Enter book rating: "; std::cin >> rr.rating; if (!std::cin) return false; std::cin.get(); return true; }
// 打印 void ShowReview(const Review & rr) { std::cout << rr.rating << "\t" << rr.title << std::endl; }
-
排序策略
An Excellent Resource : http://www.cppblog.com/mzty/archive/2005/12/15/1770.html
以下是所有STL sort算法函数的名字列表:
函数名 | 功能描述 |
---|---|
sort | 对给定区间所有元素进行排序 |
stable_sort | 对给定区间所有元素进行稳定排序 |
partial_sort | 对给定区间所有元素部分排序 |
partial_sort_copy | 对给定区间复制并排序 |
nth_element | 找出给定区间的某个位置对应的元素 |
is_sorted | 判断一个区间是否已经排好序 |
partition | 使得符合某个条件的元素放在前面 |
stable_partition | 相对稳定的使得符合某个条件的元素放在前面 |
迭代器 Iterator
遍历字符串数组
一、迭代器种类
Authoritative source : http://www.cplusplus.com/reference/iterator/
The STL implements five different types of iterators. These are 五种类型的迭代器:
- input iterators (that can only be used to read a sequence of values),
- output iterators (that can only be used to write a sequence of values),
- forward iterators (that can be read, written to, and move forward),
- bidirectional iterators (that are like forward iterators, but can also move backwards) and
- random access iterators(that can move freely any number of steps in one operation).
二、拷贝策略
-
静态拷贝
两个有意思的点:(a) ostream_iterator 迭代器; (b) reverse_iterator 迭代器。
两种方式 “依次/迭代地” 打印数组中的内容。
// copyit.cpp -- copy() and iterators #include <iostream> #include <iterator> #include <vector> int main() { using namespace std; int casts[10] = {6, 7, 2, 9 ,4 , 11, 8, 7, 10, 5}; vector<int> dice(10); // 数组直接拷贝到vector里面,虽然是不同的类型 copy(casts, casts + 10, dice.begin());
// 输出流
ostream_iterator<int, char> out_iter(std::cout, " "); // copy from vector to output copy(dice.begin(), dice.end(), out_iter); // 直接将copy的内容 --> 输出流 cout << endl;
// ---------------------------------------------
cout <<"Implicit use of reverse iterator.\n"; copy(dice.rbegin(), dice.rend(), out_iter); // 直接将copy的内容 --> 输出流 cout << endl; cout <<"Explicit use of reverse iterator.\n"; vector<int>::reverse_iterator ri; for (ri = dice.rbegin(); ri != dice.rend(); ++ri) cout << *ri << ' '; cout << endl; return 0; }
-
动态拷贝
copy模式只能给固定长度的vector做“拷贝赋值”;但,通过iterator模式,甚至可以模拟insert操作。
(a) back_insert_iterator直接指向这个模板<vector<string>>的变量words的尾巴。
(b) insert_iterator直接指向这个模板<vector<string>>的变量words的某一处位置。
// inserts.cpp -- copy() and insert iterators
// The length can be increased.
#include <iostream> #include <string> #include <iterator> #include <vector> int main() { using namespace std; string s1[4] = {"fine", "fish", "fashion", "fate"}; string s2[2] = {"busy", "bats"}; string s3[2] = {"silly", "singers"}; vector<string> words(4); // Defined, fixed length. copy(s1, s1 + 4, words.begin()); ostream_iterator<string, char> out(std::cout, " "); copy (words.begin(), words.end(), out); cout << endl;
/*
* copy() do not has permission to do operation on length of vector. But iterator has!
*/
// construct anonymous back_insert_iterator object copy(s2, s2 + 2, back_insert_iterator< vector<string> >(words) ); copy(words.begin(), words.end(), out); cout << endl; // construct anonymous insert_iterator object copy(s3, s3 + 2, insert_iterator< vector<string> >( words, words.begin() ) ); copy(words.begin(), words.end(), out); cout << endl; return 0;
Output:
fine fish fashion fate
fine fish fashion fate busy bats
silly singers fine fish fashion fate busy bats
仿函数 functor
—— 有状态的函数,方便在迭代时,编译器能判断并自动内联。
一、迭代和计算逻辑分离
Ref: 什么是仿函数(functor)
#include <stdio.h> #include <string.h> #include <thread> #include <functional> #include <iostream> #include <stdio.h> #include <algorithm> using namespace std;
struct Sum_t { Sum_t(int * t):total(t) {}; int * total; void operator()(int element) { *total+=element; } };
int main() { int total = 0; Sum_t s(&total); // <-- 这个是构造参数 int arr[] = {0, 1, 2, 3, 4, 5}; std::for_each(arr, arr+6, s); // <-- 类作为函数来使用 cout << total << endl; }
二、参数可设置
类似于Python中的Closure。
class CalculateAverageOfPowers { public: CalculateAverageOfPowers(float p) : acc(0), n(0), p(p) {} void operator() (float x) { acc += pow(x, p); n++; } float getAverage() const { return acc / n; } private: float acc; int n; float p; }; CalculateAverageOfPowers my_cal(2);
三、有状态
因为有了类中的private中的成员变量,所以自然就可以充当状态变量。
这样,计算平均值,可以考虑到之前的计算结果。
CalculateAverage avg; avg = std::for_each(dataA.begin(), dataA.end(), avg); avg = std::for_each(dataB.begin(), dataB.end(), avg); avg = std::for_each(dataC.begin(), dataC.end(), avg);
四、性能
编译器可以准确知道std::transform需要调用哪个函数(add_x::operator)。这意味着它可以内联这个函数调用。(方便编译器优化)
而如果使用函数指针,编译器不能直接确定指针指向的函数,而这必须在程序运行时才能得到并调用。
一个例子就是比较std::sort 和qsort ,STL的版本一般要快5-10倍。
测试std::sort 和std::qsort 的性能, 修改编译器栈大小
std::sort更快。 快的原因是运行的时候, sort函数使用inline的方式调用比较函数(comparision function), 然而qsort使用的是指针的方式调用(即function pointer)comparison function。
End.