大话C++:第23篇 输入输出

1 输入输出概述

C++输入输出(I/O)是C++编程语言中非常重要的一部分,它涉及到从外部设备(如键盘、文件等)读取数据以及将数据写入到这些设备中。C++提供了一套丰富的I/O库,程序员可以使用这些库来执行各种输入输出操作。 C++的I/O操作主要依赖于<iostream>头文件,它定义了用于输入输出的基本类型和函数。输入输出中的重要组件包括:

  • 流(Streams):C++的I/O操作基于流的概念。流是一个字符序列,可以从中读取数据或向其中写入数据。C++中有三种基本的流:输入流(istream)、输出流(ostream)和输入输出流(iostream)。

  • 输入流(istream) :输入流用于从外部设备(如键盘、文件等)读取数据。C++中,cin是一个预定义的对象,代表标准输入流,通常与键盘相关联。

  • 输出流(ostream) :输出流用于将数据写入到外部设备。C++中,cout是一个预定义的对象,代表标准输出流,通常与屏幕相关联。

  • 输入输出流(iostream) :输入输出流既可以用于读取数据,也可以用于写入数据。cerrclog是预定义的输入输出流对象,用于错误和日志输出。

  • 格式化I/O :C++的I/O库支持格式化输入输出,允许程序员控制数据的显示格式。例如,可以使用setwsetprecision等函数来控制输出的宽度和精度。

  • 文件I/O :除了标准输入输出流外,C++还支持文件输入输出。<fstream>头文件提供了用于文件I/O的类和函数。例如,ifstream类用于从文件中读取数据,ofstream类用于将数据写入文件。

  • 字符串流(stringstream) :字符串流允许在内存中执行I/O操作,而无需与外部设备交互。<sstream>头文件提供了stringstream类,用于在字符串上执行格式化输入输出操作。

除此此外,I/O操作通常与运算符<<(插入运算符)和>>(提取运算符)一起使用,因此,C++的输入输出系统提供了一套强大而灵活的机制。

2 标准输入输出

在C++中,标准输入输出流是最常见的输入输出方式,用于程序与用户之间的交互。这些流对象是由C++标准库提供的,并且在<iostream>头文件中定义。标准输入输出流包括:

C++ 的标准输入输出库是由 <iostream> 头文件提供的,该库使得 C++ 程序能够进行数据的输入和输出操作,且无需依赖具体的硬件设备。在标准库中,数据被看作是字符流,这种抽象允许程序以一种统一的方式来处理不同类型的设备(例如,键盘、屏幕、文件等)。

  • 标准输入流(std::cin):std::cin是C++中的标准输入流对象,通常与键盘输入关联。可以使用>>操作符从std::cin中读取用户输入的数据。

  • 标准输出流(std::cout):std::cout是C++中的标准输出流对象,通常与屏幕输出关联。可以使用<<操作符向std::cout中写入数据,以显示在屏幕上。

  • 标准错误流(std::cerr 和 std::clog):std::cerr是一个未缓冲的错误输出流,通常也与屏幕关联。它用于显示紧急的错误消息,因为这些消息需要立即显示给用户,而不需要等待缓冲区刷新。std::clog是一个缓冲的错误输出流,与std::cerr类似,但它会缓冲输出直到缓冲区满或显式地刷新缓冲区。

  • 流的状态和控制:每个输入输出流都有一个与之关联的状态,可以检查这个状态来确定流是否处于良好状态(例如,是否遇到了输入错误)。例如,std::cin.fail()会返回一个布尔值,指示输入流是否处于失败状态。还可以使用流控制操纵符来影响流的行为。例如,std::cin.ignore()可以用于忽略输入流中的字符,而std::cin.clear()可以用于重置输入流的状态。

cpp 复制代码
#include <iostream>
#include <limits>

int main() 
{
    int sum = 0;
    int number;

    std::cout << "请输入一系列整数(输入非数字结束):" << std::endl;

    while (std::cin >> number) 
    {
        sum += number;
        std::cout << "当前总和是: " << sum << std::endl;
    }

    // 检查输入流的状态
    if (std::cin.fail()) 
    {
        std::cerr << "错误: 输入无效,请输入整数!" << std::endl;
		// 清除错误状态
        std::cin.clear(); 
        // 忽略错误输入
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
    }

    if (sum > 0) 
    {
        std::cout << "输入的所有整数的总和是: " << sum << std::endl;
    } 
    else 
    {
        std::cout << "没有输入任何有效的整数。" << std::endl;
    }

    return 0;
}

