C++primer学习笔记(2)

五. 第五天

1.IO类型

头文件:(支持char类型)

  1. iostream定义读写控制窗口的类型。
  2. fstream定义读写已命名文件的类型。
  3. sstream所定义的类型则用于读写存储在内存中string对象。
    在这里插入图片描述

前面加上w则是支持wchar_t类型。

2.IO对象不可复制或赋值

只有支持赋值的元素类型可以存储在vector或其他容器类型里。
形参或返回类型也不能是流类型。
在这里插入图片描述
在这里插入图片描述

3.状态

每个IO类还定义了三个iostate类型的常量值,分别别哦啊还是特定的位模式。

  • badbit标志着系统级的故障,如无法恢复的读写错误。该流通常就不能再继续使用了。
  • 如果出现的是可恢复的错误,设置failbit表示,通常是可以修正的。
  • eofbit是在遇到文件结束符时设置的,此时同事还设置了failbit。
    流的状态由bad、fail、eof和good操作提示。
    clear和setstate操作用于改变条件成员的状态。
    clear操作将条件重设为有效状态。使用setstate操作可以打开某个指定的条件,用于某个问题的发生。除了添加的标记状态,setstate将保留其他已存在的状态变量不变。
int ival;
while(cin>>ival,!cin.eof())
{
	if(cin.bad()) throw ...
	if(cin.fail())
	{
		cerr<<"bad data";
		cin.clear(istream::failbit);
		continue;
	}
}

条件状态访问
rdstate成员函数返回一个iostate类型值,该值对应于流当前的某个条件状态。

istream::iostate old_state = cin.edstate();
cin.clear();
process_input();
cin.clear(old_state);

多种状态处理
尝尝会出现需要设置或清除多个状态二进制位的情况。

  1. 可以通过多次调用setstate或者clear函数实现。
  2. 使用按位或(OR)操作符在一次调用中生成“传递两个或更多状态位”的值。
is.setstate(ifstream::badbit|ifstream::failbit);
4.缓冲区管理
os<<"Please enter a value";

系统将字符串字面值存储在与流os关联的缓冲区中。
导致缓冲区的内容被刷新:

  1. 程序正常结束。将清空所有输出缓冲区。
  2. 阿紫一些不确定的时候,缓冲区可能已经满了, 缓冲区将会在写下一个值之前刷新。
  3. 用操作符显示的刷新缓冲区。endl。
  4. 在每次输出操作执行完后,用unitbuf操作符设置流的内部状态,从而清空缓冲区。
  5. 可将输出流与输入流关联起来,在读输入流时将刷新其团练的输出缓冲区。

输出缓冲区的刷新

  1. endl操作符用于输出一个换行符刷新缓冲区。
  2. flush用于刷新流,但不在输出中添加任何字符。
  3. ends在缓冲区中插入空字符null,然后刷新。
cout<<"hi"<<flush;
cout<<"hi"<<ends;
cout<<"hi"<<endl;

如果需要刷新所有输出,最好使用unitbuf操作符。这个操作在每次执行完写操作后都刷新流。

cout<<unitbuf<<"f"<<"s"<<nounitbuf;
cout<<"f"<<flush<<"s"<<flush;

**调试程序时,必须保证期待写入的每个输出都确实被刷新了。**系统不会在程序崩回时自动刷新缓冲区。如果仅因为缓冲区没有刷新,程序员将浪费大量时间跟踪调试并没有执行的代码,输出是应该多使用end,不必担心程序崩溃时输出是否悬而未决。

当输入流和输出流绑在一起时,任何读输入流的尝试都将首先刷新其输出流关联的缓冲区。
标准库将cout和cin绑在一起,因此输入操作会导致cout关联的缓冲区被刷新。

交互式系统通常应确保它们的输入和输出流是绑在一起的。

tie函数可用istream或ostream对象调用,使用一个指向ostream对象指针的形参。
调用tie函数时,将实参流绑定在调用该函数的对象上。
如果一个流调用tie函数将其本身绑在传递给tie的ostream实参对象上,则该流和是哪个的任何IO操作都会刷新实参所关联的缓冲区。

cin.tie(&cout);
ostream *old_tie = cin.tie();
cin,tie(0);
cin.tie(&cerr);

