-- TOC --
这部分属于使用C++的基本功,随时都要能够拿出来使用。
#include <iostream>
#include <fstream>
using namespace std;
int main(void) {
// ofstream, default is write txt truncate mode
ofstream fout{"fout.txt"};
if (!fout.is_open()) {
cerr << "ofstream open fout.txt error" << endl;
return 1;
}
fout << " 123\n45 67\t890" << endl;
fout << "ab\rcde hj\fk\vlo " << endl;
fout.flush();
fout.close();
string cont;
// ifstream, default is read txt mode
ifstream fin{"fout.txt"};
if (!fin.is_open()) {
cerr << "ifstream open fout.txt error" << endl;
return 2;
}
while (fin.peek() != EOF) {
// space and \n are both seperators
cont.clear();
fin >> cont;
if (cont.size() != 0)
cout << cont << "(" << cont.size() << ")" << endl;
}
fin.close();
return 0;
}
流式地写入文本,流式地读出文本。
ofstream
对象默认就是文本输出模式,并默认带有ios::trunc
,这会让此文件原有的内容被清理掉(w
)。ifstream
对象默认就是本文读取模式(r
),每当遇到whitespace符号,算一次读出。读出来的cont,已经做了trim,自动去掉了whilespace符号。peek
接口用于探测EOF,不会移动文件读写指针位置。is_open
接口用来判断打开文件是否成功。endl
用于写入换行。close
关闭文件,也能起到flush
的效果。
输出如下:
$ g++ -Wall -Wextra fstream.cpp -o fstream
$ ./fstream
123(3)
45(2)
67(2)
890(3)
ab(2)
cde(3)
hj(2)
k(1)
lo(2)
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
template<typename T>
void write_file(T cont) {
ofstream fout;
fout.open("fout.log", ios::app);
fout << cont << endl;
fout.close();
}
int main(void) {
write_file("12345");
write_file("abcde");
write_file("https://cs.pynote.net");
string s{"string object"};
write_file(s);
return 0;
}
创建ofstream
对象后,用ios::app
(a
)打开这个对象。
上例使用了template function,因此string object和rvalue都可以直接调用write_file接口。
本文文件还可以用getline来读取,但是传入的line buffer一定要足够长:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
template<typename T>
void write_file(T cont) {
fstream fout;
fout.open("fout.log", ios::out|ios::app);
fout << cont << endl;
fout.close();
}
int main(void) {
// clean file
fstream fout{"fout.log", ios::out};
fout.close();
write_file("12\n345678\t90");
write_file(" a bcd\re ");
write_file("https://cs.pynote.net");
string s{"string\vobject"};
write_file(s);
fstream fin("fout.log", ios::in);
if (!fin.is_open())
return 1;
char buf[64]{};
string cont{};
int i=0;
while (!fin.eof()) { // recommend: fin.peek() != EOF
fin.getline(buf, 64);
cont = string{buf};
cout << ++i << " : " << cont << "|" << cont.size() << endl;
}
return 0;
}
如果用fstream
创建对象,就要明确ios::in
或ios::out
。
如果将getline
接口64这个参数修改为4,运行就能看到出错了(很严重),getline接口要求line buffer要足够大,大于读取的所有行。除非确定知道最大长度,否则尽量别用getline。
只有\n
是换行符,因此,getline只对文本中的\n
符号敏感,其它whitespace符号都原样读出。(注意看下面输出的第3行,前面的index被读出的\r
符号冲掉了,测试代码没有\f
符号,输出的时候屏幕会上移,我不喜欢)
使用eof
接口,会导致多读一次。eof接口只是判断最后getline的内容,虽然此时已经到了EOF,但是EOF还没有被读取出来,因此eof接口判断为false,继续getline一次。(peek接口真是形象呀)
以上代码的输出为:
1 : 12|2
2 : 345678 90|9
e |9 a bcd
4 : https://cs.pynote.net|21
5 : string
object|13
6 : |0
ios::app: //以追加的方式打开文件
ios::ate: //文件打开后定位到文件尾,ios::app就包含有此属性
ios::binary: //以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文
ios::in: //文件以输入方式打开(文件数据输入到内存)
ios::out: //文件以输出方式打开(内存数据输出到文件)
ios::nocreate: //不建立文件,所以文件不存在时打开失败
ios::noreplace: //不覆盖文件,所以打开文件时如果文件存在失败
ios::trunc: //如果文件存在,把文件长度设为0
多个属性的组合,用|
,如前代码。
#include <iostream>
#include <fstream>
using namespace std;
int main(void) {
ofstream fout{"fout.txt"};
if (!fout.is_open()) {
cerr << "ofstream open fout.txt error" << endl;
return 1;
}
fout << "12345" << endl;
fout << "abcde" << endl;
fout << "67890" << endl;
fout.close();
ifstream fin{"fout.txt"};
if (!fin.is_open()) {
cerr << "ifstream open fout.txt error" << endl;
return 2;
}
int cont;
while (fin.peek() != EOF) { // never use fin.eof(), dead loop
fin >> cont;
if (fin.fail())
cout << "fail..\n";
else
cout << cont << endl;
/*if (fin.good()) // no error
cout << cont << endl;
else
cout << "not good...\n";*/
}
fin.close();
return 0;
}
cont的类型是int,读取文件的第2行是字符串,因此触发fail或not good。但不会触发bad接口。
按单个字符读写文件,使用get和put接口。
#include <iostream>
#include <fstream>
using namespace std;
int main(void) {
ofstream fout{"fout.txt"};
if (!fout.is_open()) {
cerr << "ofstream open fout.txt error" << endl;
return 1;
}
fout.put('a');
fout.put('b');
fout.put('c');
fout.close();
ifstream fin{"fout.txt"};
if (!fin.is_open()) {
cerr << "ifstream open fout.txt error" << endl;
return 2;
}
char c;
while (fin.peek() != EOF) {
c = fin.get();
cout << c << endl;
}
fin.close();
return 0;
}
g
et,表示输入流;p
ut,表示输出流;部分接口末尾有g和p。
tellp
,返回输出流指针位置;tellg
,返回输入流指针位置;seekp
,设置输出流写入指针位置;seekg
,设置输入流读取指针位置;// seek接口需要用到的第2个参数:
ios::beg //从流开始位置计算的位移
ios::cur //从流指针当前位置开始计算的位移
ios::end //从流末尾处开始计算的位移
#include <iostream>
#include <fstream>
using namespace std;
int main(void) {
ofstream fout{"fout.txt"};
if (!fout.is_open()) {
cerr << "ofstream open fout.txt error" << endl;
return 1;
}
fout.put('a');
cout << fout.tellp();
fout.put('b');
fout.seekp(0, ios::beg);
fout.put('c');
cout << fout.tellp();
fout.close();
ifstream fin{"fout.txt"};
if (!fin.is_open()) {
cerr << "ifstream open fout.txt error" << endl;
return 2;
}
char c;
while (fin.peek() != EOF) {
c = fin.get();
cout << c << fin.tellg() <<endl;
}
fin.seekg(0, ios::beg);
while (fin.peek() != EOF) {
c = fin.get();
cout << c << fin.tellg() <<endl;
}
fin.close();
return 0;
}
fin.seekg(0, ios::end);
length = fin.tellg();
fin.seekg(0, ios::beg);
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void) {
char cont[5] = {'a','b','c','d','e'};
ofstream fout("flog", ios::binary);
if (!fout.is_open())
return 1;
fout.write(cont, 5);
fout.close();
ofstream bout("flog", ios::binary | ios::app);
if (!bout.is_open())
return 2;
bout.write("12345", 5);
bout.close();
char rout[1]{};
ifstream fin("flog", ios::binary);
if (!fin.is_open())
return 3;
while (fin.peek() != EOF) {
fin.read(rout, 1);
cout << rout[0] << "|";
}
cout << endl;
fin.close();
return 0;
}
二进制文件的读写,流式符号以及getline就不好用了,而要使用write
和read
接口,并用ios::binary
创建fstream对象。
以上测试代码输入如下:
$ g++ -Wall -Wextra fstream.cpp -o fstream
$ ./fstream
a|b|c|d|e|1|2|3|4|5|
文本文件,也可以使用read和write接口!
虽然read接口可以填一个希望读取的大小,但有时真正读取的大小与希望值有差距,此时要用gcount
接口来获取真实读取的大小。
摘取一段代码作为示例:
ifstream fin{ fname, ios::binary };
if (!fin.is_open()) {
cerr << "open output file error" << endl;
}
while (1) {
fin.read(inbuf, INBUF_SIZE);
if (fin.bad()) {
cout << "read file error" << endl;
break;
}
data_size = fin.gcount();
eof = !data_size;
data = inbuf;
while (data_size > 0 || eof) {
ret = av_parser_parse2(parser, dec_context, &pkt->data, &pkt->size,
(const uint8_t*)data, (int)data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (ret < 0) {
fprintf(stderr, "Error while parsing\n");
exit(1);
}
data += ret;
data_size -= ret;
if (pkt->size)
decode(dec_context, frame, pkt);
if (eof)
break;
}
if (eof)
break;
}
fin.close();
正好文章写道这里,也在调试av_parser_parse2接口使用的bug。这段代码读取文件没有错,错在当gcount返回0的时候,还要再调用一次前述的接口,否则最后一帧出不来。
本文链接:https://cs.pynote.net/sf/c/cpp/202209241/
-- EOF --
-- MORE --