赞
踩
一起来康康C++中的文件IO操作吧
之前写OJ的时候,就已经用过上面这种方式来获取多组测试用例
string s;
while(cin>>s)
{
cout << s << endl;
}
不过之前一直没有去了解这里的底层原理是什么,借此机会一并说明
io流可以进行while判断的依据,是因为库的源码中重载了operator bool
没错,operator
不仅可以重载一个操作符,它还可以重载一个类型!即将这个类转换为bool类型,return
一个bool类型的值用于while的判断
同理,重载int/double
这些类型都是可行的!
另外,要想停止上面的多组输入,在VS下可以用
ctrl+z
的方式解决,而不要用ctrl+c
直接杀掉进程
C++的文件io类设计的较为复杂,其中还出现了菱形继承,也就是我们最常用的iostream
上面提到的operator bool
就是基类IOS
实现的,子类都没有去重写
其中需要注意的一点是,空格和回车会被当作数据之间的分隔符,所以字符串中不能有空格,回车和空格也不能通过cin读入
如果需要读入带空格的完整一行,可以使用getline
函数
因为库里面已经将所有类型通过操作符重载<<
和>>
实现了,达到了自动类型识别的效果
C++标准库中提供的打开方式如下,我们可以根据不同情况传入不同的值,或者一次性用按位或|
传入多个打开方法
同时因为C++类和对象会自动调用析构函数,所以我们也不需要手动close文件
这里为何可以用按位与传入多个方法?
假设这些方法就是简单的数字2/4/8
(必须是2的倍数)我们可以通过按位与了之后,在按位或,判断某一个数字是否在其中
如果或了之后的数字等于它本身,说明数据在其中!
这种方法在linux中常见,比如linux系统的文件接口
这个对象是用于读取文件的,默认情况下,传入的打开方法为in
void test1()
{
ifstream ifs("test.txt");
while (ifs)
{
char ch = ifs.get();
cout << ch;
}
}
因为重载了bool,所以可以很方便的直接用while来判断结束,成功输出了文件中的内容
ifstream ifs("test.txt");
char ch;
while (ifs >> ch)
{
cout << ch;
}
第二种读取方法采用了流插入>>
上面提到过,流提取和插入的时候,会把空格和换行当作数据的分隔符,所以它是不能打印出空格和换行
写文件的方式同上,用out方法打开文件就可以了(默认传的就是out)
ofstream ifs2("test1.txt",ios::out);
char str[] = "i love u\n";
ifs2.write(str,sizeof(str));
不过这个和C语言的w方法一样,写入的时候会覆盖文件中已有的内容。如果想进行追加,则需要在后面加上app
;如果是执行二进制读写,则需要与上ios::binary
ofstream ifs2("test1.txt",ios::out|ios::app);
char str[] = "i love u";
ifs2.write(str,sizeof(str));
运行成功后会在文件尾部追加内容
注意,这里的字符串不能用string进行处理,因为string内部只存了一个指向字符串空间的指针,写入string相当于把指针写入进文件中,是么有用的!
但是,如果我们用<<
进行提取的时候,就可以用string了
void test2() { ifstream ifs("test.txt"); ofstream ofs("test1.txt"); char ch; ch = ifs.get(); while (~ch) { ofs << ch; cout << ch; ch = ifs.get(); } string s("i love you"); ofs << s; }
程序运行后,会把test.txt
中的内容输出到控制台,同时写入到test1.txt
最后还会写入一个string
的内容
这是因为我们调用<<
的时候,就和cin<<s
一样,调用的是string
重载的流提取操作符
底层实现的时候会将其转为C语言的字符串,从而写入到了文件中
所以当我们需要写入一个自定义类型的时候,可以重载流提取操作符,不仅可以更方便的打印,还可以写入到文件中
这个类可以将不同的类型转为字符串
这种操作被称为序列化和反序列化,在处理自定义类型的时候非常好用
struct Date { public: friend ostream& operator << (ostream& out, const Date& d); friend istream& operator >> (istream& in, Date& d); Date(int y=0, int m=0, int d=0) { _year = y; _month = m; _day = d; } private: int _year; int _month; int _day; }; istream& operator >> (istream& in, Date& d) { in >> d._year >> d._month >> d._day; return in; } ostream& operator << (ostream& out, const Date& d) { out << d._year << " " << d._month << " " << d._day; return out; } void test4() { int i = 123; double d = 44.55; ostringstream oss;//序列化 oss << i; string stri = oss.str(); oss.str("");//清空oss oss << d; string strd = oss.str(); cout << strd<< endl; oss.str("");//清空oss Date d1(2022, 10, 11); oss << d1; string strdt = oss.str(); cout << strdt << endl; istringstream iss(strdt);//反序列 Date d2; iss >> d2; cout << d2 << endl; }
这个对象可以用于字符串拼接,也可以用来将其他类型转为str
stringstream sstream;
// 将多个字符串放入 sstream 中
sstream << "first" << " " << "string,";
sstream << " second string";
cout << "strResult is: " << sstream.str() << endl;
// 清空 sstream
sstream.str("");
sstream << "third string";
cout << endl;
cout << "After clear, strResult is: " << sstream.str() << endl;
stringstream实现转换,实际上是维护了一个string对象实现的
我们可以使用str("")
清空里面的string对象,设置为空字符串
多次数据类型转换的时候,需要用clear()
来清空,才能正确转换。不过clear()
不会清空底层的string对象
因为使用的是string对象,所以使用的时候不需要格式化控制,可以自动推导类型
stringstreams
在转换结尾时(即最后一个转换后),会将其内部状态设置为badbit
因此下一次转换是必须调用clear()
将状态重置为goodbit
才可以继续转换
这里在第一次调用stringstream
操作后,我们没有进行clear,会发现后续的double类型转换失败了
执行了clear之后,转换成功!
关于C++IO流操作的基本认识到这里就over了。
因为在实际中用的并不算多,所以这部分的内容大多数是了解一二,知道如何使用,以及3.0
中提到的按位与操作是怎么实现的就OK了!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。