C++单元小结之Vector与迭代器(续),内置数组、vector和string,文件数据处理(文件操作续),字符串流(续)
C++单元小结
Vector(续)
vector的用法:
vector是长度可变的向量,可替代内置数组,更灵活,更高效。
要使用vector,必须包含头文件
定义vector时必须指定元素的类型,格式为:
vector<元素类型> 变量名;
定义的同时可以初始化vector对象。
下面是几种常见的初始化方法:
//拷贝初始化
vector<int> ivec2(ivec); // 把ivec的元素复制给ivec2
vector<int> ivec3 = ivec; // 把ivec的元素复制给ivec3
这时拷贝的vector元素的数据类型必须一致,否则出现错误。另外列表进行初始化时,必须使用大括号,小括号则显示错误。
在使用时我们需要注意容易混淆的问题:各种括号
vector<int> v1(10);//v1有10个int元素,每个都初始化为0
vector<int> v2{10}; //v2有1个元素,值是10
vector<int> v3[10]; //v3是有10个元素的数组
//每个元素都是一个空vector对象
vector<int> v4(10, 1); //v4有10个int元素,每个都初始化为1
vector<int> v5{10, 1}; //v5有2个int元素,值分别是10和1
vector支持的操作:
注意事项:
如果要用循环向vector对象中添加元素,不能使用范围for,范围for不能改变所遍历的容器的大小
不能用下标运算符向vector中添加元素
下标运算符只能访问已经存在的元素
迭代器(续)
迭代器类似于指针类型,提供对对象的间接访问
迭代器在容器或string对象上使用,指向的对象是容器中的元素或string中的字符
访问标准容器的元素的通用方法是使用迭代器
使用迭代器可以访问容器中的某个元素,也可以在容器上移动
标准库容器迭代器的类型
iterator、const_iterator
cbegin()和cend()函数返回const_iterator
//cbegin/cend直接返回const_iterator,无论容器是否是const的,例:
auto it3 = v.cbegin();
//it3类型是vector::const_iterator
内置数组、vector和string:
使用vector和string的代码比使用内置数组和字符数组更简洁、更高效
string和vector都是可变长度的
灵活,编写相应的代码比使用动态内存管理更容易且不易出错
string和vector是对象
封装带来的简单性,对接口编程
虽然不知道string类和vector类型具体是如何实现的,但是并不妨碍正确地使用它们
文件数据处理
文件读写——输入输出重定向
将标准输入和标准输出与命名文件关联起来
c:> program outputfile
运行名为program.exe的可执行程序,用文件inputfile作为输入,用文件outputfile作为输出
文件读写——标准库文件流
C++标准库中的文件流:使用文件流要包含头文件
ifstream类:用来输入(读文件)的文件流
ofstream类:用来输出(写文件)的文件流
fstream类:把文件连接到流对象用来输入和输出
istream(ostream、iostream)的操作在ifstream(ofstream、fstream)中都可以使用
文件操作的步骤
(1)新建一个文件流对象
读文件用ifstream,写文件用ofstream;
(2)把文件流对象和文件关联起来
打开文件,使用文件流的open函数;
可以指定打开文件的模式
(3)操作文件流
使用与终端I/O相同的操作读写文件
(4)关闭文件流
使用文件流的close函数
例:
#include <fstream> //文件流标准库头文件
using namespace std;
int main(){
ifstream in("numbers.txt");
int number, sum = 0;
in >> number;
while(number != 0){
sum += number;
in >> number;
}
in.close();
ofstream out;
out.open("output.txt");
out << "sum is: " << sum << endl;
out.close();
}
具体实例:
插入文件(将文件2中的数据插入到1中):
#include<cstdio>
#include<iostream>
using namespace std;
void append(FILE*,FILE*);
int main()
{
FILE*f1,*f2;
char *fname1="data1.txt";
char *fname2="data2.txt";
f1=fopen(fname1,"a+");
f2=fopen(fname2,"r");
append(f1,f2);
fclose(f1);
fclose(f2);
printf("DONE.\n");
}
void append(FILE*f1,FILE*f2)
{
char c;
fseek(f1,0,2);
while((c=getc(f2))!=EOF)
putc(c,f1);
}
复制文件:
#include<stdio.h>
void fcopy(FILE*,FILE*);
int main(void)
{
FILE*f1,*f2;
f1=fopen("1.txt","r");
f2=fopen("2.txt","w");
fcopy(f1,f2);
fclose(f1);
fclose(f2);
}
void fcopy(FILE*f1,FILE*f2)
{
char c;
while((c=getc(f1))!=EOF)
putc(c,f2);
}
这里对于int main(),int main(void)与void main()的区别如下:
#include<stdio.h>
void main(){
}
#include<stdio.h>
int main(void){
return 0;
}
void main() 这么定义只是说明main函数没有返回值
int main(void)和int main()是一样的,括号里没有东西就相当于void,
只是说明main函数有一个int类型的返回值
C语言的规范里面要求main函数需要返回一个int类型的返回值来告诉操作系统这个程序是执行正确还是执行错误。执行正确返回0,执行错误返回非0。但是目前操作系统都不去判断这个返回值,所以写成void main()还是int main()其实是一样的,没有什么区别。不过既然有规范,还是推荐写成int main()这种形式。(C++里不支持void main()这种形式,只能写成int main()。)
字符串流(续)
字符串流用于内存I/O,即在字符串上进行I/O操作
istringstream:从string对象中读取数据
ostringstream:向string对象写入格式化的内容
stringstream:从字符串读取数据,将数据写入字符串
使用时包含标准库头文件
在定义时直接用string对象初始化
strm.str(s),将字符串s复制到字符串流strm中,返回void
strm.str(),返回字符串流strm中保存的字符串的副本
字符串流的用途:
输入缓冲区
从输入流一次性读取一大块数据,以字符串形式保存在istringstream对象中,再用适当的输入操作逐项从istringstream对象中提取各个数据项
输出格式化
将所有要输出的内容先用输出操作写到ostringstream对象上,再一次性的将这个ostringstream对象中保存的字符串写入输出流
例:读取文件中的学生成绩记录,统计并将结果输出到另一个文件
最终程序中应用了C++的结构体、string、vector、迭代器、文件流、字符串流等类型:
//利用istringstream,读取并处理一行学生记录的代码
//从输入文件流in中读一行学生成绩并存入scoreSheet中
ScoreItem item;
string buffer;
getline(in, buffer);
istringstream is(buffer);
is >> item.name;
int score;
while(is >> score)
item.scores.push_back(score);
scoreSheet.push_back(item);
//利用ostringstream构造并输出一个学生的统计信息的代码
//处理scoreSheet中的一个元素,即一个学生的记录
ostringstream format("");
format << (it -> name) << " ";
//按格式将数据继续写入format
format.precision(1); //小数点后保留1位
format << sum << " "
<< courseNumber << " "
<< fixed << average;
//构造的输出信息格式为:姓名 总成绩 课程门数 平均成绩
out << format.str() << endl;
//一次性写入输出文件流out中
从该程序中,我们可以利用复合类型,根据实际应用中的问题定义恰当的数据结构。
因此合理地利用标准库提供的类型,可以使编程更简便、更高效。