c++-I/O与文件操作
I/O
继承关系
/*
ios
/ \
istream ostream
/ \ / \
ifstream iostream ofstream
|
fstream
*/
istream // 从键盘接收输入的数据
ostream // 将数据输出到屏幕上
ifstream // 读取文件中的数据
ofstream // 想文件读取数据
iostream // 输入输出
fstream // 文件读取写入
cin/cout
相当于数据流
cin
istream
类对象,cin
对于输入的内存时根据数据类型进行判断的
cout
ostream
类对象,<<
表示把字符串发送给cout
,表示插入运算符,即把字符串插入到输出流当中
属于内置对象
注意:编译器会根据上下文,判断被重载的运算符的含义
#include <iostream>
int x;
float y;
cin>>x;
// 连续接收数据
cin>>x>>y;
// endl
cout<<x<<endl; // 等价于\n
cout<<x<<"\n";
cin
cin 输入
cin
与scanf
scanf
字符串类型,有两种定义方式,对应在内存中有两种 读取与写入权限
在读取时,要求字符串有写入权限,因此只能用char s[]="string"
这种形式
scanf
在读取字符串的时候,在输入时,以空格当做输入结束标志
实际上,scanf 遇到空白字符就停止读取,\t \n 空格
char s[30];
scanf("%s",s) // 这里不需要取地址符&
cin
char*
和char []
都可以用cin赋值,同样遇到空白字符就停止读取,\t \n 空格
cin返回的数据不包括\n等空白符
char buf[20];
char *buf2;
cin>>buf;
cout<<buf<<endl;
cin>>buf2;
cout<<buf2<<endl;
cin读取
cin基本读取形式下,正常读取返回true;遇到结束标注返回false – 没看完,返回值 看 运算符重载一章
标准输入输出设备下:ctrl+D 表示输入结束(\n回车不是)
重定向文件输入下:EOF文件末尾表示输入结束
/* 例子1 */
#include <iostream>
using namespace std;
int main()
{
int n;
int maxN=0;
// 输入没有结束,cin就返回true
while (cin>>n)
{
if(maxN<n)
{
maxN=n;
}
}
cout<<maxN<<endl;
return 0;
}
/* 例子2 文件重定向 */
/*
在这种情况下,test.txt 文件中并不需要包含 Ctrl+Z,只要有用空格或回车隔开的若干个正整数即可
cin 读到文件末尾时,cin>>n就会返回 false,从而导致程序结束
*/
#include <iostream>
using namespace std;
int main()
{
int n;
int maxN=0;
// 输入没有结束,cin就返回true
freopen("test.txt","r",stdin);
while (cin>>n)
{
if(maxN<n)
{
maxN=n;
}
}
cout<<maxN<<endl;
return 0;
}
cin控制输入
cin.get()
从输入流中读入一个字符,返回该字符的ASCII
如果读到输入的末尾,则返回EOF
不会跳过空格、制表符等特殊字符
/*
c为int类型,而不是char类型,在输入流中碰到ASCII等于0xff的字符时,cin.get()返回0xff,0xff赋值给c,此时如果c是char类型的,那么其值就是-1(因为符号位1代表负数),即等于EOF
而在c为int类型的情况下,将0xff赋值给c,c的值是255(因为符号位为0,是正数),而非-1,除非读到输入末尾,c的值都不可能是-1
*/
int main()
{
int c;
while ((c=cin.get())!=EOF)
{
cout.put(c);
}
}
cin.getline()
/*
从输入流读取bufsize-1个字符到缓冲区buf,直到遇到\n为止,函数会自动在buf中读入数据的结尾添加\0
函数不会读入\n
*/
istream & getline(char* buf,int bufsize)
/*
从输入流读到delim字符为止,\n或delim都不会被读入buf,但是会被从输入流中取走
函数会自动在buf中读入数据的结尾添加\0
函数不会读入\n
*/
istream & getline(char* buf,int bufsize,char delim)
// 上面两个函数,如果输入流中\n或delim之前的字符个数达到或超过bufsize,就会导致读入出错,结果就是,本次读入完成但是之后的输入都会失败
getline 与 >>符号 的区别
>>
遇到空格和制表符就会停止
getline遇到\n停止
#include <iostream>
using namespace std;
const int MAX_LINE_LEN=100; // 一行最多多少个字符
int main()
{
char buf[MAX_LINE_LEN+1];
/*
cin重定向
*/
freopen("test.txt","r",stdin);
// 函数会自动在buf中读入数据的结尾添加\0
while (cin.getline(buf,MAX_LINE_LEN+1))
{
cout<<buf<<endl;
}
return 0;
}
cin.clear
清楚cin内部的错误标记,使之恢复正常
cin.ignore
用于跳过输入中的无用部分,提取有用部分
/*
跳过输入流中的n个字符,或者跳过delim及其之前的所有字符
*/
cin.ignore(int n=1,int delim=EOF);
int main()
{
int n;
cin.ignore(5,'A'); // 跳过输入中的5个字符,或输入中'A'及其假面的字符,其余内容被当做整数输入n中
cin>>n;
cout<<n;
return 0;
}
cin.peek
– 有一个例子没看完P442
此函数返回输入流中的下一个字符,但是并不将该字符从输入流中取走
该函数不会跳过输入流中的空格、回车符
在输入流已经结束的情况下,返回EOF
cout
连续输出
具体见 ‘面向对象 - 友元函数 - 例子4’
cout<<a<<b;
// 会被变异成
(cout<<a)<<b; // 其中(cout<<a) 会返回一个ostream对象
ostream & operator<<(ostream &os , clsType &obj)
{
os<<obj.val;
return os;
}
cout控制输出
cout 输出的默认是以10进制显示
cout<<hex; // 以十六进制显示
cout<<data<<endl;
// cout<<oct // 以八进制显示
cout.put()
(想输出流缓冲区)输出单个字符(可以是字符或字符的ASCII)
ostream &put(char c)
cout.put(71);
cout.put(71+2);
cout.put('a');
cout.put(ch);
cout.write()
(想输出流缓冲区)输出字符串
ostream &write(cosnt char* s,streamsize n)
s为要输出的字符串或字符数组,n表示要输出的前n个字符串,n 可小不可大,且不能包含结尾\0
const char* str="helloworld";
cout.write(str,4); // n 可小不可大,且不能包含结尾\0
cout.tellp()
put() 和 write() 都是先放到输出流缓冲区,待缓冲区刷新才会输出到指定位置
在缓冲区内的对象,仍然可以修改
tellp() 获取当前输出流缓冲区中最后一个字符所在的位置
返回0:输出流缓冲区没有任何数据
返回-1: 操作失败
从1开始
cout.seekp()
用于指定心下一个进入输出缓冲区的字符所在的位置
实现缓冲区数据的覆盖与跳转
从0开始
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
using namespace std;
int main()
{
ofstream outfile;
outfile.open("test.txt");
const char* str="hello world";
for (int i=0;i<strlen(str);i++)
{
outfile.put(str[i]);
}
cout<<outfile.tellp()<<endl;
outfile.seekp(6);
cout<<outfile.tellp()<<endl;
const char* str2="qt";
outfile.write(str2,2);
outfile.close();
return 0;
}
endl
endl
是一个特殊的c++符号
将endl
插入到输出流中,将导致屏幕光标移动到下一行的开头
cout<<endl;
endl
与\n
cout<<"some str\n";
// 等价于
cout<<"some str" <<endl;
//等价于
cout<<"some str";
cout<<endl;
cerr/clog
用法和cin\cout一样
cerr
用来输出警告和错误信息
clog
用来输出程序执行过程中的日志信息(一般普通用户看不到日志)
cout/cerr/clog
cout可以重定向,将数据输出到指定文件中
cerr/clog,不支持重定向,只能将数据输出到屏幕上
cout/clog都没有缓冲区(缓冲区:输出数据时,先将数据放到缓冲区,等缓冲区满或者手动换行,才会将数据全部显示到屏幕上)
cerr没有缓冲区,直接想数据输出到屏幕上
输入输出重定向
freopen
#include <cstdio>
可以用于scanf printf等重定向
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
int main()
{
string name,language;
// 从文件到数据
freopen("ini.txt","r",stdin);
cin>>name>>language; // 从ini输出到变量
cout<<name<<" "<<language<<endl;
// 从数据到文件
freopen("out.txt","w",stdout);
cout<<name<<"\n"<<language; // string name 不包括\0 更不包括\n
return 0;
}
rdbuf – 还没看
控制台输出 – 实验有问题 没有\n
cmd>: file.exe <infile >outfile
输入输出错误处理 – 有一个例子没看P448
流状态:输入输出的错误情况
一旦输入输出流发生错误,对应的标志位(流状态)就会发生改变
badbit
<-bad()
发生严重错误
致命错误,流不能继续使用
eofbit
<-eof()
达到输入末尾或文件末尾failbit
<-fail()
发生错误
I/O 操作失败,非法数据的使用
goodbit
<-good()
操作成功没有发生任何错误
int main()
{
int i=0;
cin>>i;
if (!cin)
{
if (cin.bad())
{
// 发生严重故障
}
else if (cin.eof())
{
// 读取结束
}
else if (cin.fail())
{
// 遇到一些意外情况
cin.clear(); // 清除、恢复流状态
}
}
return 0;
}
文件操作
继承关系
/*
ios
/ \
istream ostream
/ \ / \
ifstream iostream ofstream
|
fstream
*/
ifstream // 读取文件中的数据
ofstream // 想文件读取数据
fstream // 文件读取写入
open()
is_open()
close()
swap()
>>
gcount()
get()
getline()
ignore()
peek()
putback()
<<
put()
write()
tellp()
seekp()
flush()
good()
eof()
文本与二进制
只是编辑器是否能够自动进行编码转换的区别
打开方式
ios::in // 读取,且文件必须存在
ios::out // 写入,文件不存在则新建;存在则清空再写入
ios::app // 追加写,文件则存在则创建
ios::binary // 以而二进制文件方式打开,默认是文本方式打开
ios::in | ios::binary // 二进制读去
ios::out | ios::binary // 二进制写入
使用open(成员)函数打开
例子
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
const char* name="zhangsan";
// fstream file; // 定义文件流对象
// file.open("test2.txt",ios::out); // 将文件与文件流关联
// 等价于
ofstream file2;
file2.open("test3.txt",ios::out);
if (file2.is_open()) // 还可以直接为文件名 if (file2)
{
cout<<"DONE file is open!"<<endl;
file2.write(name,8); // n 可小不可大,且不能包含结尾\0
file2.close();
}
else
{
cout<<"ERROR file not open!"<<endl;
}
return 0;
}
使用流类构造函数打开
例子
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
const char* name="zhangsan";
ofstream file3("test4.txt",ios::out);
if (file3.is_open()) // 还可以直接为文件名 if (file2)
{
cout<<"DONE file is open!"<<endl;
file3.write(name,8); // n 可小不可大,且不能包含结尾\0
file3.close();
}
else
{
cout<<"ERROR file not open!"<<endl;
}
return 0;
}
文件缓冲区
使用flush 及时刷新输出流缓冲区
const char* name="zhangsan";
ofstream file3("test4.txt",ios::out);
if (file3.is_open()) // 还可以直接为文件名 if (file2)
{
cout<<"DONE file is open!"<<endl;
file3.write(name,8); // n 可小不可大,且不能包含结尾\0
file3.flush(); // 刷新输出流缓冲区
file3.close();
}
else
{
cout<<"ERROR file not open!"<<endl;
}
文本读写
>>
和<<
读写文件
fstream 或 ifstream + ios::in 使用>> 运算符实现文件读取
fstream 或 ofstream + ios::out 使用<< 运算符实现文件写入
例子
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
int x,sum=0;
ifstream srcFile("ini.txt",ios::in); // 以文本模式打开
if (!srcFile.is_open())
{
cout<<"ERROR: failed to open file"<<endl;
return 0;
}
ofstream dstFile("out.txt",ios::out); // 以文本方式打开
if (!dstFile.is_open())
{
srcFile.close();
cout<<"ERROR: faile to open file"<<endl;
return 0;
}
while (srcFile>>x)
{
sum+=x;
dstFile<<x<<" ";
}
cout<<sum<<endl;
dstFile.close();
srcFile.close();
return 0;
}
二进制读写
read() wirte()读写文件
ostream & write(char *buffer,int count)
buffer指定要写入文件的二进制数据的起始位置,count指定写入字节的个数。注意:write()方法会从文件写指针指向的位置将二进制数据写入(文件打开默认写指针指向文件的开头)
istream & read(char *buffer,int count)
buffer指定读取字节的起始位置,count指定读取字节的个数,该方法返回一个调用该方法的对象的引用。注意read()方法从文件读指针指向的位置开始读取若干字节
#include <iostream>
#include <fstream>
using namespace std;
class Student
{
public:
char name[20];
int age;
};
int main()
{
Student stu;
ofstream outFile("students.dat",ios::out | ios::binary);
while (cin>>stu.name>>stu.age)
{
outFile.write((char*)&stu,sizeof(stu)); // ??? 强制转换
}
outFile.close();
Student stu2;
ifstream inFile("students.dat",ios::in | ios::binary);
if (!inFile.is_open())
{
cout<<"ERROR: failed to open file!"<<endl;
return 0;
}
// 因为是按字节读取的
while (inFile.read((char *)&stu2,sizeof(stu2)))
{
cout<<stu2.name<<" "<<stu2.age<<endl;
}
inFile.close();
return 0;
}
get/put
逐个字符读写
ostream & put(char)
int get()
返回读取到的字符的ASCII码,遇到末尾则返回EOF
istream &get(char &c)
将读取到的字符赋值给定义的字符变量
get()
需要注意的一点:操作系统在接收到get()方法的请求后,哪怕只读取一个字符,也会一次性从文件中将很多数据(通常至少是512个字节)读到一块内存空间中间中(文件流输入缓冲区),当读取下一个字符时,就不需要再访问硬盘中的文件,直接从该缓冲区中读取即可
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
char c;
ofstream outFile("out.txt",ios::out|ios::binary);
if (!outFile.is_open())
{
cout<<"ERROR: failed open file"<<endl;
return 0;
}
while (cin>>c)
{
outFile.put(c);
}
outFile.close();
return 0;
}
int main()
{
char c2;
ifstream inFile("out.txt",ios::out | ios::binary);
if (!inFile.is_open())
{
cout<<"ERROR: failed open file"<<endl;
return 0;
}
while ((c2=inFile.get()) && c!=EOF)
{
cout<<c2;
}
inFile.close();
return 0;
}
读写指针和获取指针
- 设置指针
seekg
读指针(ifstream fstream)ostream & seekp(int offset,int mode)
seekp
写指针(ofstream fstream)istream & seekg(int offset,int mode)
mode表示读写指针的设置模式
- ios::beg 从0(文件开头)向后offset个字节,只能是正数
- ios::cur 从当前指针向前(负数)向后(整数)offset个字节
- ios::end 从(文件结尾)向前offset个字节,只能是负数
- 获取指针
tellg
读指针的位置,(ifstream fstream),int tellg()
tellp
写指针的位置,(ofstream fstream),int tellp()
- 获取文件长度
seekg + tellg
#include <iostream>
#include <fstream>
using namespace std;
class Student
{
public:
char name[20];
int age;
};
int main()
{
Student stu;
// 读写打开
fstream ioFile("students.dat",ios::in | ios::out);
if (!ioFile.is_open())
{
cout<<"ERROR: failed to open file!"<<endl;
return 0;
}
ioFile.seekg(0,ios::end); // 读指针到文件末尾
int L=0;
int R=ioFile.tellg()/sizeof(Student)-1;
return 0;
}