所有操作的本质都是要下发命名到硬件,在Linux操作系统中提供了linux内核来驱动操作硬件、为了方便用户操作相关硬件功能,所以提供了一些函数接口,用于操作Linux内核,进而由内核来操作硬件。这些接口称之为系统调用。
在Linux内核中,大多数的代码也是系统调用。在应用层调的一些函数接口,有些也是在函数接口内在调用内核中的系统调用函数。
文件io:称之为系统调用
标准io:称之为库函数
库函数的本质是系统调用,系统调用函数只要执行一次,就需要对Linux内核访问一次,这样虽然效率较高,但是占用的资源比较大。
库函数提供了一种方式,如果需要执行多次系统调用,在执行之前,会在内存中开辟一块空间,称之为缓冲区,缓冲区的目的就是将多次执行的系统调用的数据先保存起来,然后只需要执行一次系统调用即可,这样节省空间开销。库函数的目的就是为了减少系统调用的次数。
不论是系统调用还是库函数,最终目的都是操作物理内存。
文件io是系统调用,没有缓冲区,例如:open、read、write、lseek等。
标准io是库函数,有缓冲区,例如:fopen、fread、fwrite、ftell、fseek、fgets、fputs等。
标准io通过文件指针(FILE *)来操作文件
文件io通过文件描述符(int)对文件进行操作
当程序运行时或者创建了一个新的进程时,系统都会自动分配三个文件描述符
标准io 文件io 标准输入 stdin 0 标准输出 stdout 1 标准出错输出 stderr 2
下面对一些常用的系统调用函数简单介绍。
1、open()#include2、close()#include #include int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); 功能:打开或者创建一个文件,返回一个文件描述符 参数: pathname:文件名,可以添加路径,默认为当前路径 flags:访问权限标志位 O_RDonLY 只读 O_WRonLY 只写 O_RDWR 可读可写 O_CREAT 如果文件不存在则创建,如果会用此标志位,必须有第三个参数 O_TRUNC 如果文件存在则清空 O_APPEND 如果文件存在则追加 O_EXCL 常与O_CREAT一起使用,表示如果文件存在则报错 mode:文件创建的权限,一般传八进制数即可,例如0664 返回值: 成功:文件描述符 失败:-1
#include3、perror()int close(int fd); 功能:关闭一个文件描述符,如果关闭文件描述符, 则无法再通过这个文件描述符对文件进行操作 参数: fd:指定的文件描述符 返回值: 成功:0 失败:-1
#include4、errnovoid perror(const char *s); 功能:当函数调用失败后可以输出错误信息 参数: s:错误信息的提示信息 返回值: 无
errno变量在errno.h头文件中定义, 是一个全局变量,可以在任意位置使用,用于保存函数调用后的错误码
例如:
#include5、read()#include #include #include #include #include int main(int argc, const char *argv[]) { int fd; //使用open函数创建或者打开一个文件 //if((fd = open("file.txt", O_RDONLY)) == -1) //if((fd = open("file.txt", O_RDonLY | O_CREAT | O_TRUNC, 0664)) == -1) //if((fd = open("file.txt", O_RDonLY | O_CREAT | O_APPEND, 0664)) == -1) if((fd = open("file.txt", O_RDonLY | O_CREAT | O_EXCL, 0664)) == -1) { //errno:用于函数执行失败后保存其失败的错误码,需要头文件,是一个全局变量 //printf("errno = %dn", errno); //perror:当函数调用失败时,会输出错误信息 perror("fail to open"); return -1; } printf("fd = %dn", fd); //关闭文件描述符 close(fd); return 0; }
#includessize_t read(int fd, void *buf, size_t count); 功能:从文件中读取数据 参数: fd:指定的文件描述符 buf:保存读取到的数据 count:最大一次读取多少个字节 返回值: 成功:实际读取的字节数 失败:-1 注意:如果读取到文件末尾,返回0
例如:
#include6、write()#include #include #include #include #define N 6 int main(int argc, char const *argv[]) { //从终端读取数据 //从终端读取数据时,会把n也读取到 //如果输入的数据大于设置读取的最大个数,会读取设置的字节数 #if 0 char buf[N] = ""; if(read(0, buf, sizeof(buf)) == -1) { perror("fail to read"); return -1; } printf("buf = %s", buf); #endif //从文件中读取数据 int fd; if((fd = open("file.txt", O_RDONLY)) == -1) { perror("fail to open"); return -1; } //注意:使用read函数读取文件内容是,要以返回值为标准表示实际读取的个数 ssize_t bytes; char buf[40] = ""; if((bytes = read(fd, buf, sizeof(buf))) == -1) { perror("fail to read"); return -1; } printf("buf = %sn", buf); printf("bytes = %ldn", bytes); char buf1[40] = ""; if((bytes = read(fd, buf1, sizeof(buf1))) == -1) { perror("fail to read"); return -1; } printf("buf1 = %sn", buf1); printf("bytes = %ldn", bytes); return 0; }
#includessize_t write(int fd, const void *buf, size_t count); 功能:向文件写入数据 参数: fd:指定的文件描述符 buf:要写入的数据 count:要写入的数据的长度 返回值: 成功:实际写入的字节数 失败:-1
例如:
#include7、lseek()#include #include #include #include int main(int argc, char const *argv[]) { //向终端写入数据 #if 0 if(write(1, "hello worldn", 13) == -1) { perror("fail to write"); return -1; } #endif //向文件中写入数据 int fd; if((fd = open("file.txt", O_WRonLY | O_CREAT | O_TRUNC, 0664)) == -1) { perror("fail to open"); return -1; } if(write(fd, "hello world", 12) == -1) { perror("fail to write"); return -1; } close(fd); return 0; }
#include#include off_t lseek(int fd, off_t offset, int whence); 功能:设置或者获取文件的偏移量 参数: fd:指定的文件描述符 offset:偏移的位置,可正可负 whence:相对位置 SEEK_SET 文件起始位置 SEEK_CUR 文件当前位置 SEEK_END 文件末尾位置(最后一个字符的下一个位置) 返回值: 成功:返回文件的当前偏移量 失败:-1
例如
#include8、remove()#include #include #include #include #include int main(int argc, char const *argv[]) { int fd; //if((fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0664)) == -1) //注意:如果文件io中的open函数使用O_APPEND或者标准io的fopen函数使用a或者a+ // 这样做无法修改写操作的偏移量,只能修改读操作的偏移量 // 写操作只能在末尾处去写,而读操作可以指定偏移量 if((fd = open(argv[1], O_RDWR | O_CREAT | O_APPEND, 0664)) == -1) { perror("fail to open"); return -1; } char buf[128] = "hello world, nihao beijing, nihao chengdun"; if(write(fd, buf, strlen(buf)) == -1) { perror("fail to write"); return -1; } //不管是read还是write,执行完毕后都会修改文件的偏移量 //获取当前文件的偏移量 printf("offset = %ldn", lseek(fd, 0, SEEK_CUR)); //修改文件的偏移量 lseek(fd, 0, SEEK_SET); printf("offset = %ldn", lseek(fd, 0, SEEK_CUR)); char text[128] = ""; if(read(fd, text, sizeof(text)) == -1) { perror("fail to read"); return -1; } printf("text = %sn", text); lseek(fd, 10, SEEK_SET); write(fd, "666666", 6); return 0; }
#include4、库函数 1、fopen()int remove(const char *pathname); 功能:删除文件或者目录 参数: pathname:文件名或者目录名 返回值: 成功:0 失败:-1
#includeFILE * fopen(const char * path,const char * mode); 参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。 返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno 中 r 为 读操作 打开 文本文件. 流 被定位于 文件 的 开始. r+ 为读写操作打开文本文件. 流 被定位于 文件 的 开始. w 为写操作创建文本文件, 或者 将 已经 存在的 文件长度 截断为 零. 流 被定位于 文件 的 开始. w+ 为读写操作打开文件. 如果 文件 不存在, 就 创建 它, 否则 将它 截断. 流 被定位于 文件 的 开始. a 为追加操作 (在文件尾写) 打开 文件. 如果 文件 不存在, 就 创建 它. 流 被定位于 文件 的 末尾. a+ 为追加操作 (在文件尾写) 打开 文件. 如果 文件 不存在, 就 创建 它. 读文件的初始位置 是 文件 的 开始, 但是 写文件 总是 被追加到 文件 的 末尾.
与open对比
#includesize_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 功能:从文件中读取数据 参数: ptr:保存读取的数据 size:每次读取的字节数 nmemb:一共读取的次数 stream:文件指针 返回值: 成功:实际读取的次数(对象数、块数) 失败:0 如果文件内容读取完毕,返回0
例如:将文件中信息读到结构体中
#include3、fwrite()typedef struct{ int a; int b; char c; char d[32]; }MSG; int main(int argc, char const *argv[]) { //使用fread从文件中读取数据 FILE *fp; if((fp = fopen("file.txt", "r")) == NULL) { perror("fail to fopen"); return -1; } // char buf[32] = {0}; // fread(buf, 2, 5, fp); // printf("buf = %sn", buf); // int num; // fread(&num, 4, 1, fp); // printf("num = %dn", num); // int b[4] = {0}; // fread(b, 4, 4, fp); // printf("%d %d %d %dn", b[0], b[1], b[2], b[3]); MSG msg; fread(&msg, sizeof(msg), 1, fp); printf("%d - %d - %c - %sn", msg.a, msg.b, msg.c, msg.d); return 0; }
#includesize_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 功能:向文件中写入数据 参数: ptr:要写入的数据 size:一次写入的字节数 nmemb:一共写入的次数 stream:文件指针 返回值: 成功:实际写入的次数 失败:0
例如:
#include4、fgets()、fputs()typedef struct{ int a; int b; char c; char d[32]; }MSG; int main(int argc, char const *argv[]) { //使用fwrite向文件写入数据 FILE *fp; if((fp = fopen("file.txt", "w")) == NULL) { perror("fail to fopen"); return -1; } //写入字符串 //char buf[] = "1234567890"; //fwrite(buf, 2, 5, fp); //写入整数 //int a = 97868; //fwrite(&a, 4, 1, fp); //写入数组 //int a[4] = {100, 200, 300, 400}; //fwrite(a, 4, 4, fp); //写入结构体 MSG msg = {666, 888, 'w', "zhangsan"}; fwrite(&msg, sizeof(msg), 1, fp); return 0; }
fgets()
#includechar *fgets(char *s, int size, FILE *stream); 功能:从文件中读取内容 参数: s:保存读取到的内容 size:每次读取的最大个数 stream:文件指针 返回值: 成功:读取的数据的首地址 失败:NULL 如果文件内容读取完毕,也返回NULL
fputs()
#include5、fseek()int fputs(const char *s, FILE *stream); 功能:向文件写入数据 参数: s:要写入的内容 stream:文件指针 返回值: 成功:写入文件内容的字节数 失败:EOF
#include6、rewind()int fseek(FILE *stream, long offset, int whence); 功能:设置文件位置指针的偏移量 参数: stream:文件指针 offset:偏移量 可正可负也可为0 whence:相对位置 SEEK_SET 文件起始位置 SEEK_CUR 文件当前位置 SEEK_END 文件末尾位置(最后一个字符后面一个位置) 返回值: 成功:0 失败:-1
#include7、ftell()void rewind(FILE *stream); 功能:将文件位置定位到起始位置 参数: stream:文件指针 返回值:无 rewind(fp) <==> fseek(fp, 0, SEEK_SET);
#includelong ftell(FILE *stream); 功能:获取当前文件的偏移量 参数: stream:文件指针 返回值: 获取当前文件的偏移量
例如:
#includeint main(int argc, char const *argv[]) { //写操作的文件偏移量 #if 0 FILE *fp; if((fp = fopen("file.txt", "w")) == NULL) { perror("fail to fopen"); return -1; } fputs("abcdefghijklmnopq", fp); //使用ftell获取文件偏移量 printf("offset = %ldn", ftell(fp)); //使用fseek修改文件偏移量 //rewind(fp); //fseek(fp, 0, SEEK_SET); fseek(fp, -5, SEEK_END); fputs("12345", fp); #endif #if 0 FILE *fp; if((fp = fopen("file.txt", "r")) == NULL) { perror("fail to fopen"); return -1; } char buf[32] = {0}; fgets(buf, 6, fp); printf("buf = %sn", buf); printf("offset = %ldn", ftell(fp)); fseek(fp, 3, SEEK_SET); printf("offset = %ldn", ftell(fp)); fgets(buf, 6, fp); printf("buf = %sn", buf); #endif FILE *fp; //如果fopen打开或者创建一个文件时使用的是的a或者a+权限 //则写操作的偏移量无法改变,只能在文件的末尾位置 //但是读操作没有任何影响 if((fp = fopen("file.txt", "a+")) == NULL) { perror("fail to fopen"); return -1; } // char buf[32] = {0}; // fgets(buf, 6, fp); // printf("buf = %sn", buf); // printf("offset = %ldn", ftell(fp)); // fseek(fp, 3, SEEK_SET); // printf("offset = %ldn", ftell(fp)); // fgets(buf, 6, fp); // printf("buf = %sn", buf); fputs("888888888", fp); printf("offset = %ldn", ftell(fp)); fseek(fp, 3, SEEK_SET); printf("offset = %ldn", ftell(fp)); fputs("666666", fp); return 0; }



