第11.12章: C++ iostream
概述
C++ 的标准 I/O 库提供 std::fstream、std::ifstream 和 std::ofstream。这些流类建立在 std::basic_filebuf 之上,那是 C++ 运行时的带缓冲文件抽象。
fast_io 提供包装,让你能把 std::basic_filebuf 接入 fast_io 的 I/O 系统,从而对它们使用 print、println 和 scan。
标准 C++ I/O:<iostream>、<fstream>、<sstream>
在讨论 fast_io 如何包装 C++ I/O 之前,先回顾一下标准 C++ I/O 库。C++ 为 I/O 提供三个主要头文件:
<iostream>— 标准流(std::cin、std::cout、std::cerr、std::clog)<fstream>— 文件流(std::ifstream、std::ofstream、std::fstream)<sstream>— 字符串流(std::istringstream、std::ostringstream、std::stringstream)
标准流
C++ 提供四个预定义流:
std::cin— 标准输入(通常是键盘)std::cout— 标准输出(通常是终端)std::cerr— 标准错误(不带缓冲,通常是终端)std::clog— 标准错误(带缓冲,通常是终端)
#include <iostream>
#include <string>
int main() {
int x;
std::string name;
// Read from stdin
std::cout << "Enter your name: ";
std::cin >> name;
std::cout << "Enter a number: ";
std::cin >> x;
// Write to stdout
std::cout << "Hello, " << name << "!\n";
std::cout << "You entered: " << x << "\n";
// Write to stderr
if (x < 0) {
std::cerr << "Warning: negative number\n";
}
}
文件流
#include <fstream>
#include <iostream>
#include <string>
int main() {
// Write to a file
std::ofstream fout("output.txt");
if (!fout) {
std::cerr << "Failed to open file\n";
return 1;
}
fout << "Hello, World!\n";
fout << "The answer is: " << 42 << "\n";
fout.close();
// Read from a file
std::ifstream fin("output.txt");
if (!fin) {
std::cerr << "Failed to open file\n";
return 1;
}
std::string line;
while (std::getline(fin, line)) {
std::cout << "Read: " << line << "\n";
}
fin.close();
// Read and write (bidirectional)
std::fstream fio("data.txt", std::ios::in | std::ios::out | std::ios::trunc);
fio << "Initial data\n";
fio.seekg(0); // Go back to beginning
std::getline(fio, line);
std::cout << "Read back: " << line << "\n";
}
常见的文件打开模式:
std::ios::in— 以读方式打开std::ios::out— 以写方式打开std::ios::app— 追加到末尾std::ios::ate— 打开后定位到末尾std::ios::trunc— 文件存在时截断std::ios::binary— 二进制模式(不做文本转换)
字符串流
#include <sstream>
#include <iostream>
#include <string>
int main() {
// Write to a string
std::ostringstream oss;
oss << "x = " << 42 << ", pi = " << 3.14159;
std::string result = oss.str();
std::cout << "Built string: " << result << "\n";
// Read from a string
std::istringstream iss("42 3.14 hello");
int x;
double y;
std::string word;
iss >> x >> y >> word;
std::cout << "Parsed: " << x << ", " << y << ", " << word << "\n";
// Bidirectional string stream
std::stringstream ss;
ss << "100 200";
int a, b;
ss >> a >> b;
std::cout << "Sum: " << (a + b) << "\n";
}
流的操纵器
#include <iostream>
#include <iomanip>
int main() {
// Formatting
std::cout << std::hex << 255 << "\n"; // ff
std::cout << std::oct << 255 << "\n"; // 377
std::cout << std::dec << 255 << "\n"; // 255
// Floating-point precision
std::cout << std::fixed << std::setprecision(2) << 3.14159 << "\n"; // 3.14
std::cout << std::scientific << 3.14159 << "\n"; // 3.14e+00
// Width and alignment
std::cout << "[" << std::setw(10) << std::left << "left" << "]\n";
std::cout << "[" << std::setw(10) << std::right << "right" << "]\n";
// Boolean
std::cout << std::boolalpha << true << "\n"; // true
std::cout << std::noboolalpha << true << "\n"; // 1
}
附录
完整的 C++ iostream 与 iomanip 参考见:
要点
-
::fast_io::filebuf_file以 RAII 语义拥有一个std::basic_filebuf*。 -
::fast_io::filebuf_io_observer是对已有std::basic_filebuf*的非拥有视图,非常适合包裹fstream的rdbuf()。 -
只存在窄字符(
filebuf_file)和宽字符(wfilebuf_file)两个变体,因为 C++ 标准库没有为其他字符类型提供basic_filebuf。 -
socket 就是文件。在 POSIX 系统上,你可以把 socket 包进
filebuf_file,用fstream读写它。C++ 人为地把文件与 socket 分开(例如 Boost.Asio 与 fstream 的对立)违背了 POSIX 的哲学。 -
C++ iostream 有根本性的设计缺陷:指针混淆(带来安全问题)、非线程安全、非异常安全、会破坏流状态的有状态操纵器、依赖 locale 的输出,以及糟糕的性能。
fast_io把这些全部修正了。 -
fast_io 的操纵器是无状态的。与会改变流状态的
std::hex不同,::fast_io::mnp::hex(value)只作用于那个值。没有状态需要恢复,也就没有相应的 bug。 -
在 fstream 的实现内部改用 fast_io。如果你需要与
fstream保持兼容,就在内部使用::fast_io::filebuf_io_observer。性能代价极小,而你能得到fast_io高效系统调用层的好处。