|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
C++作为一种强大而灵活的编程语言,其输入输出(I/O)系统是程序与外部世界交互的关键。无论是简单的控制台应用程序还是复杂的系统软件,输出功能都扮演着至关重要的角色。本教程将带您从C++输出编程的基础语法开始,逐步深入到高级应用,全面掌握标准输出流、文件输出和错误处理等核心技术。通过本教程的学习,您将能够解决现实编程中的输出难题,提升开发技能,并避免常见的输出编程错误。
C++输出基础:标准输出流
理解输出流概念
在C++中,输出是基于流(stream)的概念实现的。流是一个抽象概念,代表数据的流动。当我们将数据发送到输出设备(如屏幕、文件等)时,我们说数据正在”流出”程序。
C++标准库提供了几个预定义的输出流,最常用的是cout,它与标准输出设备(通常是控制台屏幕)相关联。
基本输出操作
使用cout进行基本输出非常简单,需要包含<iostream>头文件:
- #include <iostream>
- int main() {
- std::cout << "Hello, World!" << std::endl;
- return 0;
- }
复制代码
在这个例子中:
• std::cout是标准输出流对象
• <<是输出运算符(也称为插入运算符),它将右侧的数据插入到左侧的流中
• std::endl是一个操纵符(manipulator),它输出换行符并刷新输出缓冲区
输出不同类型的数据
C++的cout可以自动处理各种基本数据类型:
- #include <iostream>
- int main() {
- // 输出整数
- int age = 25;
- std::cout << "Age: " << age << std::endl;
-
- // 输出浮点数
- double price = 19.99;
- std::cout << "Price: " << price << std::endl;
-
- // 输出字符
- char grade = 'A';
- std::cout << "Grade: " << grade << std::endl;
-
- // 输出字符串
- std::string name = "Alice";
- std::cout << "Name: " << name << std::endl;
-
- // 输出布尔值
- bool isStudent = true;
- std::cout << "Is student: " << isStudent << std::endl; // 输出1表示true
-
- return 0;
- }
复制代码
链式输出
C++允许使用多个<<运算符将多个输出操作链接在一起,形成链式输出:
- #include <iostream>
- int main() {
- int x = 10, y = 20;
- std::cout << "The sum of " << x << " and " << y << " is " << x + y << std::endl;
- return 0;
- }
复制代码
格式化输出
控制输出宽度
使用std::setw操纵符可以设置输出字段的宽度。需要包含<iomanip>头文件:
- #include <iostream>
- #include <iomanip>
- int main() {
- std::cout << "Default:" << std::endl;
- std::cout << 123 << std::endl;
- std::cout << 45 << std::endl;
-
- std::cout << "\nWith setw(10):" << std::endl;
- std::cout << std::setw(10) << 123 << std::endl;
- std::cout << std::setw(10) << 45 << std::endl;
-
- return 0;
- }
复制代码
输出结果:
- Default:
- 123
- 45
- With setw(10):
- 123
- 45
复制代码
设置填充字符
默认情况下,当输出宽度大于数据宽度时,多余的位置用空格填充。可以使用std::setfill操纵符更改填充字符:
- #include <iostream>
- #include <iomanip>
- int main() {
- std::cout << std::setw(10) << 123 << std::endl;
- std::cout << std::setfill('*') << std::setw(10) << 123 << std::endl;
- std::cout << std::setfill('0') << std::setw(10) << 123 << std::endl;
-
- return 0;
- }
复制代码
输出结果:
控制对齐方式
使用std::left和std::right操纵符可以控制输出在字段中的对齐方式:
- #include <iostream>
- #include <iomanip>
- int main() {
- std::cout << "Right alignment (default):" << std::endl;
- std::cout << std::setw(10) << "Left" << std::setw(10) << "Right" << std::endl;
-
- std::cout << "\nLeft alignment:" << std::endl;
- std::cout << std::left << std::setw(10) << "Left" << std::setw(10) << "Right" << std::endl;
-
- return 0;
- }
复制代码
输出结果:
- Right alignment (default):
- Left Right
- Left alignment:
- Left Right
复制代码
控制数值格式
对于数值输出,C++提供了多种格式控制选项:
- #include <iostream>
- #include <iomanip>
- int main() {
- double pi = 3.14159265358979;
-
- // 默认浮点数输出
- std::cout << "Default: " << pi << std::endl;
-
- // 设置精度
- std::cout << "Precision 5: " << std::setprecision(5) << pi << std::endl;
-
- // 固定点表示法
- std::cout << "Fixed: " << std::fixed << pi << std::endl;
-
- // 科学计数法
- std::cout << "Scientific: " << std::scientific << pi << std::endl;
-
- // 重置为默认格式
- std::cout.unsetf(std::ios_base::floatfield);
- std::cout << "Default again: " << pi << std::endl;
-
- return 0;
- }
复制代码
输出结果:
- Default: 3.14159
- Precision 5: 3.1416
- Fixed: 3.14159
- Scientific: 3.141593e+00
- Default again: 3.14159
复制代码
输出布尔值
默认情况下,bool值输出为1(true)或0(false)。可以使用std::boolalpha操纵符输出文本形式:
- #include <iostream>
- int main() {
- bool flag = true;
-
- std::cout << "Default: " << flag << std::endl;
- std::cout << "With boolalpha: " << std::boolalpha << flag << std::endl;
-
- // 切换回数字形式
- std::cout << "With noboolalpha: " << std::noboolalpha << flag << std::endl;
-
- return 0;
- }
复制代码
输出结果:
- Default: 1
- With boolalpha: true
- With noboolalpha: 1
复制代码
输出整数的进制
可以使用std::dec(十进制)、std::hex(十六进制)和std::oct(八进制)操纵符控制整数的输出进制:
- #include <iostream>
- int main() {
- int number = 42;
-
- std::cout << "Decimal: " << std::dec << number << std::endl;
- std::cout << "Hexadecimal: " << std::hex << number << std::endl;
- std::cout << "Octal: " << std::oct << number << std::endl;
-
- // 显示进制前缀
- std::cout << std::showbase;
- std::cout << "Hexadecimal with prefix: " << std::hex << number << std::endl;
- std::cout << "Octal with prefix: " << std::oct << number << std::endl;
-
- return 0;
- }
复制代码
输出结果:
- Decimal: 42
- Hexadecimal: 2a
- Octal: 52
- Hexadecimal with prefix: 0x2a
- Octal with prefix: 052
复制代码
文件输出
基本文件输出
C++中使用ofstream(输出文件流)类进行文件输出操作。需要包含<fstream>头文件:
- #include <iostream>
- #include <fstream>
- #include <string>
- int main() {
- // 创建输出文件流对象
- std::ofstream outFile("example.txt");
-
- // 检查文件是否成功打开
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- // 写入文件
- outFile << "Hello, File!" << std::endl;
- outFile << "This is a line of text." << std::endl;
-
- // 关闭文件
- outFile.close();
-
- std::cout << "Data written to file successfully." << std::endl;
-
- return 0;
- }
复制代码
文件打开模式
ofstream的构造函数或open()方法可以接受第二个参数,指定文件的打开模式:
- #include <iostream>
- #include <fstream>
- int main() {
- // 输出模式(默认)
- std::ofstream outFile1("example1.txt", std::ios::out);
-
- // 追加模式
- std::ofstream outFile2("example2.txt", std::ios::app);
-
- // 二进制模式
- std::ofstream outFile3("example3.dat", std::ios::binary);
-
- // 组合模式:追加和二进制
- std::ofstream outFile4("example4.dat", std::ios::app | std::ios::binary);
-
- // 检查文件是否成功打开
- if (!outFile1 || !outFile2 || !outFile3 || !outFile4) {
- std::cerr << "Error: Could not open one or more files." << std::endl;
- return 1;
- }
-
- // 写入数据
- outFile1 << "This is output mode." << std::endl;
- outFile2 << "This is append mode." << std::endl;
-
- // 关闭文件
- outFile1.close();
- outFile2.close();
- outFile3.close();
- outFile4.close();
-
- std::cout << "Files created successfully." << std::endl;
-
- return 0;
- }
复制代码
写入二进制数据
使用二进制模式可以高效地写入原始数据:
- #include <iostream>
- #include <fstream>
- struct Person {
- char name[50];
- int age;
- double salary;
- };
- int main() {
- Person person = {"John Doe", 30, 50000.50};
-
- // 以二进制模式打开文件
- std::ofstream outFile("person.dat", std::ios::binary);
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- // 写入二进制数据
- outFile.write(reinterpret_cast<const char*>(&person), sizeof(Person));
-
- outFile.close();
-
- std::cout << "Binary data written to file successfully." << std::endl;
-
- return 0;
- }
复制代码
文件位置指针
文件流维护一个位置指针,指示下一个读/写操作的位置。对于输出流,可以使用seekp()方法设置位置指针:
- #include <iostream>
- #include <fstream>
- #include <string>
- int main() {
- std::ofstream outFile("data.txt");
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- // 写入一些数据
- outFile << "1234567890" << std::endl;
-
- // 获取当前位置
- std::streampos pos = outFile.tellp();
- std::cout << "Current position: " << pos << std::endl;
-
- // 移动到文件开头
- outFile.seekp(0, std::ios::beg);
-
- // 在开头写入新数据
- outFile << "ABCDE";
-
- // 移动到文件末尾
- outFile.seekp(0, std::ios::end);
-
- // 在末尾追加数据
- outFile << "FGHIJ" << std::endl;
-
- outFile.close();
-
- std::cout << "Data written with positioning successfully." << std::endl;
-
- return 0;
- }
复制代码
刷新输出缓冲区
输出流通常使用缓冲区来提高性能。有时需要强制刷新缓冲区,确保数据立即写入:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ofstream outFile("buffered.txt");
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- // 写入数据(可能还在缓冲区中)
- outFile << "This data might be buffered." << std::endl;
-
- // 手动刷新缓冲区
- outFile.flush();
-
- // 写入更多数据
- outFile << "This data will also be buffered." << std::endl;
-
- // 关闭文件时会自动刷新缓冲区
- outFile.close();
-
- std::cout << "Buffered output completed successfully." << std::endl;
-
- return 0;
- }
复制代码
错误处理
检查流状态
C++的流对象维护一个状态标志,可以用来检查操作是否成功:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ifstream inFile("nonexistent.txt");
-
- // 检查文件是否成功打开
- if (!inFile) {
- std::cerr << "Error: Could not open file." << std::endl;
-
- // 检查具体错误状态
- if (inFile.fail()) {
- std::cerr << "Fail bit is set." << std::endl;
- }
-
- return 1;
- }
-
- // 读取文件内容
- char ch;
- while (inFile.get(ch)) {
- std::cout << ch;
- }
-
- // 检查是否到达文件末尾
- if (inFile.eof()) {
- std::cout << "\nReached end of file." << std::endl;
- }
-
- inFile.close();
-
- return 0;
- }
复制代码
异常处理
可以配置流对象在遇到错误时抛出异常:
- #include <iostream>
- #include <fstream>
- #include <stdexcept>
- int main() {
- // 设置在失败时抛出异常
- std::ifstream inFile;
- inFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
-
- try {
- inFile.open("nonexistent.txt");
-
- // 读取文件内容
- char ch;
- while (inFile.get(ch)) {
- std::cout << ch;
- }
-
- inFile.close();
- }
- catch (const std::ifstream::failure& e) {
- std::cerr << "Exception opening/reading file: " << e.what() << std::endl;
- return 1;
- }
-
- return 0;
- }
复制代码
处理输出错误
输出操作也可能失败,例如磁盘空间不足或文件权限问题:
- #include <iostream>
- #include <fstream>
- #include <string>
- int main() {
- std::ofstream outFile("/root/protected.txt"); // 假设我们没有权限写入此目录
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
-
- // 检查具体错误
- if (outFile.fail()) {
- std::cerr << "Fail bit is set. Possible reasons:" << std::endl;
- std::cerr << "- File does not exist" << std::endl;
- std::cerr << "- No permission to write" << std::endl;
- std::cerr << "- Disk full" << std::endl;
- }
-
- return 1;
- }
-
- try {
- // 尝试写入大量数据(可能导致磁盘空间不足)
- for (int i = 0; i < 1000000; ++i) {
- outFile << "This is line " << i << " with some sample text." << std::endl;
-
- // 定期检查错误状态
- if (outFile.fail()) {
- throw std::runtime_error("Failed to write to file.");
- }
- }
- }
- catch (const std::exception& e) {
- std::cerr << "Exception: " << e.what() << std::endl;
- outFile.close();
- return 1;
- }
-
- outFile.close();
- std::cout << "Data written successfully." << std::endl;
-
- return 0;
- }
复制代码
自定义错误处理
可以创建自定义的错误处理函数,并将其与流对象关联:
- #include <iostream>
- #include <fstream>
- void customErrorHandler(std::ios_base::failure& e, const std::string& filename) {
- std::cerr << "Custom error handler for file: " << filename << std::endl;
- std::cerr << "Error: " << e.what() << std::endl;
-
- // 可以在这里添加恢复逻辑,如尝试创建备份文件等
- }
- int main() {
- std::ofstream outFile;
-
- // 设置在失败时抛出异常
- outFile.exceptions(std::ofstream::failbit | std::ofstream::badbit);
-
- try {
- outFile.open("readonly.txt"); // 假设这是一个只读文件
-
- // 尝试写入
- outFile << "This should fail." << std::endl;
-
- outFile.close();
- }
- catch (std::ios_base::failure& e) {
- customErrorHandler(e, "readonly.txt");
- return 1;
- }
-
- return 0;
- }
复制代码
高级应用
重定向标准输出
有时需要将标准输出重定向到文件或其他设备:
- #include <iostream>
- #include <fstream>
- #include <string>
- void printToConsole() {
- std::cout << "This message goes to the console." << std::endl;
- }
- void printToFile(const std::string& filename) {
- // 保存原始的cout缓冲区
- std::streambuf* originalCoutBuffer = std::cout.rdbuf();
-
- // 打开文件
- std::ofstream outFile(filename);
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return;
- }
-
- // 重定向cout到文件
- std::cout.rdbuf(outFile.rdbuf());
-
- // 现在cout输出将写入文件
- std::cout << "This message goes to the file." << std::endl;
-
- // 恢复原始的cout缓冲区
- std::cout.rdbuf(originalCoutBuffer);
-
- // 关闭文件
- outFile.close();
-
- std::cout << "Message redirected to file successfully." << std::endl;
- }
- int main() {
- printToConsole();
- printToFile("redirected.txt");
-
- return 0;
- }
复制代码
使用字符串流进行格式化
C++的stringstream类允许将输出格式化到字符串中,然后再输出:
- #include <iostream>
- #include <sstream>
- #include <iomanip>
- #include <string>
- std::string formatPerson(const std::string& name, int age, double height) {
- std::ostringstream oss;
-
- oss << "Name: " << std::left << std::setw(20) << name
- << "Age: " << std::setw(3) << age
- << "Height: " << std::fixed << std::setprecision(2) << height << "m";
-
- return oss.str();
- }
- int main() {
- std::string formatted = formatPerson("John Smith", 30, 1.75);
- std::cout << formatted << std::endl;
-
- // 也可以直接使用stringstream进行复杂的格式化
- std::ostringstream report;
- report << "Sales Report\n"
- << "------------\n"
- << "Item: " << std::setw(15) << std::left << "Laptop"
- << "Price: $" << std::setw(10) << std::right << std::fixed << std::setprecision(2) << 999.99 << "\n"
- << "Item: " << std::setw(15) << std::left << "Mouse"
- << "Price: $" << std::setw(10) << std::right << std::fixed << std::setprecision(2) << 19.99 << "\n"
- << "Item: " << std::setw(15) << std::left << "Keyboard"
- << "Price: $" << std::setw(10) << std::right << std::fixed << std::setprecision(2) << 49.99 << "\n";
-
- std::cout << report.str();
-
- return 0;
- }
复制代码
多线程输出
在多线程环境中,输出操作需要同步以避免数据竞争:
- #include <iostream>
- #include <thread>
- #include <mutex>
- #include <chrono>
- std::mutex coutMutex; // 用于同步cout的互斥锁
- void threadFunction(int id) {
- for (int i = 0; i < 5; ++i) {
- // 使用lock_guard自动管理锁的生命周期
- std::lock_guard<std::mutex> lock(coutMutex);
-
- std::cout << "Thread " << id << ": Message " << i << std::endl;
-
- // 锁在lock_guard析构时自动释放
- }
- }
- int main() {
- std::thread t1(threadFunction, 1);
- std::thread t2(threadFunction, 2);
-
- t1.join();
- t2.join();
-
- std::cout << "All threads completed." << std::endl;
-
- return 0;
- }
复制代码
输出到多个目标
有时需要将相同的输出同时发送到多个目标,例如控制台和文件:
- #include <iostream>
- #include <fstream>
- #include <string>
- class MultiOutputStream {
- private:
- std::ostream& stream1;
- std::ostream& stream2;
-
- public:
- MultiOutputStream(std::ostream& s1, std::ostream& s2) : stream1(s1), stream2(s2) {}
-
- template<typename T>
- MultiOutputStream& operator<<(const T& value) {
- stream1 << value;
- stream2 << value;
- return *this;
- }
-
- // 处理操纵符(如std::endl)
- MultiOutputStream& operator<<(std::ostream& (*manip)(std::ostream&)) {
- stream1 << manip;
- stream2 << manip;
- return *this;
- }
- };
- int main() {
- std::ofstream logFile("log.txt");
-
- if (!logFile) {
- std::cerr << "Error: Could not open log file." << std::endl;
- return 1;
- }
-
- MultiOutputStream multiOut(std::cout, logFile);
-
- multiOut << "This message goes to both console and file." << std::endl;
- multiOut << "Error: Something went wrong!" << std::endl;
- multiOut << "Warning: Low disk space." << std::endl;
-
- logFile.close();
-
- std::cout << "Multi-output completed successfully." << std::endl;
-
- return 0;
- }
复制代码
国际化输出
处理不同语言和地区的输出:
- #include <iostream>
- #include <locale>
- #include <iomanip>
- int main() {
- // 设置本地化环境
- std::locale::global(std::locale(""));
- std::cout.imbue(std::locale());
-
- // 输出数字(使用本地化的数字格式)
- double number = 1234567.89;
- std::cout << "Localized number: " << number << std::endl;
-
- // 输出货币(使用本地化的货币格式)
- std::cout << "Localized currency: " << std::put_money(123456) << std::endl;
-
- // 输出时间(使用本地化的时间格式)
- time_t now = time(nullptr);
- std::tm tm = *std::localtime(&now);
- std::cout << "Localized time: " << std::put_time(&tm, "%c") << std::endl;
-
- return 0;
- }
复制代码
常见错误及避免方法
1. 未检查文件是否成功打开
错误示例:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ofstream outFile("readonly.txt"); // 假设这是一个只读文件
-
- // 未检查文件是否成功打开
- outFile << "This will fail silently." << std::endl;
- outFile.close();
-
- return 0;
- }
复制代码
正确方法:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ofstream outFile("readonly.txt");
-
- // 检查文件是否成功打开
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- outFile << "This will work if the file is writable." << std::endl;
- outFile.close();
-
- return 0;
- }
复制代码
2. 忘记关闭文件
错误示例:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ofstream outFile("data.txt");
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- outFile << "Some data" << std::endl;
-
- // 忘记关闭文件
-
- return 0;
- }
复制代码
正确方法:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ofstream outFile("data.txt");
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- outFile << "Some data" << std::endl;
-
- // 显式关闭文件
- outFile.close();
-
- return 0;
- }
复制代码
更好的方法(使用RAII):
- #include <iostream>
- #include <fstream>
- void writeData() {
- // 文件将在函数结束时自动关闭
- std::ofstream outFile("data.txt");
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return;
- }
-
- outFile << "Some data" << std::endl;
- // 不需要显式关闭文件,它会在outFile析构时自动关闭
- }
- int main() {
- writeData();
- return 0;
- }
复制代码
3. 错误使用格式化操纵符
错误示例:
- #include <iostream>
- #include <iomanip>
- int main() {
- double number = 123.456789;
-
- // 错误:std::fixed和std::scientific同时使用
- std::cout << std::fixed << std::scientific << number << std::endl;
-
- return 0;
- }
复制代码
正确方法:
- #include <iostream>
- #include <iomanip>
- int main() {
- double number = 123.456789;
-
- // 正确:使用一种格式
- std::cout << "Fixed: " << std::fixed << number << std::endl;
-
- // 重置格式
- std::cout.unsetf(std::ios_base::floatfield);
-
- // 使用另一种格式
- std::cout << "Scientific: " << std::scientific << number << std::endl;
-
- return 0;
- }
复制代码
4. 缓冲区刷新问题
错误示例:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ofstream outFile("buffered.txt");
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- // 写入重要数据,但不刷新缓冲区
- outFile << "Critical data that must be written immediately";
-
- // 程序可能在这里崩溃,导致数据丢失
-
- outFile.close();
-
- return 0;
- }
复制代码
正确方法:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ofstream outFile("buffered.txt");
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- // 写入重要数据
- outFile << "Critical data that must be written immediately";
-
- // 立即刷新缓冲区
- outFile.flush();
-
- // 现在即使程序崩溃,数据也已经写入文件
-
- outFile.close();
-
- return 0;
- }
复制代码
5. 二进制模式下的文本输出
错误示例:
- #include <iostream>
- #include <fstream>
- int main() {
- // 以二进制模式打开文件
- std::ofstream outFile("binary.dat", std::ios::binary);
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- // 错误:在二进制模式下使用文本输出
- outFile << "Hello, World!" << std::endl;
-
- outFile.close();
-
- return 0;
- }
复制代码
正确方法:
- #include <iostream>
- #include <fstream>
- #include <string>
- int main() {
- // 对于文本数据,使用文本模式
- std::ofstream textFile("text.txt");
- if (textFile) {
- textFile << "Hello, World!" << std::endl;
- textFile.close();
- }
-
- // 对于二进制数据,使用二进制模式
- std::ofstream binaryFile("binary.dat", std::ios::binary);
- if (binaryFile) {
- int numbers[] = {1, 2, 3, 4, 5};
- binaryFile.write(reinterpret_cast<const char*>(numbers), sizeof(numbers));
- binaryFile.close();
- }
-
- return 0;
- }
复制代码
6. 忽略错误状态
错误示例:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ofstream outFile("disk_full.txt");
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- // 尝试写入大量数据(可能导致磁盘空间不足)
- for (int i = 0; i < 1000000; ++i) {
- outFile << "This is line " << i << " with some sample text." << std::endl;
- // 不检查错误状态
- }
-
- outFile.close();
-
- return 0;
- }
复制代码
正确方法:
- #include <iostream>
- #include <fstream>
- int main() {
- std::ofstream outFile("disk_full.txt");
-
- if (!outFile) {
- std::cerr << "Error: Could not open file for writing." << std::endl;
- return 1;
- }
-
- // 尝试写入大量数据
- for (int i = 0; i < 1000000; ++i) {
- outFile << "This is line " << i << " with some sample text." << std::endl;
-
- // 定期检查错误状态
- if (outFile.fail()) {
- std::cerr << "Error: Failed to write to file. Disk may be full." << std::endl;
- outFile.close();
- return 1;
- }
- }
-
- outFile.close();
-
- return 0;
- }
复制代码
实战案例
案例1:生成格式化报告
假设我们需要生成一个销售报告,包含产品信息、销售额和统计信息,并输出到控制台和文件。
- #include <iostream>
- #include <fstream>
- #include <iomanip>
- #include <vector>
- #include <string>
- #include <algorithm>
- struct Product {
- std::string name;
- int quantity;
- double price;
- };
- class ReportGenerator {
- private:
- std::vector<Product> products;
-
- public:
- void addProduct(const std::string& name, int quantity, double price) {
- products.push_back({name, quantity, price});
- }
-
- double calculateTotalSales() const {
- double total = 0.0;
- for (const auto& product : products) {
- total += product.quantity * product.price;
- }
- return total;
- }
-
- std::string getBestSellingProduct() const {
- if (products.empty()) return "None";
-
- auto it = std::max_element(products.begin(), products.end(),
- [](const Product& a, const Product& b) {
- return a.quantity < b.quantity;
- });
-
- return it->name;
- }
-
- void generateReport(std::ostream& out) const {
- // 设置格式
- out << std::fixed << std::setprecision(2);
-
- // 报告标题
- out << "SALES REPORT" << std::endl;
- out << "============" << std::endl << std::endl;
-
- // 表头
- out << std::left << std::setw(20) << "Product"
- << std::right << std::setw(10) << "Quantity"
- << std::setw(15) << "Unit Price"
- << std::setw(15) << "Total" << std::endl;
-
- out << std::string(60, '-') << std::endl;
-
- // 产品数据
- for (const auto& product : products) {
- double total = product.quantity * product.price;
- out << std::left << std::setw(20) << product.name
- << std::right << std::setw(10) << product.quantity
- << std::setw(15) << product.price
- << std::setw(15) << total << std::endl;
- }
-
- out << std::string(60, '-') << std::endl;
-
- // 统计信息
- out << std::left << std::setw(20) << "Total Sales:"
- << std::right << std::setw(40) << calculateTotalSales() << std::endl;
-
- out << std::left << std::setw(20) << "Best Selling Product:"
- << std::right << std::setw(40) << getBestSellingProduct() << std::endl;
-
- out << std::endl << "Report generated on: ";
-
- // 输出当前日期和时间
- time_t now = time(nullptr);
- tm tm = *localtime(&now);
- out << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << std::endl;
- }
- };
- int main() {
- ReportGenerator generator;
-
- // 添加产品数据
- generator.addProduct("Laptop", 15, 999.99);
- generator.addProduct("Smartphone", 30, 699.99);
- generator.addProduct("Tablet", 20, 399.99);
- generator.addProduct("Headphones", 50, 99.99);
- generator.addProduct("Smartwatch", 25, 249.99);
-
- // 输出到控制台
- generator.generateReport(std::cout);
-
- // 输出到文件
- std::ofstream reportFile("sales_report.txt");
- if (reportFile) {
- generator.generateReport(reportFile);
- reportFile.close();
- std::cout << "\nReport also saved to 'sales_report.txt'." << std::endl;
- } else {
- std::cerr << "Error: Could not save report to file." << std::endl;
- }
-
- return 0;
- }
复制代码
案例2:日志系统
创建一个多功能的日志系统,支持不同级别的日志输出,并能同时输出到控制台和文件。
- #include <iostream>
- #include <fstream>
- #include <string>
- #include <ctime>
- #include <iomanip>
- #include <mutex>
- #include <sstream>
- enum class LogLevel {
- DEBUG,
- INFO,
- WARNING,
- ERROR,
- CRITICAL
- };
- class Logger {
- private:
- std::ofstream logFile;
- std::mutex logMutex;
- LogLevel currentLevel = LogLevel::INFO;
- bool consoleOutput = true;
- bool fileOutput = false;
-
- std::string levelToString(LogLevel level) {
- switch (level) {
- case LogLevel::DEBUG: return "DEBUG";
- case LogLevel::INFO: return "INFO";
- case LogLevel::WARNING: return "WARNING";
- case LogLevel::ERROR: return "ERROR";
- case LogLevel::CRITICAL: return "CRITICAL";
- default: return "UNKNOWN";
- }
- }
-
- std::string getCurrentTime() {
- time_t now = time(nullptr);
- tm tm = *localtime(&now);
-
- std::ostringstream oss;
- oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
- return oss.str();
- }
-
- public:
- Logger() = default;
-
- ~Logger() {
- if (logFile.is_open()) {
- logFile.close();
- }
- }
-
- bool setLogFile(const std::string& filename) {
- std::lock_guard<std::mutex> lock(logMutex);
-
- if (logFile.is_open()) {
- logFile.close();
- }
-
- logFile.open(filename, std::ios::app);
- if (!logFile) {
- std::cerr << "Error: Could not open log file: " << filename << std::endl;
- return false;
- }
-
- fileOutput = true;
- return true;
- }
-
- void setLogLevel(LogLevel level) {
- currentLevel = level;
- }
-
- void enableConsoleOutput(bool enable) {
- consoleOutput = enable;
- }
-
- void enableFileOutput(bool enable) {
- fileOutput = enable;
- }
-
- void log(LogLevel level, const std::string& message) {
- if (level < currentLevel) {
- return;
- }
-
- std::lock_guard<std::mutex> lock(logMutex);
-
- std::string logEntry = "[" + getCurrentTime() + "] [" +
- levelToString(level) + "] " + message;
-
- if (consoleOutput) {
- // 根据日志级别设置不同的颜色
- switch (level) {
- case LogLevel::DEBUG:
- std::cout << "\033[36m"; // 青色
- break;
- case LogLevel::INFO:
- std::cout << "\033[32m"; // 绿色
- break;
- case LogLevel::WARNING:
- std::cout << "\033[33m"; // 黄色
- break;
- case LogLevel::ERROR:
- std::cout << "\033[31m"; // 红色
- break;
- case LogLevel::CRITICAL:
- std::cout << "\033[1;31m"; // 亮红色
- break;
- }
-
- std::cout << logEntry << "\033[0m" << std::endl;
- }
-
- if (fileOutput && logFile.is_open()) {
- logFile << logEntry << std::endl;
- logFile.flush(); // 确保日志立即写入文件
- }
- }
-
- // 便捷方法
- void debug(const std::string& message) { log(LogLevel::DEBUG, message); }
- void info(const std::string& message) { log(LogLevel::INFO, message); }
- void warning(const std::string& message) { log(LogLevel::WARNING, message); }
- void error(const std::string& message) { log(LogLevel::ERROR, message); }
- void critical(const std::string& message) { log(LogLevel::CRITICAL, message); }
- };
- // 全局日志对象
- Logger logger;
- int main() {
- // 配置日志系统
- logger.setLogFile("application.log");
- logger.setLogLevel(LogLevel::DEBUG);
-
- // 记录一些日志消息
- logger.debug("Application starting up...");
- logger.info("Initializing components...");
-
- // 模拟一些操作
- try {
- logger.info("Performing operation A...");
- // 模拟成功
- logger.info("Operation A completed successfully.");
-
- logger.warning("This is a warning message.");
-
- logger.info("Performing operation B...");
- // 模拟错误
- throw std::runtime_error("Simulated error in operation B");
- }
- catch (const std::exception& e) {
- logger.error(std::string("Operation failed: ") + e.what());
- }
-
- logger.critical("This is a critical message.");
- logger.info("Application shutting down.");
-
- std::cout << "Log messages have been written to console and 'application.log'." << std::endl;
-
- return 0;
- }
复制代码
案例3:数据导出工具
创建一个将数据库查询结果导出为CSV文件的工具。
- #include <iostream>
- #include <fstream>
- #include <vector>
- #include <string>
- #include <iomanip>
- #include <sstream>
- // 模拟数据库记录
- struct Record {
- int id;
- std::string name;
- double value;
- std::string category;
- std::string date;
- };
- class DataExporter {
- private:
- std::vector<Record> records;
-
- // 转义CSV字段中的特殊字符
- std::string escapeCsvField(const std::string& field) {
- // 如果字段包含逗号、双引号或换行符,需要用双引号包围
- // 并且字段中的双引号需要转义为两个双引号
- if (field.find(',') != std::string::npos ||
- field.find('"') != std::string::npos ||
- field.find('\n') != std::string::npos) {
-
- std::string escaped = """;
- for (char c : field) {
- if (c == '"') {
- escaped += """";
- } else {
- escaped += c;
- }
- }
- escaped += """;
- return escaped;
- }
- return field;
- }
-
- public:
- void addRecord(const Record& record) {
- records.push_back(record);
- }
-
- bool exportToCsv(const std::string& filename) {
- std::ofstream outFile(filename);
-
- if (!outFile) {
- std::cerr << "Error: Could not open file " << filename << " for writing." << std::endl;
- return false;
- }
-
- // 写入CSV头部
- outFile << "ID,Name,Value,Category,Date" << std::endl;
-
- // 写入数据
- for (const auto& record : records) {
- outFile << record.id << ","
- << escapeCsvField(record.name) << ","
- << std::fixed << std::setprecision(2) << record.value << ","
- << escapeCsvField(record.category) << ","
- << escapeCsvField(record.date) << std::endl;
- }
-
- outFile.close();
-
- std::cout << "Successfully exported " << records.size() << " records to " << filename << std::endl;
- return true;
- }
-
- bool exportToHtml(const std::string& filename) {
- std::ofstream outFile(filename);
-
- if (!outFile) {
- std::cerr << "Error: Could not open file " << filename << " for writing." << std::endl;
- return false;
- }
-
- // 写入HTML头部
- outFile << "<!DOCTYPE html>" << std::endl;
- outFile << "<html>" << std::endl;
- outFile << "<head>" << std::endl;
- outFile << "<title>Data Export</title>" << std::endl;
- outFile << "<style>" << std::endl;
- outFile << "table { border-collapse: collapse; width: 100%; }" << std::endl;
- outFile << "th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }" << std::endl;
- outFile << "th { background-color: #f2f2f2; }" << std::endl;
- outFile << "tr:nth-child(even) { background-color: #f9f9f9; }" << std::endl;
- outFile << "</style>" << std::endl;
- outFile << "</head>" << std::endl;
- outFile << "<body>" << std::endl;
- outFile << "<h1>Data Export</h1>" << std::endl;
- outFile << "<table>" << std::endl;
-
- // 写入表头
- outFile << "<tr><th>ID</th><th>Name</th><th>Value</th><th>Category</th><th>Date</th></tr>" << std::endl;
-
- // 写入数据
- for (const auto& record : records) {
- outFile << "<tr>"
- << "<td>" << record.id << "</td>"
- << "<td>" << record.name << "</td>"
- << "<td>" << std::fixed << std::setprecision(2) << record.value << "</td>"
- << "<td>" << record.category << "</td>"
- << "<td>" << record.date << "</td>"
- << "</tr>" << std::endl;
- }
-
- // 写入HTML尾部
- outFile << "</table>" << std::endl;
- outFile << "</body>" << std::endl;
- outFile << "</html>" << std::endl;
-
- outFile.close();
-
- std::cout << "Successfully exported " << records.size() << " records to " << filename << std::endl;
- return true;
- }
-
- void printSummary() {
- if (records.empty()) {
- std::cout << "No records to summarize." << std::endl;
- return;
- }
-
- // 计算统计信息
- double totalValue = 0.0;
- double minValue = records[0].value;
- double maxValue = records[0].value;
-
- for (const auto& record : records) {
- totalValue += record.value;
- if (record.value < minValue) minValue = record.value;
- if (record.value > maxValue) maxValue = record.value;
- }
-
- // 输出摘要
- std::cout << std::fixed << std::setprecision(2);
- std::cout << "Data Summary:" << std::endl;
- std::cout << "=============" << std::endl;
- std::cout << "Total Records: " << records.size() << std::endl;
- std::cout << "Total Value: " << totalValue << std::endl;
- std::cout << "Average Value: " << (totalValue / records.size()) << std::endl;
- std::cout << "Minimum Value: " << minValue << std::endl;
- std::cout << "Maximum Value: " << maxValue << std::endl;
- }
- };
- int main() {
- DataExporter exporter;
-
- // 添加一些示例数据
- exporter.addRecord({1, "Product A", 125.50, "Electronics", "2023-01-15"});
- exporter.addRecord({2, "Product B", 89.99, "Clothing", "2023-01-16"});
- exporter.addRecord({3, "Product C", 45.00, "Home & Garden", "2023-01-17"});
- exporter.addRecord({4, "Product D", "Special, "Limited" Edition", 199.99, "Collectibles", "2023-01-18"});
- exporter.addRecord({5, "Product E", 75.25, "Electronics", "2023-01-19"});
-
- // 打印摘要
- exporter.printSummary();
- std::cout << std::endl;
-
- // 导出为CSV
- if (!exporter.exportToCsv("data_export.csv")) {
- return 1;
- }
-
- // 导出为HTML
- if (!exporter.exportToHtml("data_export.html")) {
- return 1;
- }
-
- std::cout << "Data export completed successfully." << std::endl;
-
- return 0;
- }
复制代码
总结
本教程全面介绍了C++输出编程的各个方面,从基础语法到高级应用。我们学习了:
1. 基础输出:使用cout进行基本输出操作,处理不同数据类型,以及链式输出的技巧。
2. 格式化输出:控制输出宽度、填充字符、对齐方式,以及数值格式、布尔值和整数的进制表示。
3. 文件输出:使用ofstream进行文件操作,包括不同的文件打开模式、二进制数据写入、文件位置指针操作和缓冲区管理。
4. 错误处理:检查流状态、使用异常处理、处理输出错误,以及实现自定义错误处理。
5. 高级应用:重定向标准输出、使用字符串流进行格式化、多线程输出、输出到多个目标,以及国际化输出。
6. 常见错误及避免方法:识别并避免C++输出编程中的常见错误,如未检查文件是否成功打开、忘记关闭文件、错误使用格式化操纵符等。
7. 实战案例:通过生成格式化报告、创建日志系统和数据导出工具等实际案例,展示了如何将所学知识应用到实际问题中。
基础输出:使用cout进行基本输出操作,处理不同数据类型,以及链式输出的技巧。
格式化输出:控制输出宽度、填充字符、对齐方式,以及数值格式、布尔值和整数的进制表示。
文件输出:使用ofstream进行文件操作,包括不同的文件打开模式、二进制数据写入、文件位置指针操作和缓冲区管理。
错误处理:检查流状态、使用异常处理、处理输出错误,以及实现自定义错误处理。
高级应用:重定向标准输出、使用字符串流进行格式化、多线程输出、输出到多个目标,以及国际化输出。
常见错误及避免方法:识别并避免C++输出编程中的常见错误,如未检查文件是否成功打开、忘记关闭文件、错误使用格式化操纵符等。
实战案例:通过生成格式化报告、创建日志系统和数据导出工具等实际案例,展示了如何将所学知识应用到实际问题中。
通过本教程的学习,您应该已经掌握了C++输出编程的核心技能,能够解决现实编程中的输出难题,提升开发技能,并避免常见的输出编程错误。继续实践和探索,您将能够更加熟练地运用这些技术,开发出更加健壮和高效的C++应用程序。 |
|