如果你在阅读过程中有任何疑问或想要进一步探讨的内容欢迎在评论区畅所欲言我们一起学习、共同成长~ 如果你觉得这篇文章还不错不妨顺手点个赞、加入收藏并分享给更多的朋友噢~1. C 语言的输入输出1.1 常用输入输出函数C 语言中最常用的输入输出方式是scanf()与printf()。scanf()从标准输入流通常是键盘读取格式化数据按照指定的格式将输入数据存储到对应的变量中。printf()将格式化的数据输出到标准输出流通常是显示器。使用时需注意宽度输出和精度输出控制。1.2 输入输出缓冲区C 语言借助相应的缓冲区来进行输入与输出输入输出缓冲区作用如下屏蔽低级 I/O 的实现低级 I/O 的实现依赖操作系统本身内核屏蔽这部分差异可使程序更具可移植性。实现 “行” 读取行为计算机本身没有 “行” 的概念通过缓冲区可定义 “行”并解析其内容返回 “行”。2. 流是什么“流” 是对有序连续且具有方向性的数据的抽象描述。C 流指信息从外部输入设备如键盘向计算机内部如内存输入以及从内存向外部输出设备显示器输出的过程。C 流的特性有序连续数据按顺序流动。有方向性分为输入流和输出流。基于 I/O 标准类库通过类对象实现输入输出操作。3. C IO 流C 中构建了一个体系庞大的类库在这个类库的继承体系里ios类是基类其他所有相关类均直接或间接派生自ios类。3.1 C 标准 IO 流3.1.1 全局流对象C 标准库提供了 4 个全局流对象cin、cout、cerr、clog。cin标准输入流键盘输入到程序属于istream类。cout标准输出流程序输出到显示器属于ostream类。cerr标准错误输出流无缓冲直接输出。clog标准日志输出流有缓冲。cout、cerr、clog是ostream类的三个不同对象应用场景不同但基本功能类似。使用时必须包含头文件iostream并引入std标准命名空间。3.1.2 关键细节3.1.2.1 缓冲机制cin为缓冲流输入数据先存入缓冲区提取时从缓冲区读取。输入错误会设置流状态字state但程序继续执行。3.1.2.2 数据类型匹配输入类型需与提取类型一致否则出错如给int变量输入字符将出错。3.1.2.3 分隔符处理空格和回车可作为数据分隔符字符型和字符串无法读取空格或回车。3.1.2.4 内置类型支持标准库已重载和运算符可直接输入输出内置类型如int、double。3.1.2.5 自定义类型支持需重载和运算符才能使用cin/cout。3.1.2.6 在线 OJ 输入输出1 IO类型的算法一般都要循环输入1单个元素循环输入适用场景读取多组单一类型数据如多组整数。 int num; while (cin num) { // 输入整数遇EOF结束 cout 输入的数 num endl; }2多个元素循环输入适用场景读取每行包含多个数据的输入如每行输入 “姓名 年龄 分数”。string name; int age; double score; while (cin name age score) { // 按顺序读取多个元素 cout name age score endl; }3非整行字符串输入不含空格适用场景读取以空格 / 换行分隔的单词如多个独立字符串。string word; while (cin word) { // 读取单个单词遇空格/换行/EOF结束 cout 单词 word endl; }4整行字符串输入含空格适用场景读取完整句子或段落如 “Hello, world!”。string line; while (getline(cin, line)) { // 读取整行包括空格遇EOF或空行结束 if (line.empty()) break; // 可选跳过空行 cout 整行内容 line endl; }2 输出必须与题目要求完全一致包括空格、换行符、标点符号等多一个或少一个字符均可能导致错误。3 Windows 系统的 VS 系列编译器中通过输入Ctrl Z再按下Enter就可发送EOF文件结束符信号快捷终止输入循环。3.1.2.7 流对象的逻辑判断istream 对象能直接用于条件判断。若输入成功则继续执行相关操作若输入失败则停止执行相关操作。自定义类型可通过重载operator bool()函数来添加额外的逻辑判断。这些额外逻辑判断通常用于设置业务层面的结束条件。istream对象可作为逻辑条件值是因为该对象会调用operator bool函数。当流提取成功时operator bool返回true当遇到文件结束符EOF如键盘输入CtrlZ、提取类型不匹配如给int变量输入字符或流被关闭时operator bool返回false循环终止。示例#include iostream #include string using namespace std; class Date { // 声明友元函数以访问私有成员 friend ostream operator(ostream out, const Date d); friend istream operator(istream in, Date d); public: Date(int year 1, int month 1, int day 1) : _year(year) , _month(month) , _day(day) {} // 类型转换运算符定义Date对象的逻辑判断条件 explicit operator bool() const { // 假设输入_year为0时返回false return _year ! 0; } private: int _year; int _month; int _day; }; // 重载流提取运算符从输入流读取Date对象 istream operator(istream in, Date d) { in d._year d._month d._day; return in; // 返回输入流对象支持链式调用如cin d1 d2 } // 重载流插入运算符向输出流写入Date对象 ostream operator(ostream out, const Date d) { out d._year - d._month - d._day; return out; // 返回输出流对象支持链式调用如cout d endl } int main() { // 输出内置类型自动调用标准库重载的运算符 int i 1; double j 2.2; cout 内置类型输出 endl; cout 整数i i endl; cout 浮点数j j endl; // 输出自定义类型Date调用重载的运算符 Date d(2022, 4, 10); cout \n初始化的日期 d endl; // 循环输入Date对象直到输入年份为0时结束 cout \n请输入日期格式年 月 日输入0 0 0结束 endl; while (cin d) { // 用operator返回的istream对象调用operator bool // 输入成功后检查Date对象的逻辑状态_year是否为0 if (!d) { cout 检测到结束标志年份为0退出程序。 endl; break; } cout 输入的日期为 d endl; cout 请继续输入或输入0 0 0结束 endl; } return 0; }3.2 C 文件 IO 流根据数据的组织形式数据文件可分为二进制文件和文本文件。数据在内存中以二进制形式存储若不加转换输出到外存文件中就是二进制文件若要求在外存上以 ASCII 码形式存储则需在存储前转换以 ASCII 字符形式存储的文件就是文本文件。采用文件流对象操作文件的一般步骤1定义文件流对象类名用途定义语法示例ifstream只读取文件1.直接构造并打开文件ifstream 对象名(文件名,模式);2. 默认构造后打开文件ifstream 对象名;对象名.open(文件名, 模式);若未显式指定模式将默认ios_base::instd::ifstream fin(data.txt);std::ifstream fin; fin.open(data.bin, std::ios_base::binary); // 或 std::ifstream fin; fin.open(data.bin, std::ios_base::in | std::ios_base::binary);ofstream只写入文件1. 直接构造并打开文件覆盖写入ofstream 对象名(文件名,模式);2.默认构造后打开文件ofstream 对象名;对象名.open(文件名, 模式);若未显式指定模式将默认ios_base::out类似ifstreamfstream读写文件fstream 对象名(文件名, 模式);模式必须包含ios_base::in|ios_base::outstd::fstream file(data.txt, std::ios_base::in | std::ios_base::out);// 追加读写 std::fstream file(data.txt, std::ios_base::in | std::ios_base::out | std::ios_base::app);注意ifstream / ofstream / fstream均需包含头文件fstream ifstream 、ofstream 、fstream、ios_base 要展开命名空间或使用std::前缀。2打开文件成员函数open(const char* filename, ios_base::mode)。常用打开模式ios_base::in输入模式读文件。ios_base::out输出模式写文件覆盖原有内容。ios_base::app追加模式写文件内容追加到末尾。ios_base::binary二进制模式默认为文本模式。3读写文件文本读写使用和运算符需重载自定义类型运算符。二进制读写使用write()和read()成员函数按字节操作。4关闭文件调用close()成员函数。3.2.1 模拟服务器配置信息的读写#include iostream #include fstream #include string using namespace std; class Date { public: Date(int year 1, int month 1, int day 1) : _year(year), _month(month), _day(day) {} friend ostream operator(ostream out, const Date d); friend istream operator(istream in, Date d); private: int _year; int _month; int _day; }; ostream operator(ostream out, const Date d) { out d._year - d._month - d._day; return out; } istream operator(istream in, Date d) { char separator1, separator2; // 用于读取日期中的分隔符如- in d._year separator1 d._month separator2 d._day; return in; } // 服务器信息结构体 struct ServerInfo { char _address[32]; // 地址字符数组 int _port; // 端口号 Date _date; // 日期已重载IO运算符 }; // 配置管理类 class ConfigManager { public: // 构造函数初始化文件名 explicit ConfigManager(const char* filename) : _filename(filename) {} // 二进制写入文件 void WriteBin(const ServerInfo info) { ofstream ofs(_filename, ios_base::out | ios_base::binary); if (!ofs.is_open()) { // 检查文件是否打开成功 cerr Error: Failed to open file for writing (binary). endl; return; } // 写入结构体数据需保证结构体无虚函数、无动态成员否则需自定义序列化逻辑 ofs.write(reinterpret_castconst char*(info), sizeof(info)); ofs.close(); } // 二进制读取文件 void ReadBin(ServerInfo info) { ifstream ifs(_filename, ios_base::in | ios_base::binary); if (!ifs.is_open()) { cerr Error: Failed to open file for reading (binary). endl; return; } // 读取结构体数据 ifs.read(reinterpret_castchar*(info), sizeof(info)); ifs.close(); } // 文本写入文件 void WriteText(const ServerInfo info) { ofstream ofs(_filename); if (!ofs.is_open()) { cerr Error: Failed to open file for writing (text). endl; return; } // 使用流插入运算符写入数据需保证Date类已重载 ofs info._address info._port info._date endl; ofs.close(); } // 文本读取文件 void ReadText(ServerInfo info) { ifstream ifs(_filename); if (!ifs.is_open()) { cerr Error: Failed to open file for reading (text). endl; return; } // 使用流提取运算符读取数据需保证Date类已重载 ifs info._address info._port info._date; ifs.close(); } private: string _filename; // 配置文件名 }; int main() { // 初始化服务器信息 ServerInfo winfo { 192.0.0.1, // 地址 80, // 端口号 {2022, 4, 10} // 日期使用Date构造函数初始化 }; // 创建配置管理器对象二进制和文本文件 ConfigManager cf_bin(test.bin); ConfigManager cf_text(test.txt); // ---------------------- 二进制操作 ---------------------- // 写入二进制文件 cf_bin.WriteBin(winfo); cout 二进制文件写入完成。 endl; // 读取二进制文件 ServerInfo rbinfo; cf_bin.ReadBin(rbinfo); cout 二进制读取结果 endl; cout 地址 rbinfo._address endl; cout 端口 rbinfo._port endl; cout 日期 rbinfo._date endl; cout endl; // ---------------------- 文本操作 ---------------------- // 写入文本文件 cf_text.WriteText(winfo); cout 文本文件写入完成。 endl; // 读取文本文件 ServerInfo rtinfo; cf_text.ReadText(rtinfo); cout 文本读取结果 endl; cout 地址 rtinfo._address endl; cout 端口 rtinfo._port endl; cout 日期 rtinfo._date endl; return 0; }4. 简单介绍stringstream传统C语言方法如 sprintf / itoa 在进行类型转换与字符串操作时存在缓冲区溢出风险且需手动管理内存空间尤其在处理结构体数据的序列化与反序列化时不够安全便捷。stringstream 通过流式操作实现自动类型推导并利用安全的string缓冲区管理有效简化了类型转换与字符串拼接过程规避了上述风险更适用于序列化场景。4.1 头文件与类定义头文件#include sstream相关类istringstream从字符串读取数据输入流。ostringstream向字符串写入数据输出流。stringstream双向操作读写字符串。4.2 核心功能4.2.1 数值类型转字符串避免itoa()或sprintf()的缓冲区溢出问题自动推导类型。示例代码#include iostream #include sstream #include string // 包含string类型头文件 using namespace std; int main() { int a 12345678; string sa; // 定义用于存储转换结果的string对象 stringstream s; // 定义stringstream对象用于数据转换 // 第一次转换将整数a转换为字符串 s a; // 向stringstream中插入int类型数据自动格式化 s sa; // 从stringstream中提取数据到string对象sa cout 整数转字符串结果 sa endl; // ---------------------- 关键操作清空流状态和底层缓冲区 ---------------------- s.clear(); // 重置流状态标志清除可能的badbit状态 s.str(); // 清空stringstream底层维护的string对象避免残留数据影响下次转换 // ---------------------------------------------------------------------------- // 第二次转换将双精度浮点数d转换为字符串 double d 12.34; s d; // 向stringstream中插入double类型数据自动格式化 s sa; // 从stringstream中提取数据到string对象sa cout 浮点数转字符串结果 sa endl; return 0; }4.2.2 字符串拼接方便合并多个字符串或变量。示例代码#include iostream #include sstream #include string using namespace std; int main() { // 创建stringstream对象用于字符串操作 stringstream sstream; // 向流中插入多个字符串进行拼接 sstream Hello World, 你好; // 从stringstream中提取拼接后的完整字符串 string result sstream.str(); cout 拼接结果 result endl; return 0; }4.2.3 序列化与反序列化结构数据将结构体数据转为字符串如网络传输或从字符串解析回结构体。示例代码#include iostream #include sstream #include string using namespace std; class Date { public: Date(int year 1, int month 1, int day 1) : _year(year), _month(month), _day(day) {} friend istream operator(istream in, Date d); friend ostream operator(ostream out, const Date d); private: int _year; int _month; int _day; }; istream operator(istream in, Date d) { char separator1, separator2; // 用于处理日期格式中的分隔符如YYYY-MM-DD in d._year separator1 d._month separator2 d._day; return in; } ostream operator(ostream out, const Date d) { out d._year - d._month - d._day; return out; } struct ChatInfo { string _name; int _id; Date _date; // 假设已重载和 string _msg; }; int main() { // 序列化结构体转字符串 ChatInfo winfo { 张三, 135246, {2022, 4, 10}, // 使用Date构造函数初始化日期 晚上一起看电影吧 }; ostringstream oss; // 按顺序输出结构体成员用空格分隔需注意字符串中的空格会导致反序列化问题 oss winfo._name winfo._id winfo._date winfo._msg; // 假设_msg中不含空格否则需特殊处理如转义或使用其他分隔符 string str oss.str(); cout 序列化字符串 str endl; // 反序列化字符串转结构体 ChatInfo rInfo; istringstream iss(str); // 按顺序读取数据与序列化顺序严格一致 iss rInfo._name rInfo._id rInfo._date rInfo._msg; // 检查反序列化是否成功处理可能的读取错误 if (iss.fail()) { cerr 反序列化失败输入格式错误 endl; return 1; } cout \n反序列化结果 endl; cout 姓名 rInfo._name ( rInfo._id ) endl; cout 时间 rInfo._date endl; cout 消息 rInfo._msg endl; return 0; }4.3 注意事项状态与缓冲区管理clear()重置流状态如badbit但不清空底层字符串。str()清空底层string对象避免多次转换时数据累积。安全性使用string代替字符数组避免缓冲区溢出。自动类型推导无需手动格式化减少错误风险。
郑州网站建设
网页设计
企业官网