文件操作、流对象示例

一、向文件中写入内容

我们现在桌面创建一个.txt文件,将里面的内容设置为abcdefghijklmn

如果想要向这个文件中写入内容,我们需要借助文件流对象建立起文件与程序之间的联系。

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
using namespace std;

int main()
{
	ofstream outFile("C:\Users\31604\Desktop\example.txt", ios::app);
	if (outFile.is_open())
	{
		outFile << " opqrst " << endl;
		outFile.close();
	}
	else
	{
		throw runtime_error("Fail to open file");
		cout << endl;
	}
	return 0;
}

这么写会报错,因为在C++中,\是转义字符,如果只使用单个反斜杠,会报错:通用字符名的格式不正确。

解决方法有两个:

方法一:加两个反斜杠,就代表我们真的想写入反斜杠。

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
using namespace std;

int main()
{
	ofstream outFile("C:\\Users\\31604\\Desktop\\example.txt", ios::app);
	if (outFile.is_open())
	{
		outFile << " opqrst " << endl;
		outFile.close();
	}
	else
	{
		throw runtime_error("Fail to open file");
		cout << endl;
	}
	return 0;
}

方法二:使用原始字符串字面量。从 C++11 开始,可以使用原始字符串字面量,在字符串前加上R,并用 ( ) 括起字符串内容,这样字符串中的反斜杠就不会被当作转义字符处理。代码如下:

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
using namespace std;

int main()
{
    ofstream outFile(R"(C:\Users\31604\Desktop\example.txt)", ios::app);
    if (outFile.is_open())
    {
        outFile << " opqrst " << endl;
        outFile.close();
    }
    else
    {
        throw runtime_error("Fail to open file");
        cout << endl;
    }
    return 0;
}

我们此时再打开这个example.txt文件,就会发现里面的内容变成了这样:

说明成功写入。

需要注意的是,当我们在 IDE中运行程序时,程序的当前工作目录不一定是桌面。所以它可能尝试在其他目录中创建或追加到名为example.txt的文件,而不是桌面上的那个文件。

ios::app是什么?

ofstream(输出文件流)对象的构造函数有多个重载版本,以下是两个常见的版本:

cpp 复制代码
ofstream(const char* filename);//只指定文件路径
cpp 复制代码
ofstream(const char* filename,ios_base::openmode mode);//指定文件路径和打开方式

ios_base::openmode是一个位掩码类型,定义了多种打开模式,常见的有:

cpp 复制代码
ios_base::app    追加模式,写入数据时将数据追加到文件末尾
ios_base::ate    文件打开后立即将文件指针移动到文件末尾
ios_base::in     以读模式打开文件(通常用于ifstream)
ios_base::out    以写模式打开文件(默认覆盖写入)
ios_base::trunc  如果文件存在,截断文件,即删除原有内容  

这些模式可以通过按位或(|)运算符组合使用。例如:

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

int main() 
{
    // 使用 ios_base 设置文件打开模式
    std::ofstream outFile("example.txt", std::ios_base::out | std::ios_base::trunc);
    if (outFile.is_open()) 
    {
        // 使用 ios_base 设置格式标志
        outFile << std::ios_base::hex << 100 << std::endl; 
        // 以十六进制输出 100,输出结果为 64
        outFile.close();
    }

    return 0;
}

追问:ios_base和ios有什么区别?

ios_base类是ios类的父类,ios类是istream类和ostream类的父类。

ios_base类定义了一些基本的流操作特性和常量,比如格式标志(控制输入输出的格式)、流操作符等。当涉及到文件打开模式、格式标志(例如控制数字输出的进制)这些基础的流操作特性常量时,要用ios_base.例如ios_base::in(用于输入文件流打开方式)、ios_base::hex(设置十六进制输出格式)等。

而当我们需要处理流的状态管理,比如判断流是否处于良好状态(ios::good())、是否到达文件末尾(ios::eof()),或者设置和获取流的错误状态标志等操作时,就使用ios类及其派生类(istream\ostream等)

