当谈及输入输出流时,需要牢记的是:输入输出是相对于程序而言的,比如读文件相当于向程序输入数据,写文件相当于程序向磁盘输出数据。

本章节将会叙述 标准 IO 流、文件流、字符串流 以及相关的迭代器

标准 IO 流

所谓标准 IO,包含三个部分:标准输入流、标准输出流、标准错误流, C++ 将其映射为四个类对象

流名称作用

cin

标准输入流(stdin)

cout

标准输出流(stdout)

cerr

无缓冲的标准错误流(stderr)

clong

有缓冲的标准错误流(stderr)

用于宽字节标准 IO 的类对象为:wcin, wcout, werr, wclong

一般来说,要使用标准 IO 流,需要包含的头文件为 istream, ostream 或者 iostream

实际上,istream, ostream 等只是 C++ 创建的别名:

typedef basic_istream<char>         istream;
typedef basic_ostream<char>         ostream;
typedef basic_iostream<char>         iostream;

标准 IO 流是在 C++ 输入输出流中是最重要的流,文件流和字符串流都直接或者间接继承自标准流,因此,标准 IO 流对于其他流也是适用的。

标准 IO 流的打开模式

标准 IO 流定义了多种打开模式:

模式作用

ios_base::app

追加模式(append)

ios_base::ate

打开文件并定位到文件尾部(at end)

ios_base::trunc

截断模式

ios_base::binary

以二进制模式打开或写入流,该行为系统相关

ios_base::in

ios_base::out

尽管以上模式定义于 ios_base:: 中,但是一般使用 ios:: 以方便书写。

标准 IO 流的状态

一个流总是处于以下状态之一:

状态名含义

good()

上一个流操作成功

eof()

到达流末尾

fail()

非致命错误,上一个流操作失败(比如读取数组却得到一个 'X')

bad()

致命错误(比如磁盘读取错误)

IO 流只有在 good() 状态下才可以进行操作。否则对其操作全部无效。当一个流被当作条件时,其返回结果为 good() 的值:

for(char s = 'a'; cin>>s;){
   if( s == 'f') cin.setstate(ios::failbit);
   cout<<s<<endl;
}

当流不处于 good() 状态时,你可以使用 clear() 将流的状态重新设置为 good() 状态。

在某些情况下,你可能当流处于某种状态时直接抛出异常(比如当流处于 bad() 状态时):

cin.exceptions(cin.exceptions() | ios::badbit); // 当流处于 bad() 状态时抛出异常
try{
   cin.setstate(ios::badbit);
} catch (ios::failure&s) {
   cout<<s.what()<<endl;
}

需要特别注意的是,IO 流重载了 operator bool() 这意味着 IO 流对象可以隐式转换为 bool 值。

输入控制

C++ 对输入添加了以下函数:

get 有 6 种重载形式:

函数签名解释

int get()

返回值为从流中读到的字符或者 EOF

istream& get (char& c)

返回值为 *this,将读到的字符储存到 c 中

istream& get (char* s, streamsize n)

读取 c-string,直到读够 n-1 个或者遇到 'n'

istream& get (char* s, streamsize n, char delim)

读取 c-string,直到读够 n-1 个或者遇到 delim 字符

istream& get (streambuf& sb)

将字符读入到 sb 中,遇到 'n' 停止

istream& get (streambuf& sb, char delim)

将字符读入到 sb 中,遇到 delim 字符 停止

如果你希望读取整个文件,一般有以下几种方式:

fstream fs("Z:/main.cpp", ios::in | ios::out);
char cstr[30];
while (fs.get(cstr, 30, EOF))
   cout << cstr;
fstream fs("Z:/main.cpp", ios::in | ios::out);
char cstr;
while (fs.get(cstr))
   cout << cstr;

其可能造成以下结果:

  1. 对于无参形式而言,若流中无可用字符,返回 EOF 并置 failbit,否则返回读到的字符。遇到 EOF 停止读取

  2. 对于有参形式而言,返回值为 *this,但是可以隐式转换为 bool,可以看做返回值为 !fail()

  3. 若读流到达末尾,置 eofbit

  4. 若没有字符被写入,置 failbit

  5. 若流内部发生错误(比如内部抛异常),置 badbit

