一、C++输入输出流IO
(一)基础知识概念
-
什么是“流”?
流=字节流:①流向内存→输入流;②流向设备→输出流 -
流的3种类型(继承关系)
(1)标准IO:标准的输入输出流
(2)文件IO:文件输入输出流
(3)串IO :字符串的输入输出流 -
流的4种状态
-
缓冲区
(1)目的:解决内存和I/O设备速度不匹配
(2)类型:全缓冲/满缓冲、行缓冲(endl)、不带缓冲(cerr)
(3)缓冲区的刷新:
①程序正常结束的时候,会刷新缓冲区
②缓冲区满的时候,也会进行刷新
③可以手动刷新,endl、flush
endl //刷新并换行
flush //只刷新不换行
ends //既不刷新,也不换行
二、文件IO
(一)文件输入流ifstream——输入进去才能读
//1. 空的
basic_ifstream();
//2. 绑定文件.txt
explicit basic_ifstream( const char* filename,
std::ios_base::openmode mode = ios_base::in );
//3. C++字符串
explicit basic_ifstream( const std::string& filename,
std::ios_base::openmode mode = ios_base::in );
//explicit清楚的,明白的,清晰的,为了防止隐式转换的。
- explicit清楚的,明白的——为了防止隐式转换的
(1)什么是隐式转换?
如:
①Point pt2 = 10; // int类型的10 ====>Point(10,0)
②“hello,world”; //String s1 = ”hello,world” ====> String(“hello,world”)
(2)隐式转换→显示转换,在相应的位置上加“explicit”
(3)对于文件输入流而言
①若文件存在→打开成功;不存在→打开失败;
②文件流去读文件,以空格为分隔符
1 #include <iostream>
2 #include <fstream>//文件流
3
4 using std::cout;
5 using std::endl;
6 using std::ifstream;
7 using std::cerr;//不带缓冲
8 using std::string;
9
10 void test()
11 {
12 ifstream ifs("test.txt");//创建文件,打开文件
13 //因为是文件流打开文件,有可能打开失败
14
15 if(!ifs.good())//若状态不是正常态
16 {
17 //错误就不要用缓冲区了,即不用cout,直接用cerr就行
18 cerr << "ifstream is not good" << endl;//文件没有打开
19 return;//文件没有打开,就直接退出
20 }
21
22 //文件流读文件——while循环,读到南里去呢?用string来接收
23 string line;
24 while(ifs >> line)//ifs.efo()不到文件末尾,就一直循环输入——可由可不有
25 {
26 cout << line;
27 }
28
29
30 ifs.close();//写完打开,就要写关闭
31 }
32
✹ 33 int main(int argc, char **argv)
34 {
35 test();
36 return 0;
37 }
1. getline按行读取→能读到与源文件一样的东西
template< class CharT, class Traits, class Allocator >
std::basic_istream<CharT,Traits>& getline( std::basic_istream<CharT,Traits>& input,
std::basic_string<CharT,Traits,Allocator>& str );
std::basic_istream& getline( std::basic_istream& input, std::basic_string& str );
//主要是将while循环的条件改变→getline(ifs,line),并改变line默认以空格分隔→用endl
while(getline(ifs,line))//ifs.efo()不到文件末尾,就一直循环输入??可由可不有
25 {
26 cout << line << endl;
27 }
//完整版
1 #include <iostream>
2 #include <fstream>//文件流
3
4 using std::cout;
5 using std::endl;
6 using std::ifstream;
7 using std::cerr;//不带缓冲
8 using std::string;
9
10 void test()
11 {
12 ifstream ifs("test.txt");//创建文件,打开文件
13 //因为是文件流打开文件,有可能打开失败
14
15 if(!ifs.good())//若状态不是正常态
16 {
17 //错误就不要用缓冲区了,即不用cout,直接用cerr就行
18 cerr << "ifstream is not good" << endl;//文件没有打开
19 return;//文件没有打开,就直接退出
20 }
21
22 //文件流读文件——while循环,读到南里去呢?用string来接收
23 string line;
24 while(getline(ifs,line))//ifs.efo()不到文件末尾,就一直循环输入——可由可不有
25 {
26 cout << line << endl;
27 }
28
29
30 ifs.close();//写完打开,就要写关闭
31 }
32
✹ 33 int main(int argc, char **argv)
34 {
35 test();
36 return 0;
37 }
2. 若想打印指定行,该怎么办?→ 先存,再打印
思路一:用数组存,再输出
//文件流读文件??while循环,读到南里去呢?用string来接收
23 string line[60];
24 size_t idx = 0;//size_t是无符号整数
25 while(getline(ifs,line[idx]))
26 {
27 ++idx;//这样每行对会存入对应的line[idx]
28 }
29
30 //第43行,下标42
31 cout << "line[42] = " << line[42] << endl;
32
33 ifs.close();//写完打开,就要写关闭
34 }
报错了 → 可以用容器vector来解决数组溢出的问题
思路二:用容器vector存,再输出
1° 容器是一种数据结构
2° 语法:vector<对象类型> vec; → vector<string> vec;
3° 输出时,用vector类中的成员函数push_back
4° 读43行,用vec[42](从0开始)
1 #include <iostream>
2 #include <fstream>//文件流
3 #include <string>
4 #include <vector>
5
6 using std::cout;
7 using std::endl;
8 using std::ifstream;
9 using std::cerr;//不带缓冲
10 using std::string;
11 using std::vector;
12
13 void test()
14 {
15 string filename("test.txt");
16 ifstream ifs(filename);//这样读的时候,从文本变成了C++字符串
17
18 if(!ifs.good())//若状态不是正常态
19 {
20 //错误就不要用缓冲区了,即不用cout,直接用cerr就行
21 cerr << "ifstream is not good" << endl;//文件没有打开
22 return;//文件没有打开,就直接退出
23 }
24
25 //文件流读文件——while循环,读到南里去呢?用string来接收
26 string line;
27 //创建一个对象,名为vec
28 vector<string> vec;//容器中,存储string类型,<string>写在后面了,不同于int arr[]
29
30 while(getline(ifs,line))
31 {
32 vec.push_back(line);
33 }
34
35 cout << "vec[42] = " << vec[42] << endl;
36 ifs.close();//写完打开,就要写关闭
38 }
39
✹ 40 int main(int argc, char **argv)
41 {
42 test();
43 return 0;
44 }
3. 若想用vector打印出文本.txt的全部内容
用vector对象中的size()函数,得知容器中元素的个数
37 //打印文本全部内容
38 for(size_t idx = 0; idx != vec.size(); ++idx)
39 {
40 cout << vec[idx] << endl;
41 }
(二)文件输出流ofstream——输出了写
basic_ofstream();
explicit basic_ofstream( const char* filename,
std::ios_base::openmode mode = ios_base::out );
explicit basic_ofstream( const std::string& filename,
std::ios_base::openmode mode = ios_base::out );
对于文件输出流而言:
①当文件不存在的时候→创建文件
②当文件存在的时候 → 清空文件
1. 想把读ifs方式打开的文件,写到ofs的文件中去
- 怎么做呢?
【注】ofs中,打开文件失败,则要在if语句中,把ifs的文件关掉
25 string filename2("wd.txt");
26 ofstream ofs(filename2);//这样写的时候,从文本变成了C++字符串
27 if(!ofs.good())//若状态不是正常态
28 {
29 cerr << "ofstream is not good" << endl;
30 ifs.close();
31 return;
32 }
- 如何把ifs的内容,写到ofs中去呢?
【思路】while循环
1 #include <iostream>
2 #include <fstream>//文件流
3 #include <string>
4 #include <vector>
5
6 using std::cout;
7 using std::endl;
8 using std::ifstream;
9 using std::ofstream;
10 using std::cerr;//不带缓冲
11 using std::string;
12 using std::vector;
13
14 void test()
15 { //以读的方式打开
16 string filename("test.txt");
17 ifstream ifs(filename);
18 if(!ifs.good())
19 {
20 cerr << "ifstream is not good" << endl;
21 return;
22 }
23
24 //以写的方式打开
25 string filename2("wd.txt");
26 ofstream ofs(filename2);//这样写的时候,从文本变成了C++字符串
27 if(!ofs.good())//若状态不是正常态
28 {
29 cerr << "ofstream is not good" << endl;
30 ifs.close();
31 return;
32 }
33
34 //文件流写文件——while循环,读到哪里去呢?用string来接收
35 string line;
36 while(getline(ifs,line))
37 {
38 ofs << line << endl;//写入ofstream的对象ofs中
39 }
40
41 ifs.close();//记得关闭
42 ofs.close();//记得关闭
43 }
44
✹ 45 int main(int argc, char **argv)
46 {
47 test();
48 return 0;
49 }
… …
(三)文件输入输出流fstream
basic_fstream();
explicit basic_fstream(const char* filename,
std::ios_base::openmode mode = ios_base::in|ios_base::out );
explicit basic_fstream(const std::string& filename,
std::ios_base::openmode mode = ios_base::in|ios_base::out );
对于文件的输入输出流而言:
①当文件不存在 → 文件打开失败,要自己生成
②当文件存在 → 文件正常操作(要先生成,则存在文件)
1. 从键盘输入数据,通过fs写到文件中
26 //1. 从键盘输入数据,通过fs写到文件中
27 int number = 0;
28 for(size_t idx = 0; idx != 5; ++idx)
29 {
30 cin >> number;//从键盘输入
31 fs << number << " ";//通过fs写入
32 }
33 cout << endl;
2. 通过fs进行读文件,将数据输出到屏幕
34 //1. 从键盘输入数据,通过fs写到文件中
35 for(size_t idx = 0; idx != 5; ++idx)
36 {
37 fs >> number;//通过fs读出
38 cout << number << "";//输出到屏幕
39 }
【bug】可知,1 2 3 4 5成功写入到wuhan.txt中了,但是读出的时候全都是5
【分析调代码】流的状态可能出bug了
∵已经成功输入1 2 3 4 5,故33行之前的代码没有问题
∴出现bug在34行后
35 //2. 通过fs读出文件。并输出到屏幕
36 for(size_t idx = 0; idx != 5; ++idx)
37 {
38 //调试
39 cout << "fs.failbit = " << fs.fail() << endl
40 << "fs.eofbit = " << fs.eof() << endl
41 << "fs.goodbit = " << fs.good() << endl;
42 fs >> number;//通过fs读出
43 cout << number << " ";//输出到屏幕
44 }
45 cout << endl;
(1)可知,第一轮的时候(001)是正常的;从第二轮开始出问题(110)
failbit=1、eofbit=1
(2)解决思路:文件指针到达了文件末尾,要偏移到文件头部
(3)解决操作:
· C语言:① 通过ftell()得到文件长度;②通过fseek()调整文件指针
· C++:① 通过tellg()获取文件指针位置;②通过seekg()偏移文件指针
【注意】
- 用文件输入流ifstream时:只能用“g”——记忆:我寄ig
- 用文件输出流ofstream时:只能用“p”——记忆:原批op
- 用文件IO流fstream时:都能用
【掌握偏移seekp()的用法】
- 绝对位置:fs.seekp(0);//直接移动到0处
- 相对位置:fs.seekp(-10,std::ios::end);//相对于尾部end,倒退10B
35 //tellp/tellg(p:put读;g:get写)
36 size_t len = fs.tellp();//求文件长度
37 cout << "文件长度/偏移了len = " << len << "B" << endl;
38 //seekp/seekg(p:put写;g:get读)
39 fs.seekp(0);//绝对位置:移动文件指针于文件头部0
/* fs.seekp(-10,std::ios::end); 相对位置 */
40 len = fs.tellp();
41 cout << "文件指针偏移了len = " << len << "B" << endl;
42
(四)文件模式
文件模式 | 作用 |
---|---|
in | 输入 |
out | 输出 |
☆app | 尾部追加(针对于写操作ofream,记忆:OP) |
☆ate | 末尾(针对于读操作ifream) |
trunc | 截断:若打开文件存在,则内容丢弃,其大小被截断为0 |
ate | 二进制,读写的文件为二进制形式 |
1. 文件输入(末尾ate)
16 void test()
17 { //以读的方式打开
18 ifstream ifs("test.txt",std::ios::in | std::ios::ate);//到文件末尾
19 if(!ifs.good())
20 {
21 cerr << "fstream is not good" << endl;
22 return;
23 }
24
25 //输出ifs的位置/长度
26 cout << "ifs.tellg() = " << ifs.tellg() << endl;
27 ifs.close();
30 }
31
✹ 32 int main(int argc, char **argv)
33 {
34 test();
35 return 0;
36 }
2. 文件输出(末尾app)
32 void test2()
33 { //以写的方式打开
34 ofstream ofs("wd.txt",std::ios::out | std::ios::app);//到文件末尾
35 if(!ofs.good())
36 {
37 cerr << "ofstream is not good" << endl;
38 return;
39 }
40
41 //输出ofs的位置/长度
42 cout << "ofs.tellp() = " << ofs.tellp() << endl;
43
- 因为文件存在,若34行如下 → ofstream会首先会清空文件
34 ofstream ofs("wd.txt");
- 从末尾追加内容(app)
三、字符串IO / 内存流 / 内存IO
istringstream | 字符串输入流 |
---|---|
ostringstream | 字符串输出流 |
stringstream | 字符串输入输出流 |
//委托构造函数
basic_ostringstream() : basic_ostringstream(ios_base::out) { }
explicit basic_ostringstream( ios_base::openmode mode = ios_base::out );
explicit basic_ostringstream( ios_base::openmode mode );
explicit basic_ostringstream( const std::basic_string<CharT,Traits,Allocator>& str,
ios_base::openmode mode = ios_base::out );
(一)把int转换为字符串string
1 #include <iostream>
2 #include <sstream>
3 #include <string>
4
5 using std::cout;
6 using std::endl;
7 using std::ostringstream;
8 using std::istringstream;
9 using std::stringstream;
10 using std::string;
11
12 string int2String(int value)
13 {
14 ostringstream oss; //通过ostring创建空对象(中转)
15 oss << value;//把值输入进去
16
17 return oss.str(); //获取底层的字符串
18 }
19
20 void test()
21 {
22 int number = 10;
23 string s1 = int2String(number);
24 cout << "s1 = " << s1 << endl;
25 }
26
27 int main(int argc, char **argv)
28 {
29 test();
30 return 0;
31 }
【注意】用函数str(); //获取底层的字符串
以字符串形式输出了:
(二)先通过stringstram得到字符串,然后可拆开
1 #include <iostream>
2 #include <sstream>
3 #include <string>
4
5 using std::cout;
6 using std::endl;
7 using std::ostringstream;
8 using std::istringstream;
9 using std::stringstream;
10 using std::string;
27 void test2()
28 {
29 int number1 = 10;
30 int number2 = 20;
31 stringstream ss;
32 //先输出到ss里面
33 ss << "number1= " << number1
34 << " ,number2= " << number2 << endl;
35 string s1 = ss.str();
36 cout << s1 << endl;
37
38 string key;
39 int value;
40 while(ss >> key >> value)
41 {//ss现在是number1= 10,number2= 20
42 //ss把字符串部分给key,int部分给value————利用了空格
43 cout << key << "------->" << value << endl;
44 }
45 }
46
✹ 47 int main(int argc, char **argv)
48 {
49 test2();
50 return 0;
51 }
(三)拆开字符串部分和数字部分
若my.Conf内容如下:
1 #include <iostream>
2 #include <sstream>
3 #include <string>
4 #include <fstream>
5
6 using std::cout;
7 using std::endl;
8 using std::cerr;
9 using std::ifstream;
10 using std::ostringstream;
11 using std::istringstream;
12 using std::stringstream;
13 using std::string;
50 void readConfig(const string &filename)
51 {
52 ifstream ifs(filename);
53 if(!ifs)//!ifs.good()
54 {
55 cerr << "open " << filename << "error!" << endl;
56 return;
57 }
58
59 string line;
60 while(getline(ifs,line))
61 {
62 istringstream iss(line);
63 string key,value;
64 iss >> key >> value;//拆分
65
66 cout << key << " " << value << endl;
67 }
68
69
70 ifs.close();
71 }
72
73 void test3()
74 {
75 readConfig("my.Config");
76 }
77
✹ 78 int main(int argc, char **argv)
79 {
80 test3();
81 return 0;
82 }
四、容器vector的原理
(一)vec.size()和vec.capacity()
vec.size() | vec.capacity() |
---|---|
容器中,元素的个数 | 容器的大小 |
若不用push_back()赋值,则容器大小初始化为0
1 #include <iostream>
2 #include <vector>
3
4 using std::cout;
5 using std::endl;
6 using std::vector;
7 //打印vector的容量和元素个数
8 void printVectorCapacity(const vector<int> &vec)
9 {
10 cout << "容器的容量大小capacity = " << vec.capacity() << endl;
11 cout << "容器中元素个数size = " << vec.size()<< endl;
12 }
13
14 void test()
15 {
16 vector<int> vec;//容器里面存int类型的数据
17 printVectorCapacity(vec);//若不赋值,初始化为0
18
19 cout << endl;
20 vec.push_back(1);
21 printVectorCapacity(vec);
22
23 cout << endl;
24 vec.push_back(2);
25 printVectorCapacity(vec);
26
28 cout << endl;
29 vec.push_back(3);
30 printVectorCapacity(vec);
32
33 cout << endl;
34 vec.push_back(4);
35 printVectorCapacity(vec);
36
38 cout << endl;
39 vec.push_back(5);
40 printVectorCapacity(vec);
41
43 cout << endl;
44 vec.push_back(6);
45 printVectorCapacity(vec);
46 }
47
48 int main(int argc, char **argv)
49 {
50 test();
51 return 0;
52 }
- 底层的扩容原理:当size()==capacity(),会按照2*capacity()扩容,而扩容,是连续重新申请分配
- 新申请的空间在新的位置,然后再将旧空间的数据——拷贝—→到新空间,然后将旧空间回收
(二)其他vector对象,常用成员函数的用法
成员函数 | 作用 |
---|---|
push_back() | 插入新元素,插入位置:当前最后一个元素的下一个元素 |
pop_back() | 移除最后一个元素 |
clear() | 清空所有元素 |
empty() | 判断vector是否为空,如果返回true为空 |
erase() | 删除指定元素 |
//在vec尾部直接添加10
vector<int> vec;
vec.push_back(10);
//在vec尾部间接添加10
int num = 10;
vector<int> vec;
vec.push_back(num);
//在string字符串最后插入一个字符
string str;
str.push_back('d');