系列文章目录
前言
一、文件IO
概述
c++程序把输入和输出看作字节流。输入时,程序从输入流中抽取字节:输出时,程序将字节流插入到输出流中。
一般输入和输出都有缓冲区。
C++程序通常在用户按下回车键时刷新输入缓冲区。
cin: 标准输入流
cout: 标准输出流
如果输出被重定向到文件,则标准错误流依然会被输出到屏幕
cerr: 标准错误流,无缓冲区
clog: 标准错误流,有缓冲区
cout
ostream类将输出转化为字符字节流
put() 显示字符
cout.put(‘W’).put(65.1);
write() 显示字符串
cout.write(“abcd”, 2).write(“ab”, 9);
long val = 234234; cout.write((char*)&val, sizeof(long));
将val内存中的数据作为字节字符输出到屏幕
输出缓冲区
输出缓冲区为512字节或为其整数倍
当输出为磁盘时有缓冲区,当输出为屏幕时会进行优化,也可显示指出刷新输出缓冲区:
cout << flush (刷新输出换出区) << endl(刷新输出换出区并插入换行符); flush(cout)也可
格式化
ostream插入运算符将输入转换成文本输出。
- 数字显示方式:dec, hex, oct
- 调整字段宽度:cout.width(x)
- 填充字符:cout.fill(‘*’)
- 设置浮点数的显示精度:cout.precision(2)
- 打印末尾的0和小数点
- cout.setf(ios_base::uppercase)
常量 含义 ios_base::boolalpha 输入和输出bool值,可以为true或false ios_base::showbase 对于输出,使用C++基数前缀(0, 0x) ios_base::showpoint 显示末尾的小数点 ios_base::uppercase 对于16进制输出,使用大写字母,E表示法 ios_base::showpos 在正数前加+ - 标准控制符:略
- 头文件iomanip
cin
cin 对象将标准输入表示为字节流。
对于如下代码:
int ele;
cin >> ele;
假设键入下面的字符:
-123Z
运算符将读取字符-123,因为它们都是整数的有效部分。但Z字符不是有效字符,因此输入中最后一个可接受的字符是3。Z将留在输入流中,下一个cin语句将从这里开始读取。与此同时,运算符将字符序列-123转换为一个整数值,并将它付给ele。
输入有时可能没有满足程序的期望。例如,假设输入的是Zcar,而不是-123Z。在这种情况下,抽取运算符将不会修改ele的值,并返回0(如果istream对象的错误状态被设置,if或while语句将判定该对象为false)。返回值false让程序能够检查输入是否满足要求。
while(cin >> num);
由于输入被缓冲。因此通过键盘输入的第二行在用户按下enter之前,不会被发送给程序。然而,循环在字符Z处停止了对输入的处理,因此它不与任何一种浮点格式匹配。输入与预期格式不匹配反过来将当导致表达式cin>>input的结果为false,因此while循环被终止。
成员 | 描述 |
---|---|
eofbit | 如果到达文件尾,则设置为1 |
badbit | 如果流被破坏,则设置为1:例如,文件读取错误 |
failbit | 如果输入操作未能读取预期的字符或输出操作没有写入预期的字符,则设置为1 |
goodbit | 另一种表示0的方法 |
good() | 如果流可以使用(所有的位都被清除),则返回true |
eof() | 如果eofbit被设置,则返回true |
bad() | 如果badbit被设置,则返回true |
fail() | 如果badbit或failbit被设置,则返回true |
rdstate() | 返回流状态 |
exceptions() | 返回一个位掩码,指出那些标记导致异常被引发 |
exceptions(isostate ex) | 设置那些状态将导致clear()引发异常:例如,如果ex是eofbit,则如果eofbit被设置,clear()将引发异常 |
clear(iostate s) | 将流状态设置为s:s的默认值为0(goodbit):如果(restate()& exceptions())!=0, 则引发异常 basic_ios::failure |
setstate(iostate s) | 调用clear(rdstate() | s)。这将设置与s中设置的为对应的流状态为,其他流状态为保持不变 |
其他istream类方法
int ct = 0;
char ch;
do{
cin.get(ch);
cout << ch;
ct++;
}while(ch != '\n\);
cout << ct << endl;
// 若为下面的则跳过空格
int ct = 0;
char ch;
do{
cin >> ch;
cout << ch;
ct++;
}while(ch != '\n\);
cout << ct << endl;
文件输入和输出
#include <fstream>
ofstream fout("file.name"); // or f.open("xxx");
ifstream fin("file.name");
char ch;
while(fin.get(ch)); // 一次读取一个字符,直到文件结尾,EOF
以默认模式打开文件进行输出将自动把文件的长度阶短为零,这相当于删除已有的内容
流状态
if(fin); // 打开成功为1,失败为0
if(fin.is_open()); // 可检查“文件模式”是否正确
// 一次打开多个文件
ifstream fin;
fin.open("xxx1");
...
fin.close();
fin.clear();
fin.open("xxx2");
...
fin.close();
文件模式
常量 | 含义 |
---|---|
io_base::in | 打开文件,以便读取 |
io_base::out | 打开文件,以便写入 |
io_base::ate | 打开文件,并移到文件尾 |
ios_base::app | 追加到文件尾 |
ios_base::trunc | 如果文件存在,则截短文件 |
ios_base::binary | 二进制文件 |
ofstream.open("xxx", ios_base::out | ios_base::trunc);
ofstream.open("xxx", ios_base::out | ios_base::app);
- 文本格式
- 二进制文件
以二进制形式写读文件
ofstream fout("planets.dat", ios_base::out | ios_base::app | ios_base::binary);
fout.write( (char*)&pl, sizeof(pl));
ifstream fin("planets.dat", ios_base::in | ios_base::binary);
fin.read((char*)&pl, sizeof(pl);
随机存取: 直接移动(不是依次移动)到文件的任何位置。随机存取常被用于数据库文件,程序维护一个独立的索引文件,该文件指出数据在主数据文件中的位置。
fstream类从iostream类派生,后者基于istream和ostream连个类,因此它继承了它们的方法。它还继承了两个缓冲区,一个用于输入,一个用于输出,并能同步化这两个缓冲区的处理。也就是说当程序读写文件时,它将协调地移动输入缓冲区中的输入指针和输出缓冲区中的输出指针。
读写模式:
finout.open(file, ios_base::in | ios_base::out | ios_base::binary);
seekp ofstream
seekg ifstream
seekp()用于将输出指针移到指定的文件位置。seekg(),原型:
basic_istream<charT, traits>& seekg(off_type, ios_base::seekdir);
basic_istream<charT, traits>& seekg(pos_type);
equal to:
istream& seekg(streamoff, ios_base::seekdir);
istream& seekg(streampos);
第一个原型定位到离第二个参数指定的文件位置特定距离的位置,第二个原型定位到离文件开头特定距离的位置。
assume fin is a ifstream object:
fin.seekg(30, ios_base::beg); // 30 bytes beyond the beginning
fin.seekg(-1, ios_base::cur); // back up one byte
fin.seekg(0, ios_base::end); // go to the end of the file
将streampos位置看作相对文件开始处的位置(第一个字节编号为0),偏移量
fin.seekg(112);
如果要检查文件指针的当前位置,则对于输入流,可以使用tellg()方法,对于输出流,可以使用tellp()方法,它们返回streampos。fstream对象的输入输出指针同步。如果使用istream ostream对象则输入输出指针不同步。
打开文件、移到文件开头并显示文件内容:
fstream finout; // read and write streams
finout.open(file, ios::in | ios::out | ios::binary);
// note: some unix systems require omitting | ios::binary
int ct = 0;
if (finout.is_open()) {
finout.seekg(0); // go to beginning
cout << "Here" << file << "file:\n";
while(finout.read((char*)&pl, sizeof(pl)){
cout << ct++ << ": " << setw(LIM) << pl.name << ": "
<< setprecision(0) << setw(12) << pl.population
<< setprecision(2) << setw(6) << pl.g << endl;
}
if (finout.eof()) { // 读取到文件尾
finout.clear(); // clear eof flag
} else { // 没有读到文件尾,而是其他故障导致
cerr << "Error in reading" << file << ".\n";
exit(EXIT_FAILURE);
}
} else { // 若文件打开失败
cerr << file << " could not be opened.\n";
exit(EXIT_FAILURE);
}
cout << "Enter the record number you wish to change: ";
long rec;
cin >> rec;
eatline(); // get rid of newline
if (rec < 0 || rec >= ct) {
cerr << "Invalid record number.\n";
exit(EXIT_FAILURE);
}
streampos place = rec * sizeof pl; // convert to streampos type
finout.seekg(place); // random access
finout.read((char*)&pl, sizeof pl);
cout << "Your selec:\n";
cout << rec << ": " << setw(LIM) << pl.name << ": "
<< setprecision(0) << setw(12) << pl.population
<< setprecision(2) << setw(6) << pl.g << endl;
if (finout.eof()) finout.clear();
cout << "Enter planet name: ";
cin.get(pl.name, LIM);
eatline();
cout << "Enter population: ";
cin >> pl.population;
cout << "Enter planet's acceleration of gravity: ";
cin >> pl.g;
finout.seekp(place);
finout.write((char*)&pl, sizeof pl) << flush; // 刷新输出
if (finout.fail()){
cerr << "Error on attempted write\n";
exit(EXIT_FAILURE);
}
stdio stddef stdlib unistd
使用临时文件
#include
char* tmpnam(char* pszName);
生成临时文件名
tmpnam()函数创建一个临时文件名,将它放在pszName指向的C-风格字符串中。常量L_tmpnam和TMP_MAX (二者都是在cstdio中定义的)限制了文件名包含的字符数以及在确保当前目录中不生成重复文件名的情况下可被调用的最多次数
内核格式化
iostream族支持程序与中断之间的I/O,而fstream族使用相同的接口提供程序和文件之间的I/O。C++库还提供了sstream族,它们使用相同的接口提供程序和string对象之间的I/O。也就是说,可以使用于cout 的ostream方法将格式化信息写入到string对象中,并使用istream方法(如getline())来读取string对象中的信息。读取string对象中格式化信息或将格式化信息写入到string对象中被称为内核格式化(incore formatting)。
#include <iostream>
#include <sstream>
#include <string>
int main()
{
using namespace std;
ostringstream outstr; // manages a string stream
string hdisk;
cout << "What's the name of your hard disk? ";
getline(cin, hdisk); // getline
int cap;
cout << "What's its capacity in GB? ";
cin >> cap;
// write formatted information to string stream
outstr << "The hard disk " << hdisk << " has a capacity of "
<< cap << " gigabytes.\n";
string result = outstr.str(); // save result
cout << result; // show contents
}
...
string lit = "it was a dark and storm day";
istringstream instr(lit); // use buf for input
string word;
while(instr >> word)
cout << word << endl;
总结
get() 单字符输入
getline() 字符串输入
seekg()和seekp()函数提供对文件的随机存取。这些类方法使得能够将文件指针放置到相对于文件开头、文件尾和当前位置的某个位置。tellg()和tellp()方法报告当前的文件位置。
获取文件大小
ifstream fin("planets.dat", ios::in | ios::binary);
fin.seekg(0, ios::end);
unsigned long long pos = fin.tellg();
cout << pos << endl;
random.cpp
#include <iostream>
#include <fstream>
#include <iomanip> // 格式化输出
#include <cstdlib> // for exit
using namespace std;
const int LIM = 20;
struct planet
{
char name[LIM];
double population;
double g;
};
const char* file = "planets.dat";
inline void eatline()
{
while (cin.get() != '\n');
}
int main()
{
planet pl;
cout << fixed;
fstream finout;
finout.open(file, ios::in | ios::out | ios::binary);
int cnt = 0;
if (finout.is_open()) {
finout.seekg(0); // go to beginning
cout << "Here, contents " << file << " file:\n";
while (finout.read((char*)&pl, sizeof pl)) {
cout << cnt++ << ": " << setw(LIM) << pl.name << ": "
<< setprecision(0) << setw(12) << pl.population
<< setprecision(2) << setw(6) << pl.g << endl;
}
if (finout.eof()) {
finout.clear();
} else {
cerr << "Error in reading " << file << ".\n";
exit(EXIT_FAILURE);
}
} else {
cerr << file << " could not be opened.\n";
exit(EXIT_FAILURE);
}
// change a record
cout << "Enter the record number you wish to change: ";
long rec;
cin >> rec;
eatline();
if (rec < 0 || rec >= cnt) {
cerr << "Invalid record number.\n";
exit(EXIT_FAILURE);
}
streampos place = rec * sizeof pl;
finout.seekg(place);
if (finout.fail()) {
cerr << "Error on attempted seek\n";
exit(EXIT_FAILURE);
}
finout.read((char*)&pl, sizeof pl);
cout << "Your selection:\n";
cout << rec << ": " << setw(LIM) << pl.name << ": "
<< setprecision(0) << setw(12) << pl.population
<< setprecision(2) << setw(6) << pl.g << endl;
if (finout.eof()) {
finout.clear();
}
cout << "Enter name: ";
cin.get(pl.name, LIM);
eatline();
cout << "Enter popu: ";
cin >> pl.population;
cout << "Enter g: ";
cin >> pl.g;
finout.seekp(place);
finout.write((char*)&pl, sizeof pl) << flush;
if (finout.fail()) {
cerr << "Error\n";
exit(EXIT_FAILURE);
}
// show revised file
cnt = 0;
finout.seekg(0); // goto beginning of file
cout << "Here " << file << " file:\n";
while (finout.read((char*)&pl, sizeof pl)) {
cout << cnt++ << ": " << setw(LIM) << pl.name << ": "
<< setprecision(0) << setw(12) << pl.population
<< setprecision(2) << setw(6) << pl.g << endl;
}
finout.close();
cout << "Done.\n";
}
writedat.cpp
#include <iostream>
#include <fstream>
#include <iomanip> // 格式化输出
#include <cstdlib> // for exit
const int LIM = 20;
struct planet
{
char name[LIM];
double population;
double g;
};
int main()
{
using namespace std;
ofstream fout("planets.dat", ios::out | ios::binary);
if (!fout) {
return 0;
}
planet p1 = {"earth", 1000, 9.80001};
planet p2 = {"mars", 34234, 4.555};
planet p3 = {"moon", 45, 3.55};
fout.seekp(0);
fout.write((char*)&p1, sizeof p1);
fout.write((char*)&p2, sizeof p2);
fout.write((char*)&p3, sizeof p3);
fout.close();
}