Skip to content

C++ 标准输入输出与文件操作

标准输入输出

基本输入输出

头文件 <iostream>,命名空间 std,定义了 cincoutcerrclog

#include <iostream>

using namespace std;

int main(){
    char str[] = "Hello";
    char data[100]
    int a;
    cout << "value of str is: " << str << endl;
    cin >> a;
    cin.getline(data, 100);
    cerr << "Error" << endl; // 非缓冲标准错误流
    clog << "Log" << endl; // 缓冲标准错误流

}

cin 常用成员方法:

成员方法名 功能
getline(str,n,ch) 从输入流中接收 n-1 个字符给 str 变量,当遇到指定 ch 字符时会停止读取,默认情况下 ch 为 '\0'。
get() 从输入流中读取一个字符,同时该字符会从输入流中消失。
gcount() 返回上次从输入流提取出的字符个数,该函数常和 get()、getline()、ignore()、peek()、read()、readsome()、putback() 和 unget() 联用。
peek() 返回输入流中的第一个字符,但并不是提取该字符。
putback(c) 将字符 c 置入输入流(缓冲区)。
ignore(n,ch) 从输入流中逐个提取字符,但提取出的字符被忽略,不被使用,直至提取出 n 个字符,或者当前读取的字符为 ch。
operator>> 重载 >> 运算符,用于读取指定类型的数据,并返回输入流对象本身。

coutcerrclog常用成员方法:

成员方法名 功能
put(ch) 输出单个字符
write(str, n) 输出指定的字符串。
tellp() 用于获取当前输出流指针的位置。
seekp(pos)
seek(off, way)
设置输出流指针的位置。
flush() 刷新输出流缓冲区。
operator<< 重载 << 运算符,使其用于输出其后指定类型的数据。

cin 读取

cin 逐字节读取

int ch;   // 注意 ch 是 int 类型的,因为 EOF 为 -1,如果读取到的字符 ASCII 码为 0xFF,如果类型为 char,其值就等于 -1,会被误判为文件结尾
while ((ch = cin.get()) != EOF){
    // 处理ch    
}

cin 读入一行字符串

  • getline(buf, n, ch) 如果输入流中 \n 或者 ch 之前的字符个数达到或者超过 n,程序出错,虽然会向 buf 中读入 n-1 个字符,但之后的读入操作都会失败,可用 cin.clear() 清除错误标记,使之恢复正常。
const int MAX_LINE_LEN = 1000;
char szBuf[MAX_LINE_LEN + 10];
while(cin.getline(szBuf, MAX_LINE_LEN+5))
    // 处理

cin 读入结束判断

Windows 中,键盘 Ctrl + Z + Enter 表示输入结束

UNIX / Linux / Mac OS 中,Ctrl +D 表示输入结束

int n;
while (cin >> n) {
    // 处理
}

cin 读取到文件末尾,就会返回 false(这是因为 istream 类对 operator bool() 进行了重载,cin 没有读到输入结尾时为 true,否则为 false),如果 cin 在读取中发生了错误,同样返回 false

输入输出错误

c++ 将输入输出错误的所有可能情况归结为四类流状态(定义于 ios_base,但 ios 派生于 ios_base,故 ios_base::badbit 可写作 ios::badbit):

  • badbit:发生了(或许是物理上的)致命性错误,流将不能继续使用
  • eofbit:输入结束
  • failbit:I/O 操作失败,主要原因是非法数据
  • goodbit:一切止常,没有错误发生,也没有输入结束

可分别用 bad()eof()fail()bad() 成员函数来检测如上的流状态,而clear() 可用于清除流状态,clear(流状态) 可用于设置流状态。

cout 格式化输出

ostream 类中可实现格式化输出的常用成员方法:

成员函数 说明
flags(fmtfl) 当前格式状态全部替换为 fmtfl。注意,fmtfl 可以表示一种格式,也可以表示多种格式。
precision(n) 设置输出浮点数的精度为 n。
width(w) 指定输出宽度为 w 个字符。
fill(c) 在指定输出宽度的情况下,输出的宽度不足时用字符 c 填充(默认情况是用空格填充)。
setf(fmtfl, mask) 在当前格式的基础上,追加 fmtfl 格式,并删除 mask 格式。其中,mask 参数可以省略。
unsetf(mask) 在当前格式的基础上,删除 mask 格式。