cin.tie(0)
cin.tie(old_tie);

一个ostream对象每次只能与一个istream对象绑定在一起。如果调用tie函数时传递实参0,则打破该流上已存在的捆绑。

5. 文件的输入输出
#include<fstream>
  1. ifstream提供读文件的功能
  2. iostream 提供写文件的功能。
  3. fstream 提供读写同一个文件的功能。

fstream还定义了两个自己的新操作–open和close,以及形参为要打开的文件名的构造函数。

需要读文件时,则必须定义自己的对象,并将它们绑定在需要的文件上。

ifstream infile(ifile.c_str());
ofstream outfile(ofile.c_str());

ifstream infile;
ofstream outfile;

infile.open("in");
outfile.open("out");

要检查文件打开是否成功。
将文件流与新文件重新捆绑。

ifstream infile("in");
infile.close();
infile.open("next");
vector对象命名为files
ifstream input;
vector<string>::const_iterator it = files.begin();

while(it!=files.end())
{
	input.open(ti->c_str());
	if(!input) break;
	while(input>>s) process(s);
	input.close();
	input.clear();
	++it;
}

如果和忽略了clear的调用,则循环只能读入第一个文件。

如果需要重用文件流读写多个文件,必须在读另一个文件之前调用clear清除该流的状态

6.文件模式

在这里插入图片描述
在这里插入图片描述

  1. out、trunc、app模式只能用于指定与ofstream或fstream对象关联的文件。
  2. in模式只能用于指定ifstream或fstream对象关联的文件。
  3. 所有文件都可以用ate或binary模式打开。
  4. ate模式只在打开是有效,文件打开后将定位在文件尾。
  5. 以binary模式打开的流则将文件以字节序列的形式处理,而不解释流中的字符。
  6. 默认时,与ifstream流对象关联的文件将以in模式打开,允许文件做读操作。与ofstream关联的文件则以out模式打开,是文件可写。以out模式打开的文件会被清空,丢弃文件存储的所有数据。

对用于ofstream打开的文件,要保存文件中存在的数据,唯一方法是显式的指定app模式打开。