当 get 遇到连续两个 delim 时,会置 failbit

  1. getline

函数名作用

in>>x

根据 x 的类型从 in 读取数据并存入 x 中。

getline(istream&in, string&s)

并从 in 中读取一行,写入到 s 中。不保留换行符

in.get()

返回值为 int, 到达末尾时返回 EOF,提前到达置 eofbit为真

in.getline(char*s, int n)

从 in 中读取 n 个字符并写入到 s 中,不保留换行符

in.read(s, n)

从 in 中读取 n 个字符并写入到 s 中,不会自动添加 '0'

尽量选择 in>>xgetline(),而不是其他更加底层的函数。

另外,还有一个 ignore:

istream& ignore(int n = 1, int delim = EOF)

ignore 会跳过 n 个字符或遇到 delim 为止

输入输出格式控制

iomanip

无格式输入函数

输入流的 无格式输入函数 (UnformattedInputFunction ) 其行为如下:

  • 在存储周期内自动构建一个 noskipws 设置为 true 的 basic_istream::sentry

    • 若输入流的 eofbit 和 badbit 被同时设定,则 failbit 也会设置,如果输入流的 failbit 允许抛出异常,则还会抛出 ios_base::failure

    • 如果可能,刷新 std::tie 的输入流

  • 通过调用 sentry::operator bool() 检查 sentry 的情况,这与 basic_ios::good 的效果相同。

  • 如果 sentry 返回 false 或 sentry 在构造时抛出异常:

    • 设置输入流已经提取的字符数量( gcount )为 0

    • 如果函数被用于向一个 charT 数组写入数据,向数组的第一个位置写入一个空字符

  • 如果 sentry 返回值为 true ,其行为与调用 rdbuf()→sbumpc()rdbuf()→sgetc() 行为形同。

    • 若到达流的末尾(通过调用 rdbuf()→sbumpc()rdbuf()→sgetc() ),设置 eofbit 。若 eofbit 的异常被打开,则抛出 ios_base::failure

    • 如果在输入时抛出了异常,置 badbit 。若允许异常,则抛出 ios_base::badbit

    • 若没有异常抛出,设置输入流的已提取字符数( gcount )

  • 无论如何(无论是被异常终止还是被返回), sentry 的析构函数总是在离开函数时被调用。

以下标准库函数是 无格式输入函数

  • std::getline 不会修改 gcount.

  • basic_istream::operator>>(basic_streambuf*)

  • basic_istream::get

  • basic_istream::getline

  • basic_istream::ignore

  • basic_istream::peek

  • basic_istream::read

  • basic_istream::readsome

  • basic_istream::putback 它会首先清除 eofbit

  • basic_istream::unget 它会首先清除 eofbit

  • basic_istream::sync 不会修改 gcount

  • basic_istream::tellg 不会修改 gcount

  • basic_istream::seekg 首先清除 eofbit 而且不会修改 gcount

  • std::ws 不会修改 gcount

读取全部内容

std::string ReadAll(const std::string& file_path){
    if(file_path.empty()) return {};
    if(!Path::Exists(file_path)) return {};

    // 使用 binary 模式打开避免读到 eof
    std::ifstream is(file_path, std::ios::in | std::ios::binary);
    return { std::istreambuf_iterator<char>(is), std::istreambuf_iterator<char>() };
}

当一个二进制文件以文本模式打开时,fgetc 会返回得到的字节。另一方面,由于 ASCII 范围为 0\~255,因此操作系统将 getc 的返回值 -1 当作发生错误(抑或是遇到了 eof),因此可能导致文件提前结束。

这里需要注意:EOF 是读到文件末尾时文件系统给出的信号,而 fgetc 的返回值 -1 代表了收到此信号。当 fgetc 得到的字节为 -1 时,程序错误地将 -1 字节当作了 EOF 信号,从而导致文件读取提前结束。

要正确判断是否是遇到了 EOF,可以使用 feof 函数判断。当文件指针指向文件最后一个字节的下一位时,feof 返回 1

如果想往终端中输入 \^D,可以使用 ^V-\^D 实现。\^V 就相当于反转义下一个按键序列
Last moify: 2023-12-15 04:28:35
Build time:2025-07-18 09:41:42
Powered By asphinx