fmtflmask 参数的可选值:

标 志 作 用
ios::boolalpha 把 true 和 false 输出为字符串
ios::left 输出数据在本域宽范围内向左对齐
ios::right 输出数据在本域宽范围内向右对齐
ios::internal 数值的符号位在域宽内左对齐,数值右对齐,中间由填充字符填充
ios::dec 设置整数的基数为 10
ios::oct 设置整数的基数为 8
ios::hex 设置整数的基数为 16
ios::showbase 强制输出整数的基数(八进制数以 0 开头,十六进制数以 0x 打头)
ios::showpoint 强制输出浮点数的小点和尾数 0
ios::uppercase 在以科学记数法格式 E 和以十六进制输出字母时以大写表示
ios::showpos 对正数显示“+”号
ios::scientific 浮点数以科学记数法格式输出
ios::fixed 浮点数以定点格式(小数形式)输出
ios::unitbuf 每次输出之后刷新所有的流

<iomanip> 中定义格式控制符:

  • *dec, hex, oct, fixed, scientific, left, *right, setbase(b), setw(w), setfill(c), setprecision(n), setiosflags(mask), resetiosflags(mask), boolapha, *noboolalpha, showpoint, *noshowpoint, showpos, *noshowpos, uppercase, *nouppercase, internal* 表示默认)

cout << hex << 16 << endl;   // 输出 10
cout << resetiosflags(ios::basefield)<< scientific << 123 << endl;  // 输出 1.230000e+02

输入输出重定向

cincout 都可以被重定向。

方法一:用 freopen()

  • 该函数定义在 <stdio.h> 头文件

    freopen("in.txt", "r", stdin);     // cin 重定向到文件 in.txt
    freopen("out.txt", "w", stdout);   // cout 重定向到文件 out.txt
    

方法二:用 rdbuf()

  • 该函数定义在 <ios> 头文件,由于 iosistreamostream 的基类,所以,该函数也被继承,因此,coutcin 可直接调用该函

    ifstream fin("in.txt");
    ofstream fout("out.txt");
    streambuf *oldcin = cin.rdbuf(fin.rdbuf());    // cin重定向到文件in.txt
    streambuf *oldcout = cout.rdbuf(fout.rdbuf()); // cout重定向到文件out.txt
    cin.rdbuf(oldcin); // 恢复键盘输入
    cout.rdbuf(oldcout); // 恢复屏幕输出
    

方法三:控制台重定向

  • 命令行执行时 <in.txtcin 输入重定向到 in.txt>out.txtcout 重定向到 out.txt

管理输出缓存区

缓冲区刷新原因:

  • 程序正常结束
  • 缓存区满
  • endlflushends 显式刷新
    • flush 刷新缓冲区,但不输出任何额外的字符
    • ends 向缓冲区插入一个空字符,然后刷新缓冲区
  • unitbuf 清空缓冲区(默认情况下,cerr 是设置 unitbuf 的)

    • 设置 cout << unitbuf;,每次输出后都立即刷新缓冲区
  • 被关联的流读写时,关联到的流缓冲区会刷新(默认情况下,cincerr 都关联到 cout,因此,读 cin 或写 cerr 都会导致 cout 的缓冲区被刷新)

文件操作

文件流

头文件:<fstream>

  • ofstream:该数据类型表示输出文件流,用于创建文件并向文件写入信息。
  • ifstream:该数据类型表示输入文件流,用于从文件读取信息。
  • fstream:该数据类型通常表示文件流,且同时具有 ofstreamifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。

fstreamifstream 是从 istream 派生出来的,因此 cin 的成员方法,同样适用于 fstreamifstream 类的文件流对象,同样地,cout 的成员方法,同样适用于 fstreamofstream 类的文件流对象。

  • fstreamifstreamofstream 的常用成员方法

    • open()is_open()close()swap()good()eof()
  • fstreamifstream 的常用成员方法

    • operator>>gcount()get()getline(str,n,ch)ignore(n,ch)peek()putback(c)
  • fstreamofstream 的常用成员方法

    • operator<<put()write()tellp()seekp()flush()