ofstream outfile("file1));

ofstream outfile2("file1",ofstream::out|ofstream::trync);

ofstream appfile("file2",ofstream::app);

默认情况下,fstream对象以In和out模式同时打开。不清空。
如果只是用out模式,则文件会清空已存在的数据。
如果打开文件时指定了trunc模式,则无论指不指定in模式,文件同样会被清空。
模式是问加你的属性而不是流的属性

ofstream outfile;
当前目录中名为scratchpad的文件以输出模式打开并清空。
outfile.open("scratchpad",ofstream::out);

precious的文件要求以添加模式打开:保存文件里的原油数据,所有新的内容在文件尾部写入。
outfile.open("precious",ofstream::app);
outfile.close();

打开out文件时,没有明确指定输出模式,文件以out模式打开,意味着当前存储在“out”文件中的任何数据都将被丢弃
outfile.open("out");

打开模式的有效组合
在这里插入图片描述
上述模式都可以添加ate模式,只会改变文件打开时的初始化定位,将文件定位于末尾处。

一个打开并检查输入文件的程序
ifstream & open_file(ifstream &in,const string &file)
{
	//将流设置为有效状态
	in.close();
	in.clear();

	in.open(file.c_str());
	return in;
}
7.字符串流
#include<sstream>
  1. istringstream 提供读string的功能
  2. iostringstream 提供写string的功能
  3. stringstream 提供读写string的功能

stringstream对象不适用open和close函数,而fstream对象则不允许使用str。
在这里插入图片描述
有些处理基于每行实现,其他处理要操作每行中每个单词

string line,word;

while(getline(cin,line))//读取郑航内容
{
	istringstream stream(line);//将一个istringstream对象与读的行绑定
	while(stream>>word)//读取这样的一个单词
	{
		//processing
	}
}

stringstream提供的转换和/或格式化
stringstream对象的 一个常见用法是,需要在多种数据类型之间实现自动格式化时使用该类类型。
sstring输入和输出操作可自动的把算术类型转化为相应的string表示形式,反之也可以。

int val1 = 512,val2 = 1024;
ostringstream format_message; //ostringstream 空对象

//int->string
format_message<<"val1:"<<val1<<'\n '<<"val2:"<<val2; //val1:512\n val2:1024
istringstream input_istring(format_message.str());
string dump;

input_istring>>dump>>val1>>dump>>val2;
cout<<val1<<" " <<val2<<endl; //512 1024

str成员获取之前创建的ostringstream对象关联的string副本。再讲input_istring与string绑定。在读input_istring时,相应的值恢复为它们原来的数值型表示形式。
使用输入操作符读string时,空白符将会忽略。

六.第六天

1 顺序容器

标准库定义了三种顺序容器类型:vector(支持快速随机访问)、list(支持快速插入删除)、deque(双端队列)。还定义了三种适配器:stack(先进先出堆栈)、queue(先进先出队列)、priority_queue(有优先级管理的队列)。适配器是根据原始的容器类型所提供的操作,通过重新定义新的接口,来适应基础的容器类型。
容器的构造函数

  1. C c(c2);创建c2容器的副本
  2. C c(b,e);创建c,其元素是迭代器b和e标示的范围内元素的副本。
  3. C c(n,t);用n个值为t 元素创建容器c。 只适用于顺序容器
  4. C c(n);创建有n个值初始化元素的容器c;只适用于顺序容器

指针就是迭代器。可以使用内置数组的一对指针初始化容器。

char *words[] = {"s", "l", "q", "e"};

size_t len = sizeof(words) / sizeof(char *);

list<string> words2(words, words + words_size);

容器的元素类型:除了引用类型。不能创建存放IO类型对象的容器。
在这里插入图片描述

list容器的迭代器**既不支持算数运算,也不支持关系运算,只提供前置和后置的自增、自减运算以相等不等运算。
[first,end)

  1. 当first 和 last相等时,迭代器范围为空。
  2. 当first和last不相等时,迭代器范围内至少有一个元素,而且first指向该区间中的第一个元素。通过若干次自增运算first==last。

在这里插入图片描述
最后三种类型使程序员无需直接知道容器元素的真正类型,就能使用它。需要使用元素类型时,只要用value_type即可。如果要引用该类型,则通过reference和const_reference类型实现。(泛型)
在这里插入图片描述
在顺序容器中添加元素

  1. push_back();后面添加
  2. list和deque 容器类型还提供push_front() 前面添加

**容器元素都是副本。**系统将元素值复制到容器里。

在这里插入图片描述
在vector容器中添加元素可能会导致整个容器的重新加载。该容器涉及的所有迭代器都会失效。
当编写循环将元素插入到vector或deque容器中时,程序必须确保迭代器在每次循环后都得到更新。

**不要存储end操作返回的迭代器。**添加或删除deque或vector容器内的元素都会导致存储的迭代器失效。
为了避免存储end迭代器,可以在每次做完插入运算后重新计算end迭代器值。

while(first !=end );
->
while(first != v.end());

在这里插入图片描述在这里插入图片描述
如果resize操作压缩了容器,则指向已删除的元素迭代器失效。
如果容器非空,那么容器类型的front和back成员加你个返回容器内第一个或最有一个元素的引用。

在这里插入图片描述

在这里插入图片描述
assign操作首先删除容器中所有元素,然后将其参数所指定的新元素插入到该容器中。
传递给assign函数的迭代器不能指向调用哦该函数的容器内的元素。
关于swap的一个重要问题在于:该操作不会删除或插入任何元素,而且保证在常量时间内实现交换。由于容器没有移动任何元素,因此迭代器不会失效。

vector容器实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些。vector容器预留了这些额外的存储区,用于存放新添加的元素。
vector两个成员函数

  1. capacity 获取在容器需要分配更多的存储空间之前能够存储的元素总数
  2. reserve 告诉vector容器迎预留多少个元素的存储空间
    size指容器当前拥有的元素个数。capacity则指容器在必须分配新存储空间之前可以存储的元素总数。
    每当

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
只适用于string类型的操作:
3. substr函数,返回当前string对象的子串。
在这里插入图片描述

  1. append和replace函数,用于修改string对象。
    在这里插入图片描述
    在这里插入图片描述

  2. 一些列find函数,用于查找string对象。
    在这里插入图片描述
    在这里插入图片描述

find操作的返回类型是string::size_type。

string::size_type pos = 0;

while((pos=name.find_first_of(numerices,pos)) != string::npos)
{
	cout<<pos<<name[pos]<<endl;
	++pos;
}
  1. compare函数–strcmp()
    正数。负数。0
    在这里插入图片描述
2.容器适配器

适配器包括容器适配器,迭代适配器和函数适配器。
容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。
在这里插入图片描述
默认的stack和queue都是基于deque容器实现,而priority_queue则在vector容器上实现。
stack适配器所关联的基础容器可以是任意一种顺序容器类型。可以建立在vector、list或者deque容器之上。
queue适配器要求其关联的基础容器必须提供push_front运算,因此建立在list容器上。
priority_queue适配器要求提供随机访问功能,因此可以建立在vector或deque容器上。

#include <iostream>
#include <stack>

using namespace std;

int main()
{

    const stack<int>::size_type stk = 10;

    stack<int> intStack;

    int ix = 0;
    while (intStack.size != stk)
    {
        intStack.push(ix++);
    }

    int error_cnt = 0;
    while (intStack.empty() == false)
    {
        int value = intStack.top();
        if (value != --ix)
        {
            cerr << ix << value << endl;
            ++error_cnt;
        }
        intStack.pop();
    }
    cout << error_cnt << endl;
}
3.关联容器

关联容器通过键存储和读取元素,顺序容器通过元素在容器中的位置存储和访问元素。
map的元素以键值对的形式组织,键作用元素在map中的索引,而值则表示所存储和读取的数据。
set仅包含一个键,并有效的支持关于某个键是否存在的查询。
如果一个键对应多个实例,则需要使用multimap或multset。
pair
在这里插入图片描述

pair<string,string> next_auth;

next_auth = make_pair(first,last);
==
next_auth = pair<string,string>(first,last);

顺序容器和关联容器的公共操作:

  1. 前三种构造函数
    C c; Cc1(c2); C c(b,c);
  2. 关系运算符
  3. begin,end,rbegin,rend。
  4. 类型别名。typedef
  5. swap和赋值操作。不提供assign函数
  6. clear和erase操作。关联容器的erase运算返回void类型
  7. 关于容器大小的操作。resize函数不能用于关联容器。
4.Map

在这里插入图片描述
键类型必须定义<操作符,而且该操作符能正确工作。

在这里插入图片描述
map迭代器进行解引用将产生pair类型的对象。
map额外定义了两种类型:key_type和mapped_type,以获得键或值的类型。

map<string,int>::key_type
map添加元素
map<string,int> word_count;

word_count["A"] = 1;

发生顺序:

  1. 在word_count中查找键为A的元素。没有找到。
  2. 将一个新的兼职对插入到word_count中。键是const string类型的对象,保存A。而它的值进行初始化,0;
  3. 将这个新的键值对插入到word_count中。
  4. 读取新插入的元素,并将它的值赋为1.

map下标操作符(word_count[A])返回的类型(string)和对map迭代器进行解引用获得的类型不相同(pair)。

map插入操作:
在这里插入图片描述

word_count.insert(map<string,int>::value_type("A",1));

一个单词统计程序

    map<string, int> word_count;
    string word;
    while (cin >> word)
    {
        pair<map<string, int>::iterator, bool> res =
            word_count.insert(make_pair(word, 1));
        if (!res.second)
            ++res.first->second;
    }

查找并读取map中的元素
读取,如果不存在插入

map<string,int> word_count;
int occurs = word_count["F"];

仅查找
在这里插入图片描述
在这里插入图片描述

int orrurs = 0;
if(word_count.count("F"))
{
	occurs = word_count["F"];
}

map<string,int>::iterator it  = word_count.find("F");
if(it!=word_count.end()) occurs = it->second;

执行count后再使用下标操作符,实际上对元素进行两次查找。
find操作返回指向元素的迭代器,如果元素不存在,返回end迭代器。
如果希望当御酒指定键的元素存在时,就获得该元素的引用,也不在容器中创建元素,那么用find。

map删除元素
在这里插入图片描述
map对象迭代遍历

    map<string, int>::const_iterator
        map_it = word_count.begin();

    while (map_it != word_count.end())
    {
        cout << map_it.first << ' ' << map_it.second << endl;
        ++map_it;
    }

单词转换map对象
给出一个string对象,把它转换为另一个string对象。
将单词转换文件的内容存在一个map容器中,将被替换的单词作为键,而用作替换的单词作为其相应的值。然后读入,查找输入的每个单词是否对应有转换。如果有,实现转换,然后输出其转换后的单词,否则,直接输出原词。


int main()
{
    map<string, string> trans_map;
    string key, value;
    ifstream map_file;
    while (map_file >> key >> value)
    {
        trans_map.insert(make_pair(key, value));
    }
    ifstream input;

    string line;
    while (getline(input, line))
    {
        istringstream stream(line);
        string word;
        bool firstword = true;
        while (stream >> word)
        {
            map<string, string>::const_iterator map_it =
                trans_map.find(word);
            if (map_it != trans_map.end())
            {
                word = map_it->second;
            }
            if (firstword)
            {
                firstword = false;
            }
            else
            {
                cout << " ";
            }
            cout << word;
        }
        cout << endl;
    }
}

第七天

1.set

set容器只是单纯的键的集合。 不重复

set容器支持大部分map操作。

  1. 10.2列出的所有通用的容器操作。
  2. 10.3 描述的构造函数
  3. 10.5描述的insert操作
  4. 10.6描述的count和find操作。
  5. 10.7描述的erase操作。

不支持的操作:

  1. 不支持下标操作。
  2. 没有定义mapped_type类型。

set中添加元素

set<string> set1;
set1.insert("Tne");

set<int> iset2;
iset2.insert(ivec.begin(),ivec.edn());                  

带有一个键参数的insert版本返回pair类型对象,包含一个迭代器和一个bool值。
从set中获取元素
set容器不提供下标操作符。使用find运算。
如果使用count运算,返回的只能是1(存在)或0(不存在)。

删除指定文件中的所有的单词。
void restricted_wc(ifstream &remove_file, map<string, int> &word_count)
{
    set<string> excluded;
    while (remove_file >> remove_word)
    {
        excluded.insert(remove_word);
    }
    string word;
    while (cin >> word)
    {
        if (!excluded.count(word))
            ++word_count[word];
    }
}

multimap和multiset
允许一个键对应多个实例。
multimap 不支持下标运算。

authors.insert(make_pair(string("B"),string("S")));
authors.insert(make_pair(string("B"),string("SI")));

带有一个键参数的erase版本将删除拥有该键的所有元素,并返回删除元素的个数。

multimap<string,string> authors;
string serch_item("K");

multimap<string,string>::size_type cnt = anthors.erase(search_item);

迭代遍历multimap或multiset容器时,可保证依次返回特定键所关联的所有元素。

某键对应的元素可能出现多次。假设有作者与书名的映射,找到并输出某个作者写的所有书的书名。

使用find和count操作。

string search_item("A");

typedef multimap<string,string>::size_type sz_type;
sz_type entries = authors.count(search_item);

multimap<string,string>::iterator iter = authors.find(search_item);

for(sz_type cnt = 0;cnt!=entries;++cnt,++iter)
	cout << iter->second<<endl;

在这里插入图片描述

typedef multimap<string,string>::iterator authors_it;
authors_it beg=  authors.lower_cound(search_item),
		end = authors.upper_cound(search_item);

while(beg!=end){
	cout<<beg->second<<endl;
	++beg;
}


pair<authors_it,authors_it> pos = authors.equal_range(search_item);

while(pos.first!=pos.second)
{
	cout<<pos.first->second<<endl;
	++pos.first;
}

实现一个简单的文本查询程序
读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。
查询结果是该单词出现的次数,并列出每次出现的所在行。

  1. 使用一个vector类型的对象存储整个输入文件的副本。输入文件的每一行是该vector对象的一个元素。
  2. 将每个单词所在的行号存储在一个set容器对象中。
  3. 使用一个map容器将每个单词与一个set容器对象关联起来。

class TextQuery
{
public:
    typedef vector<string>::size_type line_no;

    void read_file(std::ifstream &is)
    {
        store_file(is);
        build_map();
    }
    set<line_no> run_query(const string &) const;
    string text_line(line_no) const;

private:
    void store_file(ifstream &);
    void build_map();

    vector<string> line_of_text;
    map<string, set<line_no>> word_map;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老李头带你看世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值