如果是进行输入输出操作,比如从输入流读取数据,或者向输出流写入数据,则使用istream和ostream及其派生类(ifstream\ofstream等)。

举一个使用ios管理流状态的例子:

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

int main() 
{
    std::string str = "123";
    std::istringstream iss(str);
    int num;
    iss >> num;

    if (iss.good()) 
    {
        std::cout << "成功读取数字: " << num << std::endl;
    } 
    else if (iss.eof()) 
    {
        std::cout << "到达流末尾,但可能读取不完整。" << std::endl;
    } 
    else 
    {
        std::cout << "读取失败。" << std::endl;
    }

    return 0;
}

需要注意的是,ios类尽管确实是从ios_base类public继承而来的,但那些用于文件打开模式的常量(ios_base::out、ios_base::trunc等),它们并不是以常规的成员变量或成员函数的访问控制方式来决定是否能在子类ios中直接使用。这些常量是在ios_base的作用域内定义的枚举类型成员。虽然ios继承了ios_base的成员,但在 C++ 语法规则里,要使用这些枚举常量,必须通过定义它们的类名(也就是ios_base)来限定访问,即便继承是public的。

二、将文件中的内容读入字符串中

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
using namespace std;

int main()
{
	ifstream inFile(R"(C:\Users\31604\Desktop\example.txt)");
	if (inFile.is_open())
	{
		string line;
		while (getline(inFile, line))
		{
			cout << line << endl;
		}
		inFile.close();
	}
	else
	{
		cerr << "无法打开文件" << endl;
	}
	return 0;
}

运行结果:

对getline()不熟悉的,请参阅:对流对象的理解-CSDN博客

我们还可以指定分隔符,不一定读到默认的换行符,代码如下:

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
using namespace std;

int main()
{
	ifstream inFile(R"(C:\Users\31604\Desktop\example.txt)");
	if (inFile.is_open())
	{
		string line;
		while (getline(inFile, line,'g'))
		{
			cout << line << endl;
		}
		inFile.close();
	}
	else
	{
		cerr << "无法打开文件" << endl;
	}
	return 0;
}

运行结果:

也就是说,在重载版本getline(istream&is,string&str,char ch)中,ch作为分隔符,充当了原本换行符的角色。换行后,getline()会继续读入,会覆盖原有str中的内容,所以输出结果就会如上所示。

三、使用istringstream读取字符串中的内容

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
#include<string>
using namespace std;

int main()
{
	string str = "123 45.6 hello";
	istringstream iss(str);//构造函数

	int int_val;
	double double_val;
	string string_val;

	iss >> int_val;
	iss >> double_val;
	iss >> string_val;

	cout << "读取的整数:" << int_val << endl;
	cout << "读取的双精度浮点数:" << double_val << endl;
	cout << "读取的字符串:" << string_val << endl;

	return 0;
}

运行结果:

如果数据类型和读取的顺序不一致,例如下面这样:

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
#include<string>
using namespace std;

int main()
{
	string str = "123 four 78.9 hello";
	istringstream iss(str);//构造函数

	int int_val;
	double double_val;
	string string_val;

	iss >> int_val;
	iss >> double_val;
	iss >> string_val;

	cout << "读取的整数:" << int_val << endl;
	cout << "读取的双精度浮点数:" << double_val << endl;
	cout << "读取的字符串:" << string_val << endl;

	return 0;
}

会显示 读取的双精度浮点数为0,读取的字符串为空

因为从输入流iss读取数据时,它期望读取一个符合双精度浮点数格式的值。然而在读取完整数123后,下一个非空白字符序列是"four",这不是一个有效的双精度浮点数表示。

当istringstream尝试将"four"转换为双精度浮点数时,转换失败。这会导致输入流进入错误状态(failbit被设置),并且后续从该流的读取操作(如iss>>string_val;)将不再进行,因为一旦流处于错误状态,它会忽略后续的读取请求。