3 格式化输入输出

在C++中,格式化输入输出是一个强大的功能,它允许我们以特定的格式控制数据的显示和读取。std::coutstd::cin都支持格式化操作,它们主要通过操纵符(manipulators)和格式标志(format flags)来实现。常见的格式化输入输出包括:

  • 设置字段宽度:使用std::setw操纵符可以设置下一个输出字段的最小宽度。如果输出数据的宽度小于指定的宽度,输出将使用空格进行填充。

  • 设置精度:对于浮点数,可以使用std::setprecision操纵符设置小数点后的精度。

  • 设置填充字符:使用std::setfill操纵符可以设置用于填充的字符,默认为空格。

  • 固定点表示与科学计数法:使用std::fixedstd::scientific操纵符可以选择浮点数的表示方式。

cpp 复制代码
#include <iostream>
#include <iomanip> // 包含格式化操纵符的头文件

int main() 
{
    int number = 123;
    double value = 123.456789;

    // 格式化输出
    std::cout << "整数(默认格式): " << number << std::endl;
    std::cout << "整数(5位宽度,左对齐): " << std::left << std::setw(5) << number << std::endl;
    std::cout << "整数(5位宽度,右对齐,0填充): " << std::right << std::setw(5) << std::setfill('0') << number << std::endl;
    std::cout << "浮点数(固定点表示,2位小数): " << std::fixed << std::setprecision(2) << value << std::endl;
    std::cout << "浮点数(科学计数法): " << std::scientific << value << std::endl;

    return 0;
}

4 字符串流

在C++中,字符串流是一种特殊的流,它允许我们在内存中处理字符串,就像处理文件或控制台输入/输出一样。字符串流在<sstream>头文件中定义,主要有三种类型:std::istringstreamstd::ostringstreamstd::stringstream

4.1 std::istringstream

std::istringstream是C++标准库中的一个类,定义在<sstream>头文件中。它是输入字符串流(input string stream)的一种,用于从内存中的字符串读取数据。std::istringstream继承自std::istream,因此它拥有std::istream的所有成员函数和操作符,可以用于处理各种类型的数据输入。std::istringstream的主要用法:

  • 创建对象 :需要先创建一个std::istringstream对象。

  • 读取数据 :一旦创建了std::istringstream对象,就可以使用输入操作符>>来读取数据。这与从std::cin读取数据非常相似。

  • 检查提取是否成功 :在提取数据后,可以检查提取操作是否成功。这通常通过使用条件语句和流的状态成员函数(如fail()good()eof()等)来完成。

  • 多次提取 :可以从同一个std::istringstream对象中多次提取数据。提取操作会更新流的内部状态,以便下次提取从正确的位置开始。

  • 处理错误和恢复 :如果在提取过程中遇到错误(如输入格式不匹配),std::istringstream的状态会变为错误状态。可以使用clear()成员函数来重置流状态,并使用ignore()成员函数来忽略(丢弃)错误的输入。

cpp 复制代码
#include <iostream>
#include <sstream> // 包含 stringstream 的头文件
#include <string>
#include <limits>

