目录
fopen库函数用于打开或创建文件,返回相应的文件流。
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
pathname参数用于指定要打开或创建的文件名。
mode参数用于指定文件的打开方式,注意该参数是一个字符串,输入时需要带双引号:
"r":以只读方式打开,文件指针位于文件的开头。
"r+":以读和写的方式打开,文件指针位于文件的开头。
"w":以写的方式打开,不管原文件是否有内容都把原内容清空掉,文件指针位于文件的开头。
"w+":同上,不过当文件不存在时,前面的“w”模式会返回错误,而此处的“w+”则会创建新文件。
"a":以追加内容的方式打开,若文件不存在会创建新文件,文件指针位于文件的末尾。与“w+”的区别是它不会清空原文件的内容而是追加。
"a+":以读和追加的方式打开,其它同上。
fopen的返回值是FILE类型的文件文件流,当它的值不为NULL时表示正常,后续的fread、fwrite等函数可通过文件流访问对应的文件。
?fread库函数用于从文件流中读取数据。
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
stream是使用fopen打开的文件流,fread通过它指定要访问的文件,它从该文件中读取nmemb项数据,每项的大小为size,读取到的数据会被存储在ptr指向的数组中。fread的返回值为成功读取的项数(项的单位为size)。
?fwrite库函数用于把数据写入到文件流
#include <stdio.h>
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
它的操作与fread相反,把ptr数组中的内容写入到stream文件流,写入的项数为nmemb,每项大小为size,返回值为成功写入的项数(项的单位为size)。
fclose库函数用于关闭指定的文件流,关闭时它会把尚未写到文件的内容都写出。因为标准 库会对数据进行缓冲,所以需要使用fclose来确保数据被写出。它的函数原型如下:
#include <stdio.h>
int fclose(FILE* stream);
fflush函数用于把尚未写到文件的内容立即写出。常用于确保前面操作的数据被写 入到磁盘上。fclose函数本身也包含了fflush的操作。fflush的函数原型如下:
#include <stdio.h>
int fflush(FILE *stream);
fseek函数用于设置下一次读写函数操作的位置。它的函数原型如下:
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
其中的offset参数用于指定位置,whence参数则定义了offset的意义,whence的可取值如下:
SEEK_SET:offset是一个绝对位置。
SEEK_END:offset是以文件尾为参考点的相对位置。
SEEK_CUR:offset是以当前位置为参考点的相对位置。
#include <stdio.h>
#include <string.h>
//要写入的字符串
const char buf[] = "filesystem_test:Hello World!\n";
//文件指针
FILE *fp;
char str[100];
int main(void)
{
//打开一个文件,正常返回文件指针,异常返回NULL
fp = fopen("filesystem_test.txt", "w+");
if(NULL == fp){
printf("Fail to Open File\n");
return 0;
}
//将buf的内容写入文件,每次写入1个字节,总长度由strlen给出
fwrite(buf, 1, strlen(buf), fp);
//写入InsertString,每次写入1个字节,总长度由strlen给出
fwrite("InsertString\n", 1, strlen("InsertString\n"),fp);
//把缓冲区的数据立即写入文件
fflush(fp);
//此时的文件位置指针位于文件的结尾处,使用fseek函数使文件指针回到文件头
fseek(fp, 0, SEEK_SET);
//从文件中读取内容到str中,每次读取100个字节,读取1次
fread(str, 100, 1, fp);
printf("File content:\n%s \n", str);
fclose(fp);
return 0;
}
?Linux提供的文件操作系统调用常用的有open、write、read、lseek、close等,C标准库中的操作文件函数可以认为是对系统调用的封装,方便二次开发。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
Linux使用open函数来打开文件,并返回该文件对应的文件描述符。函数参数说明如下:
pathname:要打开或创建的文件名;
flag:指定文件的打开方式,具体有以下参数,见下表 flag参数值。
标志位 | 含义 |
---|---|
O_RDONLY | 以只读的方式打开文件,该参数与O_WRONLY和O_RDWR只能三选一 |
O_WRONLY | 以只写的方式打开文件 |
O_RDWR | 以读写的方式打开文件 |
O_CREAT | 创建一个新文件 |
O_APPEND | 将数据写入到当前文件的结尾处 |
O_TRUNC | 如果pathname文件存在,则清除文件内容 |
?C库函数fopen的mode参数与系统调用open的flags参数有如下表中的等价关系。
r | O_RDONLY |
w | O_WRONLY | O_CREAT | O_TRUNC |
a | O_WRONLY | O_CREAT | O_APPEND |
r+ | O_RDWR |
w+ | O_RDWR | O_CREAT | O_TRUNC |
a+ | O_RDWR | O_CREAT | O_APPEND |
mode:当open函数的flag值设置为O_CREAT时,必须使用mode参数来设置文件与用户相关的权限。mode可用的权限如下表所示,表中各个参数可使用 " | "来组合。
返回值:成功返回文件描述符fd,失败返回-1。
\ | 标志位 | 含义 |
---|---|---|
当前用户 | S_IRUSR | 用户拥有读权限 |
\ | S_IWUSR | 用户拥有写权限 |
\ | S_IXUSR | 用户拥有执行权限 |
\ | S_IRWXU | 用户拥有读、写、执行权限 |
当前用户组 | S_IRGRP | 当前用户组的其他用户拥有读权限 |
\ | S_IWGRP | 当前用户组的其他用户拥有写权限 |
\ | S_IXGRP | 当前用户组的其他用户拥有执行权限 |
\ | S_IRWXG | 当前用户组的其他用户拥有读、写、执行权限 |
其他用户 | S_IROTH | 其他用户拥有读权限 |
\ | S_IWOTH | 其他用户拥有写权限 |
\ | S_IXOTH | 其他用户拥有执行权限 |
\ | S_IRWXO | 其他用户拥有读、写、执行权限 |
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
read函数用于从文件中读取若干个字节的数据,保存到数据缓冲区buf中,并返回实际读取的字节数,具体函数参数如下:
fd:文件对应的文件描述符,可以通过open函数获得。
buf:指向数据缓冲区的指针。
count:读取多少个字节的数据。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
write函数用于往文件写入内容,并返回实际写入的字节长度,具体函数参数如下:
fd:文件对应的文件描述符,可以通过fopen函数获得。
buf:指向数据缓冲区的指针;
count:往文件中写入多少个字节
#include <unistd.h>
int close(int fd);
当我们完成对文件的操作之后,想要关闭该文件,可以调用close函数,来关闭该fd文件描述符对应的文件。
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
lseek函数可以用与设置文件指针的位置,并返回文件指针相对于文件头的位置。
它的用法与flseek一样,其中的offset参数用于指定位置,whence参数则定义了offset的意义,whence的可取值如下:
SEEK_SET:offset是一个绝对位置。
SEEK_END:offset是以文件尾为参考点的相对位置。
SEEK_CUR:offset是以当前位置为参考点的相对位置。
对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负数,当打开一个现有文件或则创建一个新文件时,内核向进程返回一个文件描述符。
Linux进程会默认打开3个文件描述符,分别为标准输入0(stdin)、标准输出1(stdout)、标准错误2(stderror),其对应的物理设备一般为键盘、显示器、显示器。
文件描述符就是从0开始的非负数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,所以操作系统用file结构体来描述文件,表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针变量file,指向files_struct结构体,该结构体的成员变量有一个指针数组fd_array[],每个元素都是一个指向file结构体的指针!本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。
在fd_array数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
close(0);
int fd1 = open("myfile1", O_RDONLY);
if(fd1 < 0)
{
perror("open");
return 1;
}
printf("fd: %d\n", fd);
int fd2 = open("myfile2", O_RDONLY);
if(fd1 < 0)
{
perror("open");
return 1;
}
close(fd1);
close(fd2);
return 0;
}
如果我们把标准输出的文件描述符1关闭,然后在打开一个文件,使用printf函数会发生什么现象?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
close(1);
int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
fflush(stdout);
close(fd);
exit(0);
}
我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中。这种现象叫做输出重定向。常见的重定向有: >, >>, <。
产生这种现象的原因是因为我们把标准输出的文件描述符1关闭了,所以新打开的文件所获得的文件描述符就是1,往1号文件描述符写的内容都会写到了myfile中,而printf函数是往1号文件描述符中写内容,所以printf里的内容写到了myfile中,而不是写到标准输出中。
dup和dup2函数都可以用来复制一个现有的文件描述符,返回值为复制的新文件描述符。
#include <unistd>
int dup(int fd);
int dup2(int fd1, int fd2);
其中dup返回的新文件描述符一定是当前可用文件描述符中的最小值,?而dup2,可以fd2参数指定新文件描述符的值,如果指定的文件描述符的值已经被打开,则会将其关闭,如果fd1等于fd2,则dup2返回fd2,而不会去关闭它。
?