要解决这个问题,可以在每次读取操作后检查流的状态,以确保读取成功。例如:

cpp 复制代码
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

int main() 
{
    string str = "123 four 78.9 hello";
    istringstream iss(str);

    int int_val;
    double double_val;
    string string_val;

    if (!(iss >> int_val)) 
    {
        cerr << "无法读取整数" << endl;
        return 1;
    }

    if (!(iss >> double_val)) 
    {
        // 跳过 "four" 并尝试重新读取双精度浮点数
        string skip;
        iss >> skip;
        if (!(iss >> double_val)) 
        {
            cerr << "无法读取双精度浮点数" << endl;
            return 1;
        }
    }

    if (!(iss >> string_val)) 
    {
        cerr << "无法读取字符串" << endl;
        return 1;
    }

    cout << "读取的整数:" << int_val << endl;
    cout << "读取的双精度浮点数:" << double_val << endl;
    cout << "读取的字符串:" << string_val << endl;

    return 0;
}

四、使用ostringstream向字符串中写入内容

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
#include<string>
using namespace std;

int main()
{
	int num = 100;
	double pi = 3.14159;
	string name = "kvermouth";

	ostringstream oss;
	oss << "数字:" << num << ",圆周率:" << pi << ",名字:" << name;//写入内容

	string res = oss.str();//oss.str()用于获取最终生成的字符串

	cout << res << endl;

	return 0;
}

运行结果:

五、使用stringstream,既能读,又能写

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
#include<string>
using namespace std;

int main()
{
	stringstream ss;
	int num = 66;
	string str = "world";

	//写入
	ss << "Hello," << num << " " << str;

	//读取
	string new_str;
	ss >> new_str;

	cout << new_str << endl;
	
	return 0;
}

运行结果:

为什么输出的结果不是Hello,66 world呢?

  • 首先,代码中创建了一个stringstream对象ss,然后将数据写入这个流中。执行 ss << "Hello," << num << " " << str;时,stringstream会按照顺序将"Hello"、整数66(转换为字符串形式"66")、一个空格字符" "以及字符串"world"连接起来写入流中。此时stringstream内部维护的缓冲区内容大致为"Hello,66 world"
  • 接着执行ss>>new str; stringstream从其缓冲区读取数据时,会以空白字符(空格、制表符、换行符等)作为分隔符。所以它会读取到第一个空白字符(这里是空格)之前的内容,也就是"Hello,66"并将其存储到new_str中。

如果我们想要读取整个字符串的内容,可以使用getline()

cpp 复制代码
#include<iostream>
#include<fstream>
#include<sstream>
#include<string>
using namespace std;

int main()
{
	stringstream ss;
	int num = 66;
	string str = "world";

	//写入
	ss << "Hello," << num << " " << str;

	//读取
	string new_str;
	getline(ss, new_str);

	cout << new_str << endl;
	
	return 0;
}
相关推荐
大猩猩爱分享3 小时前
Mac安装docker desktop
macos·docker
伊织code3 小时前
pmset - 控制 macOS 系统电源、睡眠、唤醒与节能
macos·命令·电源·睡眠·节能·唤醒·pmset
serve the people4 小时前
在mac上安装sh脚本文件
macos
莫邪博客4 小时前
解决蓝牙MAC 地址倒序问题
macos
草明4 小时前
macOS 查看当前命令行的ruby的安装目录
开发语言·macos·ruby
五阿哥爱跳舞4 小时前
MAC无法 ping 通github 系列主页
macos
前端 贾公子19 小时前
「混合开发」H5与原生App交互流程方案全面解析
macos·objective-c·cocoa
I烟雨云渊T20 小时前
2025年的WWDC所更新的内容
macos·ios·wwdc
Fatbobman(东坡肘子)20 小时前
WWDC 2025 开发者特辑 | 肘子的 Swift 周报 #088
开发语言·macos·ios·swiftui·ai编程·swift·wwdc