# C++基础6-文件
本文是《C++ Primer Plus》的笔记,本文中的案例均自己实践过,如需转发请在转发开头贴上原文地址,谢谢!
# 文件的输入和输出
# 基本操作
C++ 处理文件IO和标准IO很相似,定义了ifstream和ofstream类,此外C++还定义了一个fstream类用于同步I/O。这些类均从iostream派生而来,可以使用之前介绍的方法。
ifstream fin;
上面定义了一个ifstream,即文件输入流的管理器,这个对象可以打开多个文件,这样多个文件便可以使用一个缓冲区,节省系统资源。
#include <fstream>
string filename;
string str1;
char ch1[20];
ofstream fout;
cin >> filename;
//filename.c_str();
fout.open(filename);
//if (!fout.fail()) {
//if (fout.good()) {
//if (fout) {
//注意上面这些测试无法检测一种情形:试图以不合适的文件模式打开文件时失败。而is_open()能够检测到这种错误以及good()检测到的错误。
if(fout.is_open()){
fout << "hello world!" << endl;
fout << "hi world!";
fout.close();
}
//如果不使用fout.close(),上面的"hi world!"不会被写入到str1中,因为这个字符串还在输入缓存区中待着。
ifstream fin;
fin.open(filename);
if (fin.is_open()) {
fin.getline(ch1, 30, '\n');
getline(fin, str1);
cout << ch1 << endl;
cout << str1 << endl;
fin.close();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
同时读取文件:
string filename="hello.txt";
char ch1[100];
fstream fout;
fout.open(filename, ios_base::in | ios_base::out | ios_base::app);
if (fout.is_open()) {
fout << "hello world xie!";
fout.seekp(0);
//fout.seekg(0);
//fstream类使用缓存区来存储中间数据,因此指针指向的是缓存区中的位置,由于文件只有一个缓存区,seekg和seekp都会改变文件指针的位置。
fout.get(ch1, 100, EOF);
cout << ch1;
fout.close();
}
2
3
4
5
6
7
8
9
10
11
12
13
# 文件模式
文件模式描述的是文件将被如何使用:读,写,追加等。一般是文件流对象和具体文件关联时的第二个参数:
打开文件的方法:
方法一:
ifstream fin("file1",mode1);
方法二:
ofstream fout();
fout.open("file2",mode2);
上面mode1,mode2可以用 ios_base::out | ios_base::app 来表示同时使用多个文件模式。
2
3
4
5
6
7
ios_base 定义了一个openmode类型,也是一种bitmask类型,选择多个这样的常量来指定模式:
文件模式常量 | 含义 |
---|---|
ios_base::in | 打开文件准备读取 |
ios_base::out | 打开文件准备写入 |
ios_base::ate | 将文件指针设置在文件末尾,默认是文件开头 |
ios_base::app | 所有输出操作只在文件末尾执行 |
ios_base::trunc | 如果文件存在,截短文件 |
ios_base::binary | 以二进制的方式打开文件 |
C语言的打开模式对应于C++模式解析
打开模式 | 读写状态 | 目标文件不存在 | 打开文件后文件指针位置 | 是否清空原有内容 | 读取位置 | 写入位置 | C++模式 |
---|---|---|---|---|---|---|---|
r | 只读 | 打开失败 | 开头 | 否 | 任意位置 | —— | in |
w | 只写 | 新建 | 开头 | 是 | —— | 任意位置 | out 或者 out | trunc |
a | 只写 | 新建 | 结尾 | 否 | —— | 尾部写入 | out | app |
r+ | 读写 | 打开失败 | 开头 | 否 | 任意位置 | 任意位置 | in | out |
w+ | 读写 | 新建 | 开头 | 是 | 任意位置 | 任意位置 | in | out | trunc |
a+ | 读写 | 新建 | 结尾 | 否 | 任意位置 | 尾部写入 | in | out | app |
注意:
- 对于只写的流,out | trunc 等同于 out。对于读写的流,要打开文件时就截断文件,必须明确指定,即 in | out | trunc。
- app模式,即便是重新将文件指针指向头,也不起作用。ate模式将指针指向头,可将文件指针重新指向到文件头。
- 上面的所有操作加上b即为二进制文件操作
# 二进制文件
将数据存储在文件中时,可存储为文本格式或二进制格式。上面我们所说的都是文本格式,即将所有内容都存储为文本,这就需要在存储前将数据转换下。如果内容是文本的话,用文本格式存取比较方便,使用编辑器或字处理来读取和编辑文本文件,从一个计算机系统传输到另一个计算机系统。
而二进制格式对于数字来说比较精确,因为它存储的是值得内部表示,不会有转换误差或者舍入误差,以二进制保存数据更快因为有不需要转换。但是同一个系统上不同的编译器可能使用不同的内部存储结构,这就可能导致不兼容的问题。
以二进制格式存储数据,要使用write()成员函数,它只是逐字节地复制数据而不进行任何转换,唯一不方便的是需要将地址强制转换为指向char的指针。从二进制文件中读数据的话要使用read()函数,下面请看实例:
struct person {
//string name;如果改成string的话就会有问题
char name[20];
int age;
}p1,p2,p3,p4;
cout << "Input Person1 name:" ;
cin >> p1.name;
cout << "Input Person1 age:" ;
cin >> p1.age;
cout << "Input Person2 name:" ;
cin >> p2.name;
cout << "Input Person2 age:" ;
cin >> p2.age;
string filename = "testbin";
ifstream fromfile;
ofstream tofile;
fromfile.open(filename,ios_base::in|ios_base::binary);
tofile.open(filename,ios_base::out|ios_base::binary);
if (tofile.is_open()) {
tofile.write((char *) &p1,sizeof(p1));
tofile.write((char *) &p2,sizeof(p2));
tofile.close();
}
if (fromfile.is_open()) {
fromfile.read((char *) &p3,sizeof(p3));
fromfile.read((char *) &p4,sizeof(p4));
fromfile.close();
}
cout << p3.name << ";" << p3.age << endl;
cout << p4.name << ";" << p4.age << endl;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 随机存取
移动输入指针的位置使用seekg,它的原型为:
istream& seekg (streampos pos);
istream& seekg (streamoff off, ios_base::seekdir way);
获取输入指针的位置使用tellg,它的原型为:
streampos tellg();
移动输出指针的位置使用seekp,它的原型为:
ostream& seekp (streampos pos);
ostream& seekp (streamoff off, ios_base::seekdir way);
获取输出指针的位置使用tellp,它的原型为:
streampos tellp();
- 只获取一个偏移值的原型,偏移值是从开头算起。
- 指定偏移量的起始位置,seekdir可以取值:ios_base::beg (开头),ios_base::cur (中间),ios_base::end (结尾)。
- fstream类使用缓存区来存储中间数据,因此指针指向的是缓存区中的位置,当使用in | out 打开文件时,由于文件只有一个缓存区,seekg和seekp都会改变文件指针的位置。
实例:
struct person {
//string name;如果改成string的话就会有问题
char name[20];
int age;
}p1, p2, p3, p4;
cout << "Input Person1 name:";
cin >> p1.name;
cout << "Input Person1 age:";
cin >> p1.age;
cout << "Input Person2 name:";
cin >> p2.name;
cout << "Input Person2 age:";
cin >> p2.age;
string filename = "testbin";
ifstream fromfile;
ofstream tofile;
fromfile.open(filename, ios_base::in | ios_base::binary);
tofile.open(filename, ios_base::out | ios_base::binary);
if (tofile.is_open()) {
tofile.write((char *)&p1, sizeof(p1));
tofile.write((char *)&p2, sizeof(p2));
tofile.close();
}
if (fromfile.is_open()) {
fromfile.seekg(sizeof(person));
fromfile.read((char *)&p3, sizeof(p3));
fromfile.seekg(0);
fromfile.read((char *)&p4, sizeof(p4));
fromfile.close();
}
cout << p3.name << ";" << p3.age << endl;
cout << p4.name << ";" << p4.age << endl;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 内核格式化
iostream相关类支持程序与终端之间的I/O,而fstream相关类使用相同的接口提供程序和文件之间的I/O。此外还有sstream相关类,使用相同的接口提供程序和string对象之间的I/O。也就是说可以使用cout的ostream的方法将格式化信息写入到string对象中,并使用istream方法(比如getline())来读取string对象中的信息。这种读取string对象中的格式化信息或将格式化信息写入string对象中称为内核格式化(incore formatting)。
#include <sstream>
ostringstream outStr;
string str1;
int age;
cout << "Input your name:";
getline(cin, str1);
cout << "Input your age:";
cin >> age;
outStr << str1 << "现在" << age << "岁了!" <<endl;
string result = outStr.str();
cout << result;
string word;
istringstream inStr("this is a apple");
while (inStr >> word) {
cout << word << endl;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
上面就是从标准输入到字符串,和从字符串到标准输出。怎么方便记忆3大IO的输入输出方向呢?
输入:从 输入 get信息
输出:put信息 到 输出
比如文件,ifstream 是从文件中获取信息,ofstream是将信息推送到文件。
比如stream,istringstream 是从string中获取信息,ostringstream是将信息推送到string中。
# C++程序命令行接收参数
定义主程序:
int main(int argc,char *argv[])
{
for (int i = 0; i < argc; i++) {
cout << argv[i] << endl;
}
}
在命令行输入:
.\BasicPrimer.exe h1 h2 h3
得到输出:
h1
h2
h3
2
3
4
5
6
7
8
9
10
11
12
13
14