再要:
哈喽!这里又是TheBlob 博客。本篇文章是IO编程基础的第一篇,主要讲述标准带缓冲IO编程。其中包括 IO编程概念、标准IO的缓冲、标准IO的输入输出函数以及函数使用的例子。希望能帮助大家!
目录
格式化IO---输入与输出可以控制格式,printf(),scanf()
利用C库中的函数实现文件操作
文件操作的三部曲;
- 打开文件
- 读写文件
- 关闭文件
定义:
文件:一组相关数据的集合
文件名:这个数据集合的名称
Linux中文件分类:
(-)常规文件 ASCII码文件 二进制文件
(d)目录
(c)字符设备
(b)块设备
(p)有名管道
(s)套接口
(l)符号连接
概念
- 不仅在UNIX系统,在很多操作系统上都实现了标准I/O库
- 标准I/O库由ANSI C标准说明
- 标准I/O库处理很多细节,如缓冲分配、以优化长度执行I/O等,这样使用户不必关心如何选择合适的块长度
- 标准I/O在系统调用函数基础上构造的,它便于用户使用
- 标准I/O库及其头文件stdio.h为底层I/O系统调用提供了一个通用的接口。
文件指针
FILE指针:每个被使用的文件都在内存中开辟一个区域,用来存放文件的有关信息,这些信息是保存在一个结构体类型的变量中,该结构体类型是由系统定义的,取名为FILE。
struct _IO_FILE;
typedef struct _IO_FILE FILE;
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
}
标准I/O库的所有操作都是围绕流(stream)来进行的,在标准I/O中,流用FILE *来描述。
流(stream):
定义:所有的I/O操作仅是简单的从程序移进或者移出,这种字节流,就称为流。
分类:文本流/二进制流。
打开和关闭文件
打开文件:
#include<stdio.h> 头文件
FILE *fopen(const char *pathname, const char *mode);
//参数1 ---- 要打开的文件
//参数2 ---- 打开的操作权限:
r //只读,文件必须存在
r+ //读写,文件必须存在
w //只写,文件不存在,则创建,文件存在,则把文件清空
w+ //写读,文件不存在,则创建,文件存在,则把文件清空
a //只写, 文件不存在,则创建,文件存在,则在文件末尾追加
a+ //写读, 文件不存在,则创建,文件存在,则在文件末尾追加,如果是读,则从文件头开始读
//返回值 ---成功:文件指针,失败:NULL
例子:
int main(int argc,char **argv)
{
FILE *fp;
if(argc != 2){
printf("Usage: %s <filename>\n",argv[0]);
exit(1); //结束程序
}
//fp = fopen(argv[1],"r");
//fp = fopen(argv[1],"r+");
//fp = fopen(argv[1],"w");
fp = fopen(argv[1],"a");
if(fp == NULL){
perror("fopen");
exit(1);
}
return 0;
}
关闭文件:
int fclose(FILE *stream);
//参数 ---- 文件指针
//返回值 ----成功:0,失败:-1
例如:
int main(int argc,char **argv)
{
FILE *fp;
if(argc != 2){
printf("Usage: %s <filename>\n",argv[0]);
exit(1); //结束程序
}
//打开文件
fp = fopen(argv[1],"a");
if(fp == NULL){
perror("fopen");
exit(1);
}
//关闭文件
fclose(fp); //刷新缓冲区,释放缓冲区
return 0;
}
标准IO的缓冲
由于CPU的速度比硬盘快,因此为了提高效率。在程序和文件之间创建了一个缓存区,程序可以先将数据写到缓冲区中,然后在一点一点刷新到文件中。
全缓冲-----操作常规文件的时候
全缓冲刷新的条件:
- 缓冲区满
- 关闭文件
- 强制刷新缓冲区
- 程序正常结束
行缓冲-----操作标准输入和标准输出的时候
当运行一个程序,系统会自动默认打开三个设备文件:
文件指针 设备
标准输入 stdin 键盘
标准输出 stdout 屏幕
标准错误输出 stderr 屏幕
输入与输出可以控制格式,例如:printf() 、 scanf()
行缓冲刷新的条件:
- 缓冲区满
- 关闭文件
- 强制刷新缓冲区
- 程序正常结束
- 当行缓存区中遇到换行符(’\n‘)时
int main(int argc, char **argv)
{
while(1){
printf("hello world");
usleep(100000);
}//循环打印满了才显示在屏幕
printf("hello world");
while(1);//这一行一直不显示
return 0;
}
不带缓冲
没有缓冲区刷新的条件,直到往缓冲区写数据,会立即刷新。很多的人机交互界面要求不可全缓冲。标准出错决不会是全缓冲的。
例子:
int main(int argc,char **argv)
{
printf("hello world");//通过标准输出打印,行缓存
fprintf(stderr,"hello world")//通过标准错误打印,无缓冲
}
格式化IO---输入与输出可以控制格式,printf(),scanf()
格式化输出函数
int pritnf(const char *format,.....); //只能向标准输出写数据
int fprintf(FILE *stream,const char *format,......);//向指定的文件中写数据
例子;
int main(int argc,char **argv)
{
FILE *fp;
if(argc!=2){
fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
exit(1);
}
if((fp = fopen(argv[1],"w")) == NULL){
perror("fopen");
exit(1);
}
char buf[100];
while(1){//向文件中写数据
bzero(buf,sizeof(100));
scanf("%s",buf);
fprintf(fp,"%s\n",buf); //将buf输出的结果写到文件中
fflush(fp); //确保数据能够立即写到文件中,可以使用fflush()强制刷新
}
}
int sprintf(char *str, const char *format, ...); //输出结果以字符串形式写到内存中
struct student{
int sno;
char name[20];
float score;
};
int main(int argc,char **argv)
{
int i;
struct student st[3];
char buf[5];
//向内存中写数据
for(i =0; i < 3; i++){//将键盘输入的结构体信息存入 结构体数组中
printf("请输入学生信息:");
scanf("%d%s%f",&st[i].sno,st[i].name,&st[i].score);
sprintf(buf,"%d %s %.2f",st[i].sno,st[i].name,st[i].score); //数组溢出
printf("%s\n",buf);
}//表面看起来没事但是会报警告,运行结束会段错误
return 0;
}
int snprintf(char *str, size_t size, const char *format, ...);
//输出结果以字符串形式写到内存中,同时限制写的字符数,防止溢出
例如: 前面如上只是更该函数为 snprintf
snprintf(buf,5,"%d %s %.2f",st[i].sno,st[i].name,st[i].score); //数组溢出
格式化输入
int scanf(const char *format, ...); //从标准输入(键盘)获取数据
int fscanf(FILE *stream, const char *format, ...); //从指定文件中获取数据
例如:
struct student{
int sno;
char name[20];
float score;
};
int main(int argc,char **argv)
{
FILE * fp;
int i;
struct student st[3];
if(argc != 2){
fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
exit(1);
}
if((fp = fopen(argv[1],"r")) == NULL){
perror("fopen");
exit(1);
}
//从文件中读出数据
for(i =0; i < 3; i++){
printf("请输入学生信息:");
fscanf(fp,"%d%s%f",&st[i].sno,st[i].name,&st[i].score);
printf("%d %s %.2f\n",st[i].sno,st[i].name,st[i].score); //打印文件中的
}
return 0;
}
int sscanf(const char *str, const char *format, ...); //从内存中获取数据
struct student{
int sno;
char name[20];
float score;
};
int main(int argc,char **argv)
{//应用就是将字符串以换形式输出 例如:时间 9:20分是字符串 用函数读取后再以整形打印
struct student st;
char buf[] = "1001 jack 87.56";
sscanf(buf,"%d%s%f",&st.sno,st.name,&st.score);
printf("%d %s %.2f\n",st.sno,st.name,st.score); //将buf输出的结果写到文件中
char buf[100];
int h,m;
printf("请输入时间:");
scanf("%s",buf);
sscanf(buf,"%d:%d",&h,&m);
printf("%s为%d小时%d分钟\n",buf,h,m);
int a;
strcpy(buf,"123");
sscanf(buf,"%d",&a);
printf("a = %d\n",a);
return 0;
}
非格式化IO-----输入与输出没有格式,以字节流方式读写
一次一个一个字的IO输入
1、输入
int fgetc(FILE *stream); //(函数)从指定的文件中获取一次字符
int getc(FILE *stream); //(宏)从指定的文件中获取一次字符
int getchar(void); //默认从标准输入(键盘)获取一次字符
例如:
int main(int argc,char **argv)
{
int a;
char b;
scanf("%d",&a);
printf("a = %d\n",a);
while(getchar() != '\n'); //清空输入缓冲区
scanf("%c",&b);
printf("b = %c\n",b);
return 0;
}
2、输出
int fputc(int c, FILE *stream); //(函数)向指定的文件中写一个字符
int putc(int c, FILE *stream); //(宏)向指定的文件中写一个字符
int putchar(int c); //向标准输出写一个字符
例如:
int main(int argc,char **argv)
{
FILE * rfp,*wfp;
char ch;
if(argc != 3){
fprintf(stderr,"Usage: %s <src_filename> <target_filename>\n",argv[0]);
exit(1);
}
if((rfp = fopen(argv[1],"r")) == NULL){
perror("fopen");
exit(1);
}
if((wfp = fopen(argv[2],"w")) == NULL){
perror("fopen");
exit(1);
}
//从一个文件中读数据,写到另一个文件中
while((ch = fgetc(rfp)) != EOF){
//printf("%c",ch);
putchar(ch);
fputc(ch,wfp);
}
fclose(rfp);
fclose(wfp);
return 0;
}
直接IO-----字节流
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
//参数1 ----- 内存地址
//参数2 ----- 对象大小
//参数3 ----- 对象个数
//参数4 ----- 文件指针
例如:
struct student{
int sno;
char name[20];
float score;
};
int main(int argc,char **argv)
{
FILE * fp;
int i;
struct student st[3];
if(argc != 2){
fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
exit(1);
}
if((fp = fopen(argv[1],"w")) == NULL){
perror("fopen");
exit(1);
}
//向结构体中写数据
for(i =0; i < 3; i++){
printf("请输入学生信息:");
scanf("%d%s%f",&st[i].sno,st[i].name,&st[i].score);
}
//直接iO,向文件中写三个结构体
fwrite(st,sizeof(struct student),3,fp);
return 0;
}
例如:
struct student{
int sno;
char name[20];
float score;
};
int main(int argc,char **argv)
{ //前面都一样,将文件的可执行改为 r
//直接iO,向文件中读三个结构体
fread(st,sizeof(struct student),3,fp);
//向结构体中写数据
printf("学生信息:\n");
for(i =0; i < 3; i++){
printf("%d %s %.2f\n",st[i].sno,st[i].name,st[i].score);
}
return 0;
}
例如:
int main(int argc,char **argv)
{
//和前面将一个文件写去另一个文件一样
//从一个文件中读数据,写到另一个文件中
while(!feof(rfp) && !ferror(rfp)){
bzero(buf,sizeof(buf));
fread(buf,sizeof(char),sizeof(buf)-1,rfp);
printf("%s",buf);
fwrite(buf,sizeof(char),strlen(buf),wfp);
}
}
定位文件指针
获取文件指针的位移量
long ftell(FILE *stream);
//参数 ----- 文件指针
//返回值 ----成功:文件指针的位移量,失败:-1
例如:
int main(int argc,char **argv)
{
FILE * fp;
char ch;
int i;
if(argc != 2){
fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
exit(1);
}
if((fp = fopen(argv[1],"r")) == NULL){
perror("fopen");
exit(1);
}
for(i = 0; i < 7; i++){
ch = fgetc(fp);
printf("%c",ch);
}
printf("\n%ld\n",ftell(fp));//
return 0;
}