文件基础
概念: 一组相关数据的集合
文件类型 | 头字母 |
常规文件 | r |
目录文件 | d |
字符设备文件 | c |
块设备文件 | b |
管道文件 | p |
套接字文件 | s |
符号链接文件 | l |
不同操作系统支持的文件类型不相同
标准I/O
标准I/O 由ANSI C标准定义
主流操作系统上都实现了C库
标准I/O通过缓冲机制减少系统调用,实现更高的效率
标准I/O - 文本流和二进制流
windows下:
二进制流 : 换行符 <> “\n”
文本流 : 换行符 <> “\r\n”
Linux下:
换行符 <> “\n”
标准I/O - 流的缓冲类型
全缓冲:当流的缓冲区无数据或者无空间的时候才执行实际的I/O操作
行缓冲:当在输入和输出中遇到"\n"执行实际的I/O操作,当流和一个终端关联时,典型的行缓冲
无缓冲:数据直接写入文件,流不进行缓冲
标准I/O - stdin,stdout,stderr
标准I/O预定义3个流,程序运行时候自动打开
标准输入流 | 0 | STDIN_FILENO | stdin |
标准输出流 | 1 | STDOUT_FILENO | stdout |
标准错误流 | 2 | STDERR_FILENO | stderr |
标准I/O - 打开流
下列函数可以用于打开一个标准的I/O流
FILE *fopen(const char *path, const char *mode);
成功时候返回流指针;错误时候返回NULL。
mode参数:
方式 | 介绍 |
“r"或"rb” | 以只读方式打开文件,文件必须存在。 |
"r+“或"r+b” | 以读写方式打开文件,文件必须存在。 |
“w"或"wb” | 以只写方式打开文件,若文件存在则文件长度清零,若文件不存在则创建。 |
"w+“或"w+b” | 以读写方式打开文件,其他同"w"。 |
“a"或"ab” | 以只写方式打开文件,若文件不存在则创建;向文件写入的数据会被追加到文件末尾。 |
"a+“或"a+b” | 以读写方式打开文件,其他同"a"。 |
当给定"b"参数时,表示以二进制方式打开文件,但是Linux下忽略该参数。
示例:
#include <stdio.h> int main() { FILE *fp; if((fp=fopen("test.txt","r+")) == NULL) { printf("fopen error\n"); return -1; } return 0; }
新建文件权限
fopen() 创建的文件访问权限是0666(rw-rw-rw-);
Linux系统中 umask 设定会影响文件的访问权限,其规则为(0666 & ~umask);
处理错误信息
extern int errno; void perror(const char *s); char *strerror(int errno);
1.errno 存放错误号
2.perror先输出字符串s,再输出错误号对应的错误信息;
3.strerror根据错误号返回对应的错误信息
#include <stdio.h> int main() { FILE *fp; if((fp=fopen("test.txt","r+")) == NULL) { perror("fopen"); return -1; } return 0; }
#include <stdio.h> #include <string.h> #include <errno.h> int main() { FILE *fp; if((fp=fopen("test.txt","r+")) == NULL) { printf("fopen %s\n", strerror(errno)); return -1; } return 0; }
标准I/O - 关闭流
int fclose(FILE *stream)
1.fclose()调用成功返回0,返回失败返回EOF 设置errno
2.流关闭时候自动刷新缓冲中的数据并且释放缓冲区
3.当一个程序正常终止时,所有打开的流都会被关闭
4.流一旦关闭后就不能执行操作
标准I/O - 读写流
流支持不同的读写方式:
读写一个字符: fgetc()/fputc() 一次读/写一个字符。
读写一行: fgets()/fputs() 一次读/写一行。
读写若干个对象: fread()/fwrite() 每次读/写若干个对象,而每个对象具有相同的长度。
fgetc
int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void);
#include <stdio.h> int main() { int ch; ch = fgetc(stdin); printf("%c\n", ch); return 0; }
#include <stdio.h> int main() { FILE *fp; int ch,count = 0; if((fp=fopen("test.txt","r+")) == NULL) { perror("fopen"); return -1; } while((ch= fgetc(fp))!=EOF) { count ++; } printf("total, %d bytes\n", count); return 0; }
fputc
int fputc(int c, FILE *stream); int putc(int c, FILE *stream); int puttchar(int c);
#include <stdio.h> int main() { fputc('a',stdout); putchar('\n'); return 0; }
#include <stdio.h> int main() { FILE *fp; int ch; if((fp=fopen("test.txt","w+"))== NULL) { perror("fopen"); return -1; } for(ch='a';ch <='z';ch++) { fputc(ch, fp); } return 0; }
fgets
char *gets(char *s); // 不建议使用 char *fgets(char *s, int size, FILE *stream);
#include <stdio.h> #define N 6 int main() { char buf[N]; fgets(buf, N, stdin); printf("buf : %s\n", buf); return 0; }
fputs
int puts(const char *s); int fputs(const char *s, FILE *stream);
#include <stdio.h> int main() { puts("hello world!"); return 0; }
#include <stdio.h> int main() { FILE *fp; char buf[] = "hello world"; if((fp = fopen("test.txt","a"))== NULL) { perror("fopen"); return -1; } fputs(buf, fp); return 0; }
标准I/O - 按照对象读写
下列函数用来从流中读取若干对象:
size_t fread(void *ptr, size_t size, size_t n, FILE *fp); size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp)
成功返回读写的对象个数,出错返回EOF。
既可以读写文本文件,又可以读写数据文件。
fread/fwrite
#include <stdio.h> int main() { FILE *fp; fp = fopen("test.txt","r"); int s[10]; if(fread(s, sizeof(int), 10, fp) < 0) { perror("fread"); return -1; } return 0; }
#include <stdio.h> struct student { int no; char name[8]; float score; }; int main() { FILE *fp; fp = fopen("test.txt","rw"); student s[] = { {1, "zhangs", 97}, {2, " wang", 98}}; fwrite(s, sizeof(student), 2, fp); student s1[2]; fread(s1, sizeof (student), 2, fp); printf("%s\n", s1[0].name); printf("%s\n", s1[1].name); return 0; }
标准I/O - 刷新流
1.缓冲区满了之后会自动刷新
2.当流关闭的时候,如果缓冲还有数据会刷新
3.fflush刷新
int fflush(FILE *fp);
成功时候返回0,出错时候返回EOF。
将流缓冲区中的数据写入实际文件。
LINUX下只能刷新输出缓冲区。
#include <stdio.h> int main() { FILE *fp; fp = fopen("test.txt","w"); if(fp == NULL) { perror("fopen"); return -1; } fputc('a', fp); fflush(fp); while(1) { } return 0; }
标准I/O - 定位流
long ftell(FILE *stream); long fseek(FILE *stream, long offset, int whence); void rewind(FILE *stream);
1.ftell 成功时候返回当前读写位置,错误时候返回EOF
2.fseek()定位一个流,成功时候返回0, 出错时候返回EOF
whence参数: SEEK_SET开始位置、SEEK_CUR当前位置、SEEK_END结束位置
3.rewind()将流定位到起始位置
当对流进行读写的时候,读写位置会自动后移
#include <stdio.h> int main() { FILE *fp; fp = fopen("test.txt","r+"); if(fp == NULL) { perror("fopen"); return -1; } fseek(fp, 0, SEEK_END); fputc('t', fp); return 0; }
#include <stdio.h> int main() { FILE *fp; fp = fopen("test.txt","r+"); if(fp == NULL) { perror("fopen"); return -1; } fseek(fp, 0, SEEK_END); printf("length is %d\n", ftell(fp)); return 0; }
标准I/O - 其他函数
int ferror(FILE *stream); int feof(FILE *steram);
ferror()返回1表示流出错;否则返回0
feof()返回1表示文件已经到末尾;否则返回0
标准I/O - 格式化输出
int printf(const char *fmt, ...); int fprintf(FILE *stream, const char *fmt, ...); int sprintf(char *s, const char *fmt, ...);
#include <stdio.h> int main() { int year = 2023, month = 1, date = 9; char buf[64]; FILE *fp; fp = fopen("test.txt","a+"); if(fp == NULL) { perror("fopen"); return -1; } fprintf(fp,"%d-%d-%d\n", year, month, date); sprintf(buf,"%d-%d-%d\n", year, month, date); printf("%s", buf); return 0; }
文件I/O
标准I/O:ANSIC,带缓冲,流FILE
文件I/O:POSIX,无缓冲,文件描述符号
#include
文件I/O是posix定义的一组函数
不提供缓冲机制,每次读写操作都引起系统调用
核心概念是文件描述符
访问各种类型文件
LINUX下标准I/O是基于文件I/O实现的
文件I/O - 文件描述符
每打开一个文件都会对应一个文件描述符
文件描述符是一个非负整数,linux为程序中每个打开的文件分配一个文件描述符
文件描述符从0开始依次递增
文件IO操作通过文件描述符来完成
0 1 2表述标准输入,标准输出和标准错误
文件I/O - open
int open(const char *pathname, int oflag, ...);
成功时候返回文件描述符;出错时返回EOF
打开文件时使用两个参数
创建文件时第三个参数置顶新文件权限
只能打开设备文件
原型 | int open(const char *pathname, int oflag, mode_t mode); |
|
参数 | pathname | 被打开的文件名称(可以包括路径名称) |
flags | O_RDONLY: O_WRONLY:O_RDWR 三个互斥 | |
O_CREAT:如果文件不存在则创建一个新的文件,使用第三个参数为其设置权限 | ||
O_EXCL:如果使用O_CREAT时候文件存在则可返回错误消息,这一参数可测试文件是否存在 | ||
O_NOCTTY:使用本参数时候如果文件为终端,那么终端不可以作为调用open()系统调用的那个进程控制终端。 | ||
O_TRUNC:如果文件存在,则文件清空 | ||
O_APPEND:以追加方式打开文件 | ||
mode | 被打开文件权限,为8进制表示法 |
#include <fcntl.h> #include <stdio.h> int main() { int fd; if((fd = open("1.txt", O_WRONLY | O_CREAT |O_TRUNC, 0666)) < 0) { perror("open"); return -1; } return 0; }
#include <fcntl.h> #include <stdio.h> int main() { int fd; if((fd = open("1.txt", O_RDWR | O_CREAT |O_EXCL, 0666)) < 0) { perror("open"); return -1; } return 0; }
文件I/O - close
int close(int fd);
成功时候返回0;出错返回EOF;
程序结束时候自动关闭所有打开的文件。
文件I/O - read/write
size_t read(int fd, void *buf, size_t count); size_t write(int fd, void *buf, size_t count);
#include <fcntl.h> #include <stdio.h> #include <unistd.h> int main() { int fd; char buf[64]; if((fd = open("2.txt", O_RDWR)) < 0) { close(fd); perror("open"); return -1; } size_t n = read(fd, buf, 64); printf("%s\n", buf); printf("%d\n", n); close(fd); return 0; }
#include <fcntl.h> #include <stdio.h> #include <unistd.h> int main() { int fd; char buf[64]; if((fd = open("2.txt", O_RDWR | O_APPEND | O_TRUNC)) < 0) { close(fd); perror("open"); return -1; } while(fgets(buf, 20, stdin) != NULL) { if(strcmp(buf, "quit\n") == 0) break; write(fd, buf, strlen(buf)); } return 0; }
文件I/O - lseek
long lseek(int fd, long offset, int whence);
用法与fseek一致。
实现文件复制:
#include <fcntl.h> #include <stdio.h> #include <unistd.h> int main() { int fds, fdt, n; char buf[64]; if((fds = open("2.txt", O_RDONLY)) == -1) { perror("open1"); return -1; } if((fdt = open("1.txt", O_WRONLY | O_CREAT | O_TRUNC)) == -1) { perror("open2"); return -1; } while ((n = read(fds, buf, 64)) > 0) { write(fdt, buf, n); } return 0; }
访问目录
访问目录 - opendir closedir
struct direct *readdir(DIR *dirp);
struct direct是用来描述目录流中一个目录项的结构体类型
包含成员char d_name[256]
成功时候返回目录流dirp中的下一个目录项
出错或到末尾时候返回NULL
int closedir(DIR *dirp);
打印一个目录下的所有文件名称:
#include <fcntl.h> #include <stdio.h> #include <dirent.h> int main() { DIR *dirp; struct dirent *dp; if((dirp = opendir("./")) == NULL) { perror("opendir"); return -1; } while((dp = readdir(dirp)) != NULL) { printf("%s\n" , dp->d_name); } closedir(dirp); return 0; }
修改文件访问权限 - chmod/fchmod
int chmod(const char *path, mode_t mode); int fchmod(int fd, mode_t mode);
成功时候返回0,出错时候返回EOF
root和文件所有者能修改访问权限
示例: chmod(“test.txt”, 0666);
获取文件属性 - stat/lstat/fstat
int stat(const char *path, struct stat *buf); int lstat(const char *path, struct stat *buf); int fstat(int fd, struct stat *buf);
struct stat:
mode_t st_mode; 类型和访问权限
uid_t st_uid; 所有者id
uid_t st_gid; 用户组id
off_t st_size; 文件大小
time_t st_mtime; 最后修改时间
**st_mode:
#include <fcntl.h> #include <unistd.h> #include <dirent.h> #include <stdio.h> #include <time.h> #include <sys/stat.h> #include <sys/types.h> int main() { struct stat buf; struct tm *dt; int n; if(stat("test.txt", &buf) < 0) { perror("stat "); return -1; } switch (buf.st_mode &S_IFMT) { case S_IFREG: printf("-"); break; } for( n = 8; n >=0; n--) { if(buf.st_mode &(1<<n)) { switch (n%3) { case 2: printf("r"); break; case 1: printf("w"); break; case 0: printf("x"); break; } } else { printf("-"); } } printf( " %ld", buf.st_size); dt = localtime(&buf.st_mtime); printf( " %d-%02d-%02d", dt->tm_year+1900, dt->tm_mon+1, dt->tm_mday); return 0; }