(Linux应用一)I/O操作基本知识点
2026/9/18 21:31:52 网站建设 项目流程

标准I/O

文件打开和关闭

文件打开

打开就是占用资源,关闭就是释放资源

打开一个标准I/O流:

FILE *fopen(const char *path,const char *mode);

path:普通文件当前路径不需要加目录,其他要使用完整的路径

成功时返回流指针

出错时返回NULL,所以使用fopen函数时必须要判断

mode参数:

重点(必须掌握):

r 或 rb以只读方式打开文件,文件必须存在
r+或r+b以读写方式打开文件,文件必须存在
w或wb以只写方式打开文件,若文件存在,则清空内容;不存在则创建
w+或w+b以读写方式打开文件,其他同w
a或ab以只写方式打开文件,文件不存在则创建,文件写入的数据被追加到文件末尾
a+或a+b以读写方式打开文件。其他同 a

总结:r是读,r+是读写,文件都必须存在;w和a都是写,w是清空写,a是追加写,有+都是读写

fopen函数的使用方式

FILE *fp; fp = fopen("1.txt","w"); if(fp == NULL) { printf("Open is fail!\n"); } else { printf("Open file success\n"); } return 1;

标准I/O-处理错误信息

extern int errno;

void perror(const char *s);

char *strerror(int errno);

errno 存放错误号,由系统生成

perror先输出字符串s,再输出错误号对应的错误信息,一般加在错误的分支

FILE *fp; fp = fopen("1.txt","w"); if(fp == NULL) { perror("fopen:"); } else { perror("fopen:"); }

strerror根据错误号返回对应错误信息