int main() 
{
    // 创建一个待解析的字符串
    std::string input = "123 45.6 hello";
    
    // 创建一个 istringstream 对象,并将待解析的字符串传递给它
    std::istringstream iss(input);
    
    // 声明变量,用于存储解析出的值
    int intValue;
    float floatValue;
    std::string strValue;
    
    // 使用 >> 操作符从 istringstream 对象中提取数据
    iss >> intValue >> floatValue; // 提取整数和浮点数
    
    // 需要注意的是,如果接下来使用 >> 操作符直接提取字符串,它只会提取到空格为止。
    // 为了提取整行(包括空格),这里我们改用 std::getline 函数。
    // 在使用 std::getline 之前,我们需要先忽略掉 istringstream 中前面的空白字符,
    // 因为前面的提取操作已经读取到了 "hello" 前面的空格,但我们不希望 strValue 包含这个空格。
    iss.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
    std::getline(iss, strValue); // 提取剩余的整行文本,包括空格
    
    // 注意:上述代码有一个错误,我们没有包含 <limits> 头文件,这是 numeric_limits 所需的。
    // 但是,在这个特定的情况下,因为我们知道字符串的格式,并且确信在提取整数和浮点数后没有多余的字符,
    // 我们可以直接使用 std::getline 而不需要忽略字符。如果格式不确定,应该小心处理空白字符。

    // 正确处理空格的情况(已知格式时):
    // 因为我们已经提取了整数和浮点数,iss 的当前位置在 "hello" 前面的空格处。
    // 而 std::getline 会提取直到下一个换行符或字符串结束的所有字符,包括开始的空格(如果不跳过的话)。
    // 但因为我们确信整数和浮点数后面紧跟的就是我们想要的字符串,所以可以直接这样写:
    std::getline(iss, strValue); // 正确:这会提取 " hello"(包括前面的空格)
    // 如果我们不想要前面的空格,可以自己用 iss.ignore() 跳过,或者直接从原始字符串处理(如果不确定有多少空格)。
    // 但在这个例子里,我们保持简单并接受这个空格。

    // 输出提取出的值
    std::cout << "Integer: " << intValue << std::endl;     // 应该输出 "Integer: 123"
    std::cout << "Float: " << floatValue << std::endl;     // 应该输出 "Float: 45.6"
    std::cout << "String: " << strValue << std::endl;      // 应该输出 "String:  hello"(注意前面可能有空格)
    
    return 0;
}

4.2 std::ostringstream

std::ostringstream 是 C++ 标准库中的一个类,用于在内存中创建和操作字符串流。这个类在 <sstream> 头文件中定义,是输出字符串流(ostream)的一种特殊形式,用于向字符串写入数据而不是向文件或控制台写入。

std::ostringstream 的主要特点:

  • 内存操作std::ostringstream 是在内存中操作字符串的,这意味着你可以在不涉及文件或外部设备的情况下生成和处理字符串。

  • 格式化输出 :你可以使用 std::ostringstream 类似于 std::cout 的方式插入和格式化各种数据类型,例如整数、浮点数、字符和字符串等。

  • 动态内存分配std::ostringstream 会根据需要自动管理其内部的内存。你不需要担心缓冲区的大小,因为它会根据内容动态地分配和重新分配内存。

  • 转换和拼接 :你可以使用 std::ostringstream 将非字符串数据类型转换为字符串,或将多个字符串轻松地拼接在一起。

  • 操作方便 :通过 << 操作符,你可以方便地向 std::ostringstream 对象中插入数据。

std::ostringstream 主要成员函数和操作符:

  • 构造函数 :创建一个 std::ostringstream 对象。

  • str() :这是一个成员函数,用于获取流中的当前字符串内容。它返回的是一个 std::string 对象,包含了从流开始到当前位置的所有字符。还有一个重载版本 str(const std::string& new_str),允许你设置流的内容。

  • << 操作符 :这是一个重载的操作符,用于向 std::ostringstream 对象中插入数据。你可以使用这个操作符来插入各种数据类型,包括字符串、数字等。

  • 其他继承自 std::ostream 的成员std::ostringstreamstd::ostream 类继承,因此它还包含了这个基类的所有成员,例如设置格式标志、控制浮点精度等。

cpp 复制代码
#include <iostream>
#include <sstream>
#include <string>

int main() 
{
    // 假设这是用户输入的信息
    std::string name = "Jack";
    int age = 30;
    std::string occupation = "软件工程师";
    double income = 120000.0;

    // 创建一个ostringstream对象来格式化字符串
    std::ostringstream oss;

    // 使用<<操作符向ostringstream对象中插入数据
    oss << "Name: " << name << "\n"
        << "Age: " << age << "\n"
        << "Occupation: " << occupation << "\n"
        << "Annual Income: ¥" << income;

    // 使用str()成员函数来获取生成的字符串
    std::string personalInfo = oss.str();

    // 输出结果
    std::cout << "格式化后的个人信息:\n" << personalInfo << std::endl;

    return 0;
}

4.3 std::stringstream

