[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 algorithmscontainersfunctional, 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;
View Code

  

如何使用

(a) findrfind 都还比较容易理解,一个是正向匹配,一个是逆向匹配,后面的参数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;
}
View Code

 

  • 对象数组

涉及的一些方法: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; 
}
View Code

  

二、排序方法

  • 数组排序

排序的两种比较策略:

(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 五种类型的迭代器:

    1. input iterators (that can only be used to read a sequence of values), 
    2. output iterators (that can only be used to write a sequence of values), 
    3. forward iterators (that can be read, written to, and move forward), 
    4. bidirectional iterators (that are like forward iterators, but can also move backwards) and 
    5. 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.

posted @ 2017-07-31 07:09  郝壹贰叁  阅读(170)  评论(0编辑  收藏  举报