这里可能只会讨论到一些基本的文件读写,有兴趣的读者可以自行参考一些资料
环境
这里为了满足比较多小伙伴的需求,我选用的是win10系统
若Mac和Linux下有各别东西不同,我会稍加说明
先来看看于输入输出流相关的类
简单介绍几个类
他们之间的继承派生关系如图所示
1.istream:用于输入的流类,cin就是该类的对象
2.ostream用于输出的流类,cout是它的对象
**fstream中:既能从文件读取数据,又可以向文件写入数据
**
3.iftream用于从文件读取数据
4.oftream用于向文件写入数据
5.iostream 既能输入,又可以输出
其他有兴趣可自行了解sstream可以看我关于
STL:string类的博客string类
标准流对象
缺省情况下
cerr << "Hello,world" << endl;
clog << "Hello,world" << endl;
和下面一样
cout << “Hello,world” << endl;
cerr和clog的区别在于cerr不使用缓冲区,直接向显示器输出信息;
而输出到clog中的信息先会被存放在缓冲区,缓冲区满或者刷新时才输出到屏幕。
判断输入流结束
int x;
while(cin>>x){
//
}
return 0;
就相当于
istream &operator >>(int & a)
{
//
return *this ;
}
put()函数
cout.put('A').put('\n')// 由此看出,返回对象的引用
1.如果是从文件输入,比如前面有freopen(“some.txt”,”r”,stdin);那么,读到文件尾部,输入流就算结束
后面会介绍freopen函数的
2.如果从键盘输入,则在单独一行输入Ctrl+Z代表输入流结束
<iostream>
1.getline函数
这里有两种形式
first type
istream & getline(char * buf, int bufSize);
从输入流当中读取bufSize-1个字符进去,或者碰到’\n’为止(哪个先到就算哪个)
second type
istream & getline(char * buf, int bufSize,char delim);
从输入流中读取bufSize-1个字符到缓冲区buf,或读到碰到delim字符为止(哪个先到算哪个)。
需要注意的是:
两个函数都会自动在buf中读入数据的结尾添加\0’。,‘\n’或delim都不会被读入buf,但会被从输入流中取走。
但是,如果输入流中‘\n’或delim之前的字符个数大于等于了bufSize个,就导致读入出错,其结果是:虽然本次读入已经完成,但是之后的读入就都会失败了
这里介绍个小技巧判断输入是否结束
if(!cin.getline(...))
2.其他成员函数
bool eof(); 判断输入流是否结束
while(!(cin>>a>>b).eof())
int peek(); 返回下一个字符,但不从流中去掉.
#include
#include
using namespace std;
int main()
{
string word;
char c;
int n;
cout << "Please enter a word or a number: ";
c = cin.peek();
if(isdigit(c))//拿回n
{
cin >> n;
cout << "You have entered a number: " << n << endl;
}
else
{
cin >> word;
cout << "You have entered a word: " << word << endl;
}
return 0;
}
get()
用法1:读取字符串
#include
using namespace std;
main ()
{
char ch,t;
ch=cin.get(); //或者cin.get(ch);
cin>>t;//get()读取完后会往后移,而peek()不会
cout<<ch<<endl;
cout<<t<<endl;
}
1 2->1 2
用法2:读取字符串
#include
using namespace std;
main ()
{
char a[20];
cin.get(a,20);
cout<<a<<endl;
}
istream & putback(char c); 将字符ch放回输入流
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
char c;
int n;
char str[256];
cout << "Enter a number or a word: ";
c = cin.get();
if ( (c >= '0') && (c <= '9') )
{
cout << c << " --- the middle output" << endl;
cin.putback (c);//放回去不用重新从键盘当中读入了
cin>> n;
cout<< "You have entered number " << n << endl;
}
else
{
cout << c << " --- the middle output" << endl;
cin.putback (c);
cin>> str;
cout<< " You have entered word " << str << endl;
}
return 0;
}
Enter a number or a word:
input:
5
output:
5 — the middle output
You have entered number 5
istream & ignore( int nCount = 1, int delim = EOF );
从流中删掉最多nCount个字符,遇到EOF时结束。
ignore()
if(cin.fail())
{
cout << "Invalid" << endl;
cin.clear();//fail后就无法继续读入了,直到看见clear()
cin.ignore(80,'\n'); //忽略这一行后面的数据后在在下面的语句当中读入,用sync()清空也行
}
others:
int p;//有效位数
double v;//值
cin >> p >> v;
cout.unsetf(ios::fixed);
cout << setprecision(p) << showpoint << v << endl;//保留有效位数(showpoint还可以完成补零)
关于函数这一部分,我的建议是不用背,不会或者忘了查就行
关于函数这一个部分可以看
C++几个重要的文件读写函数
代码演示
#include
using namespace std;
int main()
{
int x;
char buf[100];
cin>>x; ////12+空格
cin.getline(buf,90);//abcd
cout<<buf<<endl;
return 0;
}
看完这个程序你可以停一停,想一想它输出的会是什么东西呢
答案是:无输出!!!因为一开始就把\n读了进来
这里为了节省空间,主要以代码演示为主,解释在代码当中
win10系统下演示
#include
using namespace std;
int main() {
int x,y;
cin >> x >> y;
freopen("test.txt","w",stdout); //将标准输出重定向到 test.txt文件
if( y == 0 ) //除数为0则在终端屏幕上输出错误信息,txt文件里面没有
cerr << "error." << endl;
else
cout << x /y ; //输出结果到test.txt
return 0;
}
#include
using namespace std;
int main() {
double f;
int n;
freopen("test.txt","r",stdin); //cin被改为从test.txt中读取数据
cin >> f >> n;
cout << f << "," <<n << endl;
return 0;
}
读取txt的数据,输出在屏幕上
输出会以,号分隔
• 整数流的基数:流操纵算子dec,oct,hex,setbase(10进制,8,16,2)
• 浮点数的精度(precision,setprecision)
• 设置域宽(setw,width)
• 用户自定义的流操纵算子
使用流操纵算子需要 #include
这部分的内容可能都比较熟悉了,这里我就一笔带过吧
#include
#include
using namespace std;
int main()
{
double x = 1234567.89;
cout << setiosflags(ios::fixed) <<
setprecision(6) << x << endl <<
resetiosflags(ios::fixed) << x ;//取消以小数点位置固定的方式输出
}
//output:输出:
//1234567.890000
//1.23457e+006
Practice:
输入
输入共5行
第一行三个整数,表示一个日期的年,月,日(年份在1900到2100之间)
第二行一个整数h,表示需要输出一个菱形的高度(保证是奇数)
高度为5的菱形的样例如下
Copy
(请不要使用二重循环进行打印,注意后面没有空格)
第三行输入一个整数和一个实数,表示需要输出的实数的有效数字位数和实数的值
第四行两个实数,使用科学计数法表示,E使用大写,如1.35E+2
第五行输入一个正整数,判断该整数是否为质数
输出
第一行输出一个日期,格式为XXXX-XX-XX,如2000-01-01
接下来的h行,输出一个指定高度的菱形,注意后面没有空格
接下来的一行,输出一个实数,表示按指定有效数字位数表示的实数(不需要科学记数法)
接下来一行输出一个实数,用科学记数法表示,E大写,表示输入的两个实数的和,保留6位有效数字
接下来的一行输出一个字符串"true"或"false"表示输入的数是否为质数(使用函数来判断质数)
输入样例
2000 1 1
5
5 1.5
1.5E+02 1.5E+03
11
Copy
输出样例
2000-01-01
*
1.5000
1.65000E+03
true
#include
#include
#include
#include
#include
#include
using namespace std;
bool isPrime(int n)
{
if (n == 1)
return false;
if (n == 2)
return true;
if (n % 2 == 0 && n != 2)
return false;
int limit = sqrt(n);
for (int i = 3; i <= limit; i += 2)
{
if (n % i == 0)
return false;
}
return true;
}
int main()
{
string inf;
getline(cin, inf);
istringstream is(inf);
int y;
string mon, date;
is >> y >> mon >> date;
if (mon.size() == 1)
mon = "0" + mon;
if (date.size() == 1)
date = "0" + date;
ostringstream os;
os << y << "-" << mon << "-" << date << endl;
cout << os.str() ;
int h;
cin >> h;
for (int i = h / 2; i >= -h / 2; --i)
{
if (abs(i) == h / 2)
cout << setw(h / 2 + 1) << "*" << endl;
else
{
cout << setw(abs(i)+1) << "*" << setw(h-2*abs(i)-1) << "*" << endl;
}
}
int a; double b;
cin >> a >> b;
cout.unsetf(ios::fixed);
cout << setprecision(a) <<showpoint<< b << endl;
double c, d;
cin >> uppercase >> scientific >> c >> d;
cout << resetiosflags(ios::fixed) << scientific << setprecision(5) << uppercase << c + d << endl;
int t;
cin >> t;
if (isPrime(t)) cout << "true" << endl;
else cout << "false" << endl;
return 0;
}
Practice:
输入
输入数据包含若干行
每行会输入4个数据,表示两个点(向量)的坐标:x1,y1,x2,y2x_1,y_1,x_2,y_2x1,y1,x2,y2
如果输入有效,则4个坐标值应当全为整数,而且范围在[-1000,1000]内
请使用cin直接对Point对象输入
输出
输出数据包含若干行,对应每行输入
如果输入对应行出现了无效数据,则输出"Invaild input."(不带引号)
如果输入有效,则输出一个(a,b)的格式,表示两个向量相加后的结果,使用cout直接输出对象
输入样例
1 2 3 4
1 2 a b
0 0 0 1
a b c d
Copy
输出样例
(4,6)
Invaild input.
(0,1)
Invaild input.
#include
#include
using namespace std;
class Point
{
int x, y;
public:
Point()
{
}
Point(int xx, int yy) :x(xx), y(yy)
{
}
friend istream& operator>>(istream& is, Point& n);
Point operator+(Point& n);
friend ostream& operator<<(ostream& os, const Point& n);
};
Point Point::operator+(Point& n)
{
return Point(n.x + x, n.y + y);
}
istream& operator>>(istream& is, Point& n)
{
is >> n.x >> n.y;
if(!cin.fail())
{
if (n.x < -1000 || n.x>1000 || n.y < -1000 || n.y>1000)
is.setstate(ios::failbit);
}
return is;
}
ostream& operator<<(ostream& os, const Point& n)
{
cout << "(" << n.x << "," << n.y << ")" << endl;
return os;
}
int main()
{
Point a, b;
while (!(cin>>a>>b).eof())
{
if(cin.fail())
{
cout << "Invaild input." << endl;
cin.clear();
cin.ignore(80,'\n');
}
else
cout<<a+b;
}
return 0;
}
• 设置域宽(setw,width)
两者功能相同,一个是成员函数,另一个是流操作算子,调用方式不同:
cin >> setw(4); 或者 cin.width(5);
cout << setw(4); 或者 cout.width(5);
example:
#include
using namespace std;
int main() {
int w = 4;
char string[10];
cin.width(5);
while(cin >> string){
cout.width(w++);
cout << string << endl;
cin.width(5);
}
return 0;
}
输入:
1234567890
输出:
1234
5678
90
注意的是:宽度设置有效性是一次性的,在每次读入和输出之前都要设置宽度
那么这一个部分最后就来一个代码示例吧
#include
#include
using namespace std;
int main() {
int n = 141;
//1) 分别以十六进制、十进制、八进制先后输出 n
cout << "1) " << hex << n << " " << dec << n << " " << oct << n << endl;
double x = 1234567.89,y = 12.34567;
//2) 保留5位有效数字
cout << "2) " << setprecision(5) << x << " " << y << " " << endl;
//3) 保留小数点后面5位
cout << "3) " << fixed << setprecision(5) << x << " " << y << endl ;
//4) 科学计数法输出,且保留小数点后面5位
cout << "4) " << scientific << setprecision(5) <<x << " " << y << endl ;
//5) 非负数要显示正号,输出宽度为12字符,宽度不足则用'*'填补
cout << "5) " << showpos << fixed << setw(12) << setfill('*') << 12.1 << endl;
//6) 非负数不显示正号,输出宽度为12字符,宽度不足则右边用填充字符填充
cout << "6) " << noshowpos << setw(12) << left << 12.1 << endl;
//7) 输出宽度为12字符,宽度不足则左边用填充字符填充
cout << "7) " << setw(12) << right << 12.1 << endl;
//8) 宽度不足时,负号和数值分列左右,中间用填充字符填充
cout << "8) " << setw(12) << internal << -12.1 << endl;
cout << "9) " << 12.1 << endl;
return 0;
}
ostream &tab(ostream &output){
return output << '\t';
}
cout << “aa” << tab << “bb” << endl;
输出:aa bb
很明显,我们对<<进行了重载
形如
ostream & operator<<( ostream & ( * p ) ( ostream & ) ) ;
该函数内部会调用p所指向的函数,*且以 this 作为参数
但是我觉得这个可能有点点多余了
头文件#include
回忆一下开头所讲的
ofstream //文件写操作 内存写入存储设备
ifstream //文件读操作,存储设备读区到内存中
fstream //读写操作,对打开的文件可进行读写操作
ofstream outFile(“clients.dat”, ios::out|ios::binary); //创建文件
这里详细说明一下子
ios::in | 为输入(读)而打开文件,文件不存在就报错 |
---|---|
ios::out | 为输入(读)而打开文件,文件不存在就新建 |
ios::app | 所有输出附加在文件末尾,文件不存在就新建 |
ios::binary | 二进制方式 |
ios::trunc | 如果文件已存在则先删除该文件(单独使用时候与out一样) |
ios::ate | 打开存在文件,通过读写指针放在尾部。不存在就报错 |
ate只适用于ifstream对象
trunc只适用于ofstream对象
5.这些方式是能够进行组合使用的,以“或”运算(“|”)的方式:例如
直接创建打开
ofstream outFile("Gary.dat",ios::out);
Linux系统下代码演示
#include
#include
#include
#include
using namespace std;
int main()
{
ofstream outFile("Gary.dat",ios::out);
if(!outFile)
{
cerr<<"File can not be opened!"<<endl;
}
char inf[30];
while(cin>>inf)
outFile<<inf<<endl;
return 0;
}
也可以先创建ofstream对象,再用 open函数打开
ofstream fout;
fout.open("test.out",ios::out|ios::binary);
判断打开是否成功:
if(!fout){
cout << “File open error!”<<endl; }
orfout.is_open()//打开成功返回1,失败0
open函数的原型
public member function
void open ( const char * filename,
ios_base::openmode mode = ios_base::in | ios_base::out );
void open(const wchar_t *_Filename,
ios_base::openmode mode= ios_base::in | ios_base::out,
int prot = ios_base::_Openprot);
参数: filename 操作文件名 mode 打开文件的方式 prot 打开文件的属性(这个几乎不会用到)
这里讲一下关于路径的问题
绝对路径: “c:\tmp\mydir\some.txt” 由头写到尾
相对路径:
“\tmp\mydir\some.txt”
当前盘符的根目录下的tmp\dir\some.txt
“tmp\mydir\some.txt”
当前文件夹的tmp子文件夹里面的……
“…\tmp\mydir\some.txt”
当前文件夹的父文件夹下面的tmp子文件夹里面的……
“…\…\tmp\mydir\some.txt”
当前文件夹的父文件夹的父文件夹下面的tmp子文件夹里面的……
这里为了节省空间,主要为代码演示
#include
#include
#include
using namespace std;
int main()
{
ofstream fout("a1.out",ios::app); //以添加方式打开
long location = fout.tellp(); //取得写指针的位置
location = 10;
fout.seekp(location); // 将写指针移动到第10个字节处
fout.seekp(location,ios::beg); //从头数location
fout.seekp(location,ios::cur); //从当前位置数location
fout.seekp(location,ios::end); //从尾部数location
//location 可以为负值
ifstream fin("a1.in",ios::ate);
//打开文件,定位文件指针到文件尾
long location = fin.tellg(); //取得读指针的位置
location = 10L;
fin.seekg(location); // 将读指针移动到第10个字节处
fin.seekg(location,ios::beg); //从头数location
fin.seekg(location,ios::cur); //从当前位置数location
fin.seekg(location,ios::end); //从尾部数location
return 0;
}
inFile.clear(); / / reset eof for next input
inFile. seekg(0); / / reposition to beginning of file
**关于这一部分更加详细的内容,可以移步到C++文件读写**看后面部分
成员函数close(),它负责将缓存中的数据排放出来并关闭文件。这个函数一旦被调用,原先的流对象就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程所访问了。为防止流对象被销毁时还联系着打开的文件,析构函数将会自动调用关闭函数close。
显示关闭文件
ifstream fin(“test.dat”,ios::in);
fin.close();
ofstream fout(“test.dat”,ios::out);
fout.close();
example:写一个程序,将文件 in.txt 里面的整数排序后,输出到out.txt
#include
#include
#include
#include
using namespace std;
int main() {
vector<int> v;
ifstream srcFile("in.txt",ios::in);
ofstream destFile("out.txt",ios::out);
int x;
while( srcFile >> x )
v.push_back(x);
sort(v.begin(),v.end());
for( int i = 0;i < v.size();i ++ )
destFile << v[i] << " ";
destFile.close();
srcFile.close();
return 0;
}
有兴趣的话可以浏览这一篇小博客和它的续集C++文件读写操作(一)将字母表写入TXT文本文件
二进制写文件:
ofstream 和 fstream的成员函数:
istream& write (const char* s, long n);
将内存地址s处的n个字节内容,写入到文件中写指针指向的位置,然后将文件写指针向后移动n字节(以ios::out方式打开文件时,文件写指针开始指向文件开头, 以ios::app方式打开文件时,文件写指针开始指向文件尾部 )
先来说一下二进制文件和文本文件的区别
Linux,Unix下的换行符号:‘\n’ (ASCII码: 0x0a)
Windows 下的换行符号:‘\r\n’ (ASCII码: 0x0d0a) endl 就是 ‘\n’
Mac 下的换行符号: ‘\r’ (ASCII码:0x0d)
导致 Linux, Mac 文本文件在Windows 记事本中打开时不换行
导致的后果是:
Unix/Linux下打开文件,用不用 ios::binary 没区别
Windows下打开文件,如果不用 ios::binary,则:
读取文件时,所有的 ‘\r\n’会被当做一个字符’\n’处理,即少读了一个字符’\r’。
写入文件时,写入单独的’\n’时,系统自动在前面加一个’\r’,即多写了一个’\r’
在文件中写入和读取一个整数
#include
#include
using namespace std;
int main() {
ofstream fout("some.dat", ios::out | ios::binary);
int x=120;
fout.write( (const char *)(&x), sizeof(int) );//write函数,地址写入缓冲区
fout.close();
ifstream fin("some.dat",ios::in | ios::binary);
int y;
fin.read((char * ) & y,sizeof(int));//read函数,一直读到结尾
fin.close();//一定要关闭文件
cout << y <<endl;
return 0;
}
二进制文件读写
从键盘输入几个学生的姓名的成绩,并以二进制文件形式保存
#include
#include
using namespace std;
struct Student {
char name[20];
int score;
};
int main() {
Student s;
ofstream OutFile( "c:\\tmp\\students.dat",ios::out|ios::binary);
while( cin >> s.name >> s.score )
OutFile.write( (char * ) & s, sizeof( s) );
OutFile.close();
return 0;
}
输入:
Tom 60
Jack 80
Jane 40
^Z+回车
则形成的 students.dat 为 72字节,用 记事本打开,呈现:
Tom 烫烫烫烫烫烫烫烫< Jack 烫烫烫烫烫烫烫蘌 Jane 烫烫烫烫烫烫烫?
二进制文件读写
将 students.dat 文件的内容读出并显示
#include
#include
using namespace std;
struct Student {
char name[20];
int score;
};
int main() {
Student s;
ifstream inFile("students.dat",ios::in | ios::binary );
if(!inFile) {
cout << "error" <<endl;
return 0;
}
while( inFile.read( (char* ) & s, sizeof(s) ) ) {
int readedBytes = inFile.gcount(); //看刚才读了多少字节
cout << s.name << " " << s.score << endl; }
inFile.close();
return 0;
}
输出:
Tom 60
Jack 80
Jane 40
二进制文件读写
将 students.dat 文件的Jane的名字改成Mike
#include
#include
using namespace std;
struct Student {
char name[20];
int score;
};
int main()
{
Student s;
fstream iofile( "c:\\tmp\\students.dat",ios::in|ios::out|ios::binary);
if( !iofile) {
cout << "error" ;
return 0;
}
iofile.seekp( 2 * sizeof(s),ios::beg); //定位写指针到第三个记录
iofile.write("Mike",strlen("Mike")+1);
iofile.seekg(0,ios::beg); //定位读指针到开头
while( iofile.read( (char* ) & s, sizeof(s)) )
cout << s.name << " " << s.score << endl;
iofile.close();
return 0;
}
**/用法示例:mycopy src.dat dest.dat 即将 src.dat 拷贝到 dest.dat 如果 dest.dat 原来就有,则原来的文件会被覆盖 /
#include
#include
using namespace std;
int main(int argc, char * argv[])
{
if( argc != 3 ) {
cout << "File name missing!" << endl;
return 0;
}
ifstream inFile(argv[1],ios::binary|ios::in); //打开文件用于读
if( ! inFile ) {
cout << "Source file open error." << endl;
return 0;
}
ofstream outFile(argv[2],ios::binary|ios::out); //打开文件用于写
if( !outFile) {
cout << "New file open error." << endl;
inFile.close(); //打开的文件一定要关闭
return 0;
}
char c;
while( inFile.get(c)) //每次读取一个字符
outFile.put(c); //每次写入一个字符
outFile.close();
inFile.close();
return 0;
}
看完这个之后,有兴趣的小伙伴可以再来看看C++文件读写
学会程序和算法,走遍天下都不怕