std::stringstream 是 C++ 标准库中的一个类,定义在 <sstream> 头文件中。它是字符串流的一种,既可以用于读取字符串(输入操作),也可以用于写入字符串(输出操作)。std::stringstream 实际上是一个输入/输出字符串流(iostream),它允许在内存中处理字符串,而不是通过文件或控制台。

std::stringstream 的主要特点:

  • 内存操作std::stringstream 在内存中处理字符串,不涉及外部设备或文件。

  • 格式化 :可以使用 std::stringstream 格式化字符串,类似于使用 std::coutstd::cin,但操作的是内存中的字符串。

  • 动态内存管理std::stringstream 会根据需要自动分配和重新分配内存,以存储其内容。

  • 灵活性 :你可以使用 std::stringstream 读取、写入、修改和拼接字符串,以及执行其他字符串操作。

std::stringstream 的主要成员函数和操作符:

  • 构造函数 :创建一个 std::stringstream 对象。

  • str() :获取或设置流中的字符串内容。这个函数有一个无参数的版本,返回流中的当前字符串,还有一个接受 std::string 参数的版本,用于设置流的内容。

  • <<>> 操作符:用于向流中插入数据或从流中提取数据。这些操作符可以处理多种数据类型。

  • 其他继承自 std::iostream 的成员std::stringstreamstd::iostream 类继承,因此它还包含了这个基类的所有成员,如 peek()ignore()clear() 等。

cpp 复制代码
#include <iostream>
#include <sstream>
#include <string>

int main() 
{
    // 假设这是用户输入的原始信息(一行字符串)
    std::string input = "Jack 30 Software Engineer";

    // 创建一个 stringstream 对象来解析输入
    std::stringstream ss(input);

    // 声明变量来存储解析出的信息
    std::string firstName;
    int age;
    std::string occupation;

    // 解析输入字符串
    ss >> firstName; // 读取第一个空格前的字符串作为名字
    ss >> age; // 读取下一个整数作为年龄

    // 由于职业可能包含空格,我们需要读取剩余的所有内容
    std::getline(ss, occupation); // 注意:这里会在第一次调用时消耗掉一个空行(因为前面的输入操作已经读取到行尾),因此我们需要先清除这个空行
    if (occupation.empty()) {
        std::getline(ss, occupation); // 重新读取一行作为职业
    }

    // 输出解析后的信息以验证
    std::cout << "First Name: " << firstName << std::endl;
    std::cout << "Age: " << age << std::endl;
    std::cout << "Occupation: " << occupation << std::endl;

    // 现在,我们使用另一个 stringstream 对象来格式化这些信息
    std::stringstream outputSs;
	outputSs << "Name: " <<  std::endl;
    outputSs << "Age: " << age << " years old" << std::endl;
    outputSs << "Profession: " << occupation;

    // 获取格式化后的字符串
    std::string formattedOutput = outputSs.str();

    // 输出格式化后的信息
    std::cout << "\nFormatted Output:\n" << formattedOutput << std::endl;

    return 0;
}

欢迎您同步关注我们的微信公众号!!!

相关推荐
Crazy learner5 分钟前
C 和 C++ 动态库的跨语言调用原理
c语言·c++
金士顿3 小时前
MFC 文档模板 每个文档模板需要实例化吧
c++·mfc
人才程序员6 小时前
QML z轴(z-order)前后层级
c语言·前端·c++·qt·软件工程·用户界面·界面
w(゚Д゚)w吓洗宝宝了6 小时前
C vs C++: 一场编程语言的演变与对比
c语言·开发语言·c++
小老鼠不吃猫8 小时前
C++点云大文件读取
开发语言·c++
姚先生978 小时前
LeetCode 35. 搜索插入位置 (C++实现)
c++·算法·leetcode
CoderCodingNo9 小时前
【GESP】C++二级考试大纲知识点梳理, (4)流程图
开发语言·c++·流程图
小小unicorn9 小时前
【C++初阶】STL详解(十三)—— 用一个哈希表同时封装出unordered_map和unordered_set
java·c++·散列表
慕羽★9 小时前
详细介绍如何使用rapidjson读取json文件
linux·c++·windows·json·file·param·rapidjson
大梦百万秋10 小时前
C++中的虚拟化:通过虚拟机管理模拟服务器虚拟化
服务器·开发语言·c++