打开文件

用成员函数 open(fileName, mode)mode 为文件的打开模式标记

模式标记 适用对象 作用
ios::in ifstream fstream 打开文件用于读取数据。如果文件不存在,则打开出错。
ios::out ofstream fstream 打开文件用于写入数据。如果文件不存在,则新建该文件;如果文件原来就存在,则打开时清除原来的内容。
ios::app ofstream fstream 打开文件,用于在其尾部添加数据。如果文件不存在,则新建该文件。
ios::ate ifstream 打开一个已有的文件,并将文件读指针指向文件末尾(读写指 的概念后面解释)。如果文件不存在,则打开出错。
ios:: trunc ofstream 打开文件时会清空内部存储的所有数据,单独使用时与 ios::out 相同。
ios::binary ifstream ofstream fstream 以二进制方式打开文件。若不指定此模式,则以文本模式打开。
ios::in | ios::out fstream 打开已存在的文件,既可读取其内容,也可向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错。
ios::in | ios::out ofstream 打开已存在的文件,可以向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错。
ios::in | ios::out | ios::trunc fstream 打开文件,既可读取其内容,也可向其写入数据。如果文件本来就存在,则打开时清除原来的内容;如果文件不存在,则新建该文件。

判断文件是否打开成功,可以直接看“对象名”这个表达式的值是否为 true。

打开文件也可用 ifstreamofstreamfstream 的构造函数。

文本方式和二进制方式打开文件并没有本质上的区别,除了换行符:UNIX/Linux 平台上,文本文件换行符为 \n,Windows 平台上,文本文件换行符为 \r\n。故在 Windows 平台上,若以文本方式打开文件,则读入 \r\n 会转换为一个字符 \n,若写入 \n 则会自动写为 \r\n

文件关闭

close() 方法用于关闭已打开的文件。

即便不调用 close() 方法,当文件流对象生命周期结束时,会调用其析构函数,析构函数会先调用 close() 方法切断与任何文件的关联。

如果程序崩溃,很可能文件流对象的析构函数未能执行,所以一定要手动调用 close() 方法关闭文件,否则文件可能不完整。

文件读写

  1. operator>>operator<<

  2. 逐个读取(写入)字符,用 get()put() 方法。(虽然是逐个字符读取(写入),但其实,操作系统会在文件输出缓冲区中缓冲字符(文件输入缓冲区中一次性读取很多数据)。

  3. get 一次读取一个字符有两种形式 int get()istream& get(char& c)

  4. put(c) 输出字符 c。

  5. getline() 用于一次读取一行字符串

  6. getline(buf, bufSize):读取 bufSize-1 个字符到 buf,或遇到 \n 为止(\n 不读入),并在 buf 读入数据的结尾添加 \0

  7. getline(buf, bufSize, delim):读取 bufSize-1 个字符到 buf,或遇到 delim 为止,并在 buf 读入数据的结尾添加 \0

  8. 若要以二进制形式读取文件(如直接将某一 class 的对象以二进制形式存入文件,然后再读取出来),需要用到 read()write() 成员方法

  9. write(buffer, count):将 buffer 指向的 count 个字节写入文件,传入 buffer 的地址需要强制类型转换为 char *

  10. read(buffer, count):与 write 方法类似

注:以二进制方式读写用 read()write(),在 Linux 平台上用文本方式打开和用二进制方式打开文件都没有问题,但在 Windows 平台上,应该用二进制方式打开文件,否则可能出错。

文件读写指针

seekg(offset, mode)seekp(offset, mode))成员函数设置读(写)指针位置,mode 为文件读写指针的设置模式:

  • ios::beg:使文件指针指向文件开始向后 offset 字节处
  • ios::cur:使文件指针从当前位置移动,offset 正数向尾部移动,负数向起始方向移动
  • ios::end:使文件指针指向文件结尾向前 |offset| 字节处

tellg()tellp())获取文件读(写)指针的位置。