FILE *fp; fp = fopen("1.txt","w"); if(fp == NULL) { printf("fopen:%s\n",strerror(errno));//errno系统定义好了,在#include <errno.h>中 //strerror在#include <string.h>中 } else { perror("Open success\n"); }

perror与strerror功能一样

文件关闭

int fclose(FILE *stream);

fclose()调用成功返回0,失败返回EOF(-1),并设置errno

流关闭时自动刷新缓冲区中的数据并释放缓冲区

当一个程序正常终止时,所有打开的流都会被关闭

流一旦关闭后就不能执行任何操作

FILE *fp; fp = fopen("1.txt","w"); if(fp == NULL) { printf("fopen:%s\n",strerror(errno)); } else { //只有打开成功,才能关闭成功 printf("Open success\n"); if(fclose(fp) == 0) { printf("close success!\n"); } else { perror("fclose:"); } }

按字符读取

从文件中读取字符

下列函数用来输入一个字符:

#include <stdio.h>

int fgetc(FILE *stream);

int getc(FILE *stream); //宏

int getchar(void);

成功时返回读取的字符,若到文件末尾或出错时返回EOF(-1),

getchar()等同于fgetc(stdin),getc和fgetc区别是一个是宏,一个是函数

上面的语句选择一条用就行

stdin也是FILE*的指针,是系统定义好的,指向的是标准输入(键盘输入)

FILE *fp; int rec; fp = fopen("1.txt","r"); if(fp == NULL) { perror("fopen:"); return 0; } rec = fgetc(fp); printf("Get char = %c\n",rec); rec = fgetc(fp); printf("Get char = %c\n",rec); rec = fgetc(fp); printf("Get char = %c\n",rec); fclose(fp);

打开文件后读取,是从文件开头开始读,读完一个后读写指针会后移

FILE *fp; int rec; fp = fopen("1.txt","r"); if(fp == NULL) { perror("fopen:"); return 0; } rec = getchar(); printf("Getchar() = %c\n",rec); fclose(fp);

调用getchar()会阻塞,等待键盘输入

按字符写入

向文件中写入字符

下列函数用来输出一个字符

#include <stdio.h>

int fputc(int c,FILE *stream);

int putc(int c,FILE *stream);

int putchar(int c);

成功时返回写入的字符,出错时返回EOF

putchar(c)等同于fputc(c,stdout)

上面的语句选择一条用就行

stdout也是FILE*的指针,是系统定义好的,指向的是标准输出,输出到屏幕上

FILE *fp; int rec; fp = fopen("1.txt","r+"); if(fp == NULL) { perror("fopen:"); return 0; } int wrc = 'c'; rec = fputc(wrc,fp); if(rec == EOF) { perror("fputc:"); } fclose(fp);

putchar(c)

int wrc = 'c'; putchar(wrc);

按行读取

#include <stdio.h>

char *gets(char *s);//读取键盘的输入,不推荐使用

char *fgets(char *s,int size,FILE *stream);

成功时返回s,到文件末尾或出错时返回NULL

gets不推荐使用,容易造成缓冲区溢出

遇到'\n'或已输入size-1个字符时返回,总是包含'\0','\0'也占一位

//读取所有行 FILE *fp; fp = fopen("1.txt","r+"); if(fp == NULL) { perror("fopen:"); } char buff[100]; while(fgets(buff,sizeof(buff),fp) != NULL) { printf("buff = %s",buff); } printf("\n"); fclose(fp);

按行写入

#include <stdio.h>

int puts(const char *s);

int fputs(const char *s,FILE *stream);

成功时返回非负函数,出错时返回EOF

puts将缓冲区s中的字符串输出到stdout,并追加'\n'

fputs将缓冲区s中的字符串输出到stream,不追加'\n'

FILE *fp; fp = fopen("1.txt","r+"); if(fp == NULL) { perror("fopen:"); } const char *s = "hello world hello"; int res = fputs(s,fp); if(res == EOF) { perror("fpus:"); } fclose(fp);

二进制文件读取与写入

#include <stdio.h>

size_t fread(void *ptr,size_t size,size_t n,FILE *fp);

ptr为读取的内容放置位置,size为读多大的内容,n为读的个数,fp为文件

一共读取size*n个字节

读文本文件

FILE *fp; fp = fopen("1.txt","r+"); if(fp == NULL) { perror("fopen:"); } char buff[100]; size_t res; res = fread(buff,10,2,fp); if(res == EOF) { perror("fread:"); } printf("buff = %s\n",buff); fclose(fp);

size_t fwrite(const void *ptr,size_t size,size_t n,FILE *fp);

将ptr的内容写到fp中,size是写多大,n是写多少个

读写二进制文件代码

#include <stdio.h> #include <string.h> struct Student{ char name[100]; int age; char sex[8]; }; int main() { struct Student s; FILE *fp; fp = fopen("1.bin","w+"); if(fp == NULL) { perror("fopen:"); } strcpy(s.name,"zhangsan"); s.age = 20; strcpy(s.sex,"male"); //write int res = fwrite(&s,sizeof(s),1,fp); if(res == EOF) { perror("fwrite:"); } fclose(fp); //read fp = fopen("1.bin","r+"); if(fp == NULL) { perror("fopen:"); } struct Student s2; res = fread(&s2,sizeof(s),1,fp); if(res == EOF) { perror("fread:"); return 0; } printf("name:%s age:%d sex:%s\n",s2.name,s2.age,s2.sex); fclose(fp); return 1; }

结构体是二进制文件

成功返回读写的对象个数,出错时返回EOF

既可以读写文本文件,也可以读写二进制文件,效率高

注意事项:文件写完后,文件指针指向文件末尾,如果这时候直接读,会读不出来内容

解决办法:

1、移动文件指针(后面说)

2、关闭文件,重新打开

文件的流的刷新与定位

刷新流

#include <stdio.h>

int fflush(FILE *fp);

成功时返回0,出错时返回EOF

将流缓冲区中的数据写入实际文件

Linux下只能刷新输出缓冲区,输入缓冲区丢弃

FILE *fp; fp = fopen("1.txt","w"); if(fp == NULL) { perror("fopen"); return 0; } fwrite("abcdef",7,1,fp); fflush(fp); while(1) { sleep(1); } return 0;

定位流

#include <stdio.h>

long ftell(FILE *stream);

long fseek(FILE *stream,long offset,int whence);

void rewind(FILE *stream);

ftell()成功时返回流的当前读写位置,出错时返回EOF

fseek()定位一个流,成功时返回0,出错时返回EOF

whence参数:SEEK_SET/SEEK_CUR/SEEK_END

SEEK_CUR:以目前的读写位置往后增加offset个位移量

SEEK_END:将读写位置指向文件尾后再增加offset个位移量

SEEK_SET:从距文件开头offset位移量为新的读写位置

offset参数:偏移量,可正可负

打开a模式,fseek无效

rewind()将流定位到文件开始的位置

读写流时,当前读写位置自动后移

示例一:在文件末尾追加字符't'

FILE* fp = fopen("1.txt","r+");

fseek(fp,0,SEEK_CUR);

fputc('c',fp);

覆盖se两个字母写vvv

FILE *fp; fp = fopen("1.txt","w"); if(fp == NULL) { perror("fopen"); return 0; } fwrite("abcdse",6,1,fp); fseek(fp,-2,SEEK_CUR); fwrite("vvv",3,1,fp); fclose(fp);

从文件开始位置写内容

FILE *fp; fp = fopen("1.txt","w"); if(fp == NULL) { perror("fopen"); return 0; } fwrite("abcdse",6,1,fp); rewind(fp); fwrite("vvv",3,1,fp); fclose(fp);

显示当前文件流的位置

FILE *fp; fp = fopen("1.txt","w"); if(fp == NULL) { perror("fopen"); return 0; } fwrite("abcdse",6,1,fp); rewind(fp); fwrite("vvv",3,1,fp); printf("current fp = %ld\n",ftell(fp)); fclose(fp);

格式化输出和格式化输入

格式化输出/将内容写入文件或字符串

#include <stdio.h>

int printf(const char *fmt,...);

int fprintf(FILE* stream,const char *fmt,...);

int sprintf(char *s,const char *fmt,...);常用

成功时返回输出字符的个数,出错时返回EOF

写入字符串

char buf[100] = {0}; int year = 2025; int month = 12; int day = 15; sprintf(buf,"%d-%d-%d",year,month,day); printf("%s\n",buf);

写入文件

FILE *fp; int year = 2025; int month = 12; int day = 15; fp = fopen("1.txt","r+"); if(fp == NULL) { perror("fopen"); return 0; } fprintf(fp,"%d-%d-%d\n",year,month,day); fclose(fp);

格式化输入/从文件或字符串中读取内容

int fscanf(FILE* stream,const char *fmt,...);

int sscanf(char *s,const char *fmt,...);

从字符串中读取内容

int year = 2025; int month = 12; int day = 15; int syear; int smonth; int sday; char buf[100] = {0}; sprintf(buf,"%d-%d-%d",year,month,day); sscanf(buf,"%d-%d-%d",&syear,&smonth,&sday); printf("%d,%d,%d\n",syear,smonth,sday);

sprintf(buf,"%d-%d-%d",year,month,day);
sscanf(buf,"%d-%d-%d",&syear,&smonth,&sday);常用

其中,"%d-%d-%d"的格式要一致

int year,month,day; FILE *fp; fp = fopen("1.txt","r+"); if(fp == NULL) { perror("fopen"); return 0; } fscanf(fp,"%d-%d-%d",&year,&month,&day); printf("%d-%d-%d\n",year,month,day); fclose(fp);

其中,fscanf()中的''%d-%d-%d"要和文件中的数据格式一致

思考联系:每隔1秒向文件test.txt中写入当前系统时间,格式如下:

1、2014-10-15 15:16:42

2、2024-10-15 15:16:43

该程序无限循环,直到Ctrl+C中断程序

每次执行程序时,系统时间追加到文件末尾,序号递增

time()用来获取系统时间(秒数)

time_t time(time_t *seconds)

localtime()将系统时间转换成本地时间

struct tm *localtime(const time_t *timer);//这个结构体包含所有时间类型

#include <stdio.h> #include <time.h> #include <unistd.h> #include <string.h> int main() { FILE *fp; time_t ctime; int line = 0; char buf[32]; fp = fopen("1.txt","a+"); if(fp == NULL) { perror("fopen"); return 0; } //计算行号 while(fgets(buf,32,fp) != NULL) { if(buf[strlen(buf) - 1] == '\n') //判断一行是否结束 { line ++; } } while(1) { line ++; struct tm* ctimestr; ctime = time(NULL); ctimestr = localtime(&ctime); fprintf(fp,"%d. %04d-%02d-%02d %02d:%02d:%02d\n",line,ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec); printf("%d. %04d-%02d-%02d %02d:%02d:%02d\n",line,ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec); fflush(fp);//刷新,写到磁盘里面 sleep(1); } fclose(fp); return 0; }

文件I/O

文件描述符fd

1、每个打开的文件都对应一个文件描述符

2、文件描述符是一个非负整数,Linux为程序中每个打开的文件分配一个文件描述符

3、文件描述符从0开始分配,依次递增

4、文件IO操作通过文件描述符来完成

5、0为标准输入,1为标准输出,2为错误流

注:文件IO不提供缓冲机制

文件IO的API:

open、close、read、write

文件描述的概念:

0-1023的数字,表示文件。

open函数

#include <fcntl.h>

int open(const char* pathname,int flags,mode_t mode);

int open(const char*pathname,int flags);

pathname:被打开的文件名(可包括路径名)

flags:

O_RDONLY:只读方式打开文件

O_WRONLY:可写方式打开文件

O_RDWR:读写方式打开文件

以上三个参数是互斥的,不能同时选择,下面的参数是可选项

O_APPEND:以添加方式打开文件,所以对文件的写操作都在文件的末尾进行

O_TRUNC:如文件已经存在,那么打开文件时先删除文件中原有数据

O_CREAT:如果文件不存在,就创建一个新文件,并用第三个参数为其设置权限

O_EXCL:如果使用O_CREAT时文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端

mode:被打开文件的存取权限,为8机制表示法

0664

成功时返回文件描述符fd,出错时返回EOF

1、打开文件时使用两个参数

2、创建文件时第三个参数指定新文件的权限(只有在建立新文件时有效)此外真正建文件时的权限会受到umask值影响,实际权限是mode-umaks

3、可以打开设备文件,但是不能创建设备文件

rO_RDONLY
r+O_RDWR
wO_WRONLY|O_CREAT|O_TRUNC,0664
w+O_RDWR|O_CREAT|O_TRUNC,0664
aO_WRONLY|O_CREAT|O_APPEND,0664
a+O_RDWR|O_CREAT|O_APPEND,0664
int fd; if(fd = open("1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666) == EOF) { perror("open"); return 0; } else { printf("success!\n"); }

在man手册的第二章,man 2 open

close函数

#include <unistd.h>

int close(int fd);

1、成功时返回0,出错时返回EOF

2、程序结束时自动关闭所有打开的文件

3、文件关闭时,文件描述符不再代表文件

int fd; int res; if(fd = open("1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666) == EOF) { perror("open"); return 0; } else { printf("success!\n"); } printf("fd = %d\n",fd); res = close(fd);

read函数

#include <unistd.h>

ssize_t read(int fd,void *buf,size_t count);

成功时返回实际读取的字节数,0表示数据已经读完;出错时返回EOF

读到文件末尾时返回0

buf是接收数据的缓冲区,一般是数组

count不应超过buf的大小,是指定的数组大小

write函数

#include <unistd.h>

ssize_t write(int fd,void *buf,size_t count);

成功时返回实际写入的字节数,0表示数据已经读完;出错时返回EOF

buf是发送数据的缓冲区,一般是数组

count不要超过buf的大小,要写入的数据的实际的大小

#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> int main() { char filename[40] = "1.txt"; char buf_w[20] = "Hello World!"; char buf_r[20] = {0}; // 初始化为0 int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU); // 添加 O_CREAT 确保文件存在 if(fd == -1) { perror("open"); exit(1); // 通常用非0表示错误 } // 写入文件 int ret = write(fd, buf_w, strlen(buf_w)); if(ret == -1) { perror("write"); exit(1); } close(fd); // 重新打开读取 fd = open(filename, O_RDWR); if(fd == -1) { perror("open"); exit(1); } // 移动文件指针到开头 lseek(fd, 0, SEEK_SET); // 读取文件内容 ret = read(fd, buf_r, sizeof(buf_r) - 1); // 留一个位置给 '\0' if(ret == -1) { perror("read"); exit(1); } buf_r[ret] = '\0'; // 手动添加字符串结束符 printf("buf_r = %s\n", buf_r); close(fd); return 0; }

注:对于buf的大小,字符串用strlen,二进制用sizeof

lseek函数

lseek函数用来定位文件

#include <unistd.h>

off_t lseek(int fd,off_t offset,intt whence);

成功时返回当前的文件读写位置;出错时返回EOF

参数offset和参数whence同fseek完全一样

whence:

SEEK_CUR:以目前的读写位置往后增加offset个位移量

SEEK_END:将读写位置指向文件尾后再增加offset个位移量

SEEK_SET:从距文件开头offset位移量为新的读写位置

作用:

1、移动文件指针到文件头

lseek(fd,0,SEEK_SET);

2、获取当前文件指针的位置

lseek(fd,0,SEEK_CUR);

3、获取文件长度

lseek(fd,0,SEEK_END);

4、拓展文件的长度,当前文件10b,110b,增加了100个字节

lseek(fd,100,SEEK_END);拓展之后需要写一个数据才管用

文件属性和目录操作

目录的读取

opendir函数

打开目录

#include <dirent.h>

DIR *opendor(const char *name);

DIR *fdopendir(int fd);使用文件描述符,要配合open函数使用

DIR是用来描述一个打开的目录文件的结构体类型

成功时返回目录流指针;出错时返回NULL

读目录

struct dirent* readdir(DIR *dirp);

struct dirent是用来描述目录流中的一个目录项的结构体类型

包含成员char d_name[256]参考帮助文档

成功时返回目录流dirp中的下一目录项

出错时到末尾时返回NULL

待续...

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询