在C程序中:
与程序代码外的数据(文件)打交道,我们使用到流(stream
)这个概念,实现进程的虚拟内存与文件之间的数据交换。
——文件流:C标准库提供了FILE
(之所以命名为FILE
,因为linux
将所有机制都视为文件) ,FILE
对象是一个包含了管理流所需的所有信息的结构,包括缓冲区信息、各种标记(如文件结束标记和错误标记)以及用于实际I/O的文 件描述符等。
——输入流,输入流:数据从文件传送到内存的叫输入流,数据从内存传送到文件的叫输出流。
——打开文件:FILE
对象通过调用fopen
函数创建的。如: FILE *fp, fp=fopen("filename","r")
, 表示以只读的方式建立与filename
相关的文件流;filename
为当前目录下的相对路径名,r
代表可读(打开文件的模式)。
1:对一些有规范格式文件的读取,可使用标准库stdio.h
下的fscanf
函数,
函数原型为:int fscanf(FILE * stream, const char * format, [argument...])
如读取文件data.txt
(数据格式相对规范):
代码实现读取:
//test.c
//文件读取
#include<stdio.h>
int main()
{
//1:创建文件流,文件指针名=fopen(文件名,使用文件方式)打开失败则返回NULL;
FILE *fp=fopen("./data.txt","r"); //以data.txt文件为例
//2:检测文件是否打开成功;
if(!fp){
printf("打开失败!\n");
return -1; //返回异常
}
//3:
int num; //用来储存一个整型数据
char name[10], place[10]; //用来储存两个字符串数据
//抽象理解:
//理解文件位置含义:其表示已打开文件当前可读写字符的位置,其表示为一个到文件头的整数;
//fscanf在读取数据时可以这样理解:文件被打开后,它就成了一个无序字节流(水流),其会通过一个管道,流向被读取的一端;
//了解fscanf后知道,其遇到空格字符(空格,制表符),换行符,就会停止,这里的停止我们可以理解为:
//停下来为文件中两个不相关的数据块做一个分隔的操作,刚好适应了我们一般将空字符(包括换行符)作为两个数据之间的分隔的行为;
//我们只需理解管道的两端
//流进管道的一端的位置,就是文件的位置,表示已被读到的位置。
//流出管道一端,就是进程用来读取数据的一端,其可以对管道中已经做区分的数据进行读取。
//4:读取:
fscanf(fp,"%d%s%s",&num, name,place); //fscanf对流的格式化读取。
//注1:fscanf(fp,"%da%s%s",&num,name,place); 可以实现对数据:1a小刚 河南;的准确读取,表示两个数据之间以a为界。
//注2:因为流是指针的性质,所以函数是将各数据块的首地址交给对应参数,所以num需进行&取地址操作,
//注3:因为name,place本身已表达地址,所以不用改变;
fscanf(fp,"\n");
//\n为控制字符,此时文件的位置到了第二行的开始;
//接着进行操作:fscanf(fp,"%d%s%s",&num, name,place);就可以继续读取第二行
//所以我们常常只需利用一个while语句就可以将整个文件读取到一个数据结构(进程)中
/*
while(!feof(fp)) //feof()检测一个文件是否结束,即到达文件尾,若结束,则返回非0值,否则返回0
{
fscanf(fp,"%d%s%s\n",&num, name,place);
}
*/
//检测读取结果
printf("%d %s %s\n",num, name, place);
//关闭流
fclose(fp);
return 0;
}
程序运行结果:
2:从文件中读取整行数据(标准库stdio.h
下的fgets
)
函数原型:char *fgets(char *str, int n, FILE *stream);
其从指定的流 stream
读取一行,并把它存储在str
所指向的字符串内。当读取 (n-1)
个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,遇到空格不停止;
//test3.c
//行读取文件数据
#include<stdio.h>
#define maxlen 30
int main()
{
//创建文件流
FILE *fp=fopen("./data.txt","r");
//2:检测文件是否打开成功;
if(!fp){
printf("打开失败!\n");
return -1; //返回异常
}
char str[maxlen];//缓冲区,用来储存数据
//从文件中读取一行数据,储存到str开始的地址,最大长度为maxlen,然后下次读取从下行开始
//如果该行的数据长于maxlen-1,则只能返回一个不完整的行,并下次调用时从该处开始
fgets(str,maxlen,fp);
//检测结果
printf("%s\n",str);
//关闭流
fclose(fp);
return 0;
}
1:保存与读取往往相关联,保存格式决定了你读取的方式,使用函数fprintf
可以进行指定格式的保存:
函数原型为:int fprintf( FILE *stream, const char *format, [ argument ]...)
假设保存一个人的个人信息到文件中:
/*test2.c */
//数据保存
#include<stdio.h>
int main()
{
//例一个人的信息
int num=1;
char name[10]="小明";
char place[10]="河南";
//建立与文件的流
FILE *fp=fopen("./data.txt","w");
//2:检测文件是否打开成功;
if(!fp){
printf("打开失败!\n");
return -1; //返回异常
}
//将数据格式化输出到指定文件流,int fprintf( FILE *stream, const char *format, [ argument ]...)
//注:此函数,是将format字符串写入到指定输出流中,format包括空格字符,非空格字符,说明符之中的一个或多个。如:fprintf(fp," "); 就是将空格输入到流中。
//可理解为进程借助流将数据打印(fprintf)到了文件中;
//将个人信息,写入指定流中,数据间以一个空格分隔,最后还写入换行符(控制字符)。
fprintf(fp,"%d %s %s\n",num, name,place);
//所以常常只要利用一个while语句就可以将讲一个表(链表,顺序表)按指定行格式写入输出流中
/*
while(!feof(fp)) //feof()检测一个文件是否结束,即到达文件尾,若结束,则返回非0值,否则返回0
{
fprintf(fp,"%d %s %s\n",num, name,place);
}
*/
//关闭流
fclose(fp);
return 0;
}
运行结果:
2:写一个字符串到流中(fputs)
,函数原型:int fputs(const char *str, FILE *stream);
例:
//test4.c
//保存字符串
#include<stdio.h>
int main()
{
//1:创建文件流,文件指针名=fopen(文件名,使用文件方式)打开失败则返回NULL;
FILE *fp=fopen("./data.txt","a"); //以data.txt文件为例,a表示追加
//2:检测文件是否打开成功;
if(!fp){
printf("打开失败!\n");
return -1; //返回异常
}
//string
char string[20]="Facing the world";
//write string to the fstream
fputs(string,fp);
//关闭流
fclose(fp);
return 0;
}
#include <fstream>
ofstream //文件写操作 内存写入存储设备
ifstream //文件读操作,存储设备读区到内存中
fstream //读写操作,对打开的文件可进行读写操作
1.打开文件
在fstream类中,成员函数open()
实现打开文件的操作,从而将数据流和文件进行关联,通过ofstream,ifstream,fstream
对象进行对文件的读写操作
函数: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 打开文件的属性 //基本很少用到,在查看资料时,发现有两种方式
打开文件的方式在ios类(所以流式I/O的基类)中定义,有如下几种方式:
Column 1 | Column 2 |
---|---|
ios::in | 为输入(读)而打开文件 |
ios::out | 为输出(写)而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 所有输出附加在文件末尾 |
ios::trunc | 如果文件已存在则先删除该文件 |
ios::binary | 二进制方式 |
这些方式是能够进行组合使用的,以“或”运算(“|”)
的方式:例如
ofstream out;
out.open("Hello.txt", ios::in|ios::out|ios::binary) //根据自己需要进行适当的选取
打开文件的属性同样在ios类中也有定义:
Column 1 | Column 2 |
---|---|
0 | 普通文件,打开操作 |
1 | 只读文件 |
2 | 隐含文件 |
4 | 系统文件 |
对于文件的属性也可以使用“或”运算和“+”进行组合使用,这里就不做说明了。
很多程序中,可能会碰到ofstream out("Hello.txt"), ifstream in("..."),fstream foi("...")
这样的的使用,并没有显式的去调用open()
函数就进行文件的操作,直接调用了其默认的打开方式,因为在stream
类的构造函数中调用了open()
函数,并拥有同样的构造函数,所以在这里可以直接使用流对象进行文件的操作,默认方式如下:
ofstream out("...", ios::out);
ifstream in("...", ios::in);
fstream foi("...", ios::in|ios::out);
当使用默认方式进行对文件的操作时,你可以使用成员函数is_open()
对文件是否打开进行验证?
参考博客:https://blog.csdn.net/kingstar158/article/details/6859379/
在c++
中我们可以使用操作符<<, >>
来进行流的读写操作,更加的方便和易于理解;
具体参考下列实例:
//c++文件读取
#include<iostream> //输入输出流
#include<fstream> //文件流
//using namespace std; //若使用该声明,则可以不用在使用的每个标准库的成员前加std::
int main()
{
//序号,年龄,年;
int num, age, year;
//姓名,地址
char name[20], place[20];
//c++的文件流,ifstream为输入文件流
std::ifstream fp;
//open为ifstream的成员函数,功能为打开文件,并将它与流关联
fp.open("./data.txt",std::ios::in); //ios::in表示读流的方式,表示打开模式。
//成员函数is_open检查流是否有关联文件,即打开成功与否,成功返回true,失败返回false
if(!fp.is_open()){
std::cout<<"打开文件失败!!\n";
return 1; // 返回异常;
}
//读取数据
fp>>num>>year>>age>>name>>place; //使用操作符>>,将数据传输到对应的变量中
//检测
std::cout<<num<<":"<<name<<",age:"<<age<<",year:"<<year<<",live in:"<<place<<"\n"; //cout相当于printf
//关闭流
fp.close();
return 0;
}
实例:保存一个人的具体信息到文件data.txt
中
代码实现:
//c++数据保存
#include<iostream> //输入输出流
#include<fstream> //文件流
//using namespace std; //若使用该声明,则可以不用在使用的每个标准库的成员前加std::
int main()
{
//序号,年龄,年;
int num=3;
int age=20;
int year=1993;
//姓名,地址
char name[20]="小龙";
char place[20]="广元";
//c++的文件流,ofstream为输出文件流
std::ofstream fp;
//open为ofstream的成员函数,功能为打开文件,并将它与流关联
fp.open("./data.txt",std::ios::app); //ios::app表示每次写入是都追加到流尾,表示打开模式。
//成员函数is_open检查流是否有关联文件,即打开成功与否,成功返回true,失败返回false
if(!fp.is_open()){
std::cout<<"打开文件失败!!\n";
return 1; // 返回异常;
}
//读取数据
fp<<num<<" "<<year<<" "<<age<<" "<<name<<" "<<place<<"\n"; //使用操作符<<,将各数据传输到流所关联的文件中
//关闭流
fp.close();
return 0;
}
参考博客:https://www.cnblogs.com/hongbo-tao/p/12050416.html