一、简述
记--Linux环境下C语言编程的文件操作。
两种操作文件的方式:
1、系统I/O:系统调用接口,open(), read(), write(), lseek(), close()。是操作系统直接提供的编程接口(API)。
2、标准/IO:标准库的I/O函数,fopen(), fread(), fwrite(), fseek(), fclose(),是对系统调用接口进一步封装。
系统I/O常用于硬件级别,可以设置读缓冲区,一般没有写缓冲区;
标准I/O常用于软件级别,自带读写缓冲区。
二、系统I/O
2.1 open()函数
功能 | 打开一个指定的文件并获得文件描述符,或者创建一个新文件 | |||
头文件 | #include |
|||
#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 | 以追加方式打开文件 | |||
mode | 如果文件被新建,指定其权限为mode(八进制表示法) | |||
返回值 | 成功 | 大于等于0 的整数(即文件描述符) | ||
失败 | -1,并且errno会被设置 |
补充说明:
1)flags 的各种取值可以用位或的方式叠加起来,例如创建文件的时候需要满足这样的
选项:读写方式打开,不存在要新建,如果存在了则清空。则flags 的取值应该是:O_RDWR | O_CREAT | O_TRUNC。
2)mode 是八进制权限,比如0644,或者0755 等。也可以是使用系统已定义的
S_IRWXU 00700 user (file owner) has read, write, and execute permission //用户可读写执行权限
S_IRUSR 00400 user has read permission//用户写权限
S_IWUSR 00200 user has write permission//用户写权限
S_IXUSR 00100 user has execute permission//用户执行权限 (更多选项可查询man手册:man 2 open)
比如新创建的文件权限只需要读写权限:S_IRUSR | S_IWUSR
3)文件描述符
其实是一个数组的下标值,在内核中打开的文件是用 file 结构体来表示的,每一个结构体都会有一个 指针来指向它们,这些指针被统一存放在一个叫做 fd_array 的数组当中,而这个数组被存 放在一个叫做 files_struct 的结构体中,该结构体是进程控制块 task_struct 的重要组成部分。
2.2 close()函数
功能 |
关闭文件并释放相应资源 |
|
头文件 |
#include |
|
原型 |
int close(int fd); |
|
参数 | fd | 要关闭的文件的描述符 |
返回值 |
成功 |
0 |
|
失败 |
-1 |
备注 |
重复关闭一个已经关闭了的文件或者尚未打开的文件是安全的。 |
2.3 read()函数
功能 |
从指定文件中读取数据 |
|
头文件 |
#include |
|
原型 |
ssize_t read(int fd, void *buf, size_t count); |
|
参数 |
fd |
从文件 fd 中读数据,(fd是某个文件的描述符) |
buf |
指向存放读到的数据的缓冲区,(就是放数据的内存首地址) | |
count |
想要从文件 fd 中读取的字节数 | |
返回值 |
成功 |
实际读到的字节数 |
|
失败 |
-1 |
备注 |
实际读到的字节数小于等于 count |
补充说明:
ssize_t :是类型重定义,为了跨平台兼容。比如说long在32位系统可能是4字节,64位系统可能是8字节,嵌入式开发有的只有16位,那么int只有2个字节。
但是在编程时往往需要根据类型大小来进行操作数据,例如在64位系统编程时8字节使用long类型,如果移植到32位系统时long只有4字节,那么就需要改动好多个地方,所以重定义一种类型,然后根据实际系统再指定,方便改动移植。例如:重定义ssize类型是8字节的,在32位系统,我可以将long long重定义为ssize类型,如果是64位系统可以定义为long类型,这样只需改动一处。
2.4 write()函数
功能 |
将数据写入指定的文件 |
||
头文件 |
#include |
||
原型 |
ssize_t write(int fd, const void *buf, size_t count); |
||
参数 |
fd |
将数据写入到文件fd 中 (fd是某个文件的描述符) |
|
|
buf |
指向即将要写入的数据,(要写的数据的内存首地址) |
|
|
count |
要写入的字节数 |
|
返回值 |
成功 |
实际写入的字节数 |
|
|
失败 |
-1 |
|
备注 |
实际写入的字节数小于等于 count |
2.5 lseek()函数
功能 |
调整文件位置偏移量 |
|||
头文件 |
#include #include |
|||
原型 |
off_t lseek(int fd, off_t offset, int whence); |
|||
参数 |
fd |
要调整位置偏移量的文件的描述符 |
||
offset |
相对基准点的偏移大小 |
|||
whence:基准点 |
SEEK_SET:文件开头处 |
|||
SEEK_CUR:当前位置 |
||||
SEEK_END:文件末尾处 |
||||
返回值 |
成功 |
新文件位置偏移量(相对于文件开头的偏移) |
||
失败 |
-1 |
|||
备注 |
可用 file-size= lseek(fd,0,SEEK_END);来测量文件大小 |
示例代码:
#include
#include
#include
#include
#include
int main(int argc,char* argv[])
{
int fd;
int wr_ret;
int rd_ret;
unsigned long file_size;
char wr_buf[100] = "hello world";
char rd_buf[100];
fd = open("a.txt", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IROTH);//等价于fd = open("a.txt", O_RDWR | O_CREAT | O_TRUNC, 0x604);
if(fd == -1)
{
perror("open file error:");//只有上面的函数设置了error全局错误号,才可使用,会根据error输出对应的错误信息
return -1;
}
printf("fd = %d\n", fd);
wr_ret = write(fd, wr_buf, sizeof(wr_buf));
if(wr_ret == -1)
{
perror("write file error:");
return -1;
}
printf("wr_ret = %d\n", wr_ret);
lseek(fd, 0, SEEK_SET);//上面的写操作,文件位置偏移量也会相应的移动,此处将文件偏移到文件开始位置,然后才能读取刚刚输入的内容
rd_ret = read(fd, rd_buf, sizeof(rd_buf));
if(rd_ret == -1)
{
perror("read file error:");
return -1;
}
printf("rd_ret = %d\n",rd_ret);
printf("content=%s\n", rd_buf);
file_size = lseek(fd, 0, SEEK_END);
printf("file_size = %lu\n", file_size);
close(fd);//关闭文件
return 0;
}
2.5 mmap()函数
功能 |
将物理内存映射为虚拟内存,为了提高效率。(比如将一个文件映射到虚拟内存,以操作内存的方式进行数据的读写,但是不会改变文件的大小,一般不用于普通文件) | ||
头文件 |
#include |
||
原型 |
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); | ||
参数
|
addr | 从虚拟内存的哪个地址开始去映射这片内存 NULL代表由系统决定映射起始地址 |
|
length | 映射的内存长度(字节为单位) | ||
port |
决定这块内存的操作权限,以下数值可以相或 PROT_READ Pages may be read. 读权限 PROT_WRITE Pages may be written. 写权限 PROT_NONE Pages may not be accessed. 无权限 |
||
flags | 操作标志 MAP_SHARED:在多进程中把这块内存共享给其他进程 MAP_PRIVATE:不共享内存 |
||
fd |
将数据写入到文件fd 中 (fd是某个文件的描述符) | ||
offset |
基于文件头偏移多少单位开始映射 | ||
返回值 |
成功 |
成功返回映射的虚拟地址 | |
失败 |
失败返回MAP_FAILED,其实就是个-1, errno会被设置 | ||
备注 |
取消映射 int munmap(void *addr, size_t length); 成功返回0,失败返回-1 |
更多请查看man手册:man 2 mmap
示例代码:
#include
#include
#include
#include
#include
#include
#include //mencpy()
int main(void)
{
int fd;
char *map_ptr;
int retval;
fd = open("a.txt",O_RDWR);
if(fd == -1)
{
perror("open failed:");
return -1;
}
map_ptr = mmap( NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(map_ptr == MAP_FAILED)
{
perror("map memory failed:");
return -1;
}
printf("%s", map_ptr);
memcpy(map_ptr, "haha", 5);
retval = munmap(map_ptr, 100);
if(retval == -1)
{
perror("munmap failed:");
return -1;
}
close(fd);
return 0;
}
运行结果:
三、标准I/O
3.1 fopen()函数
功能 |
获取指定文件的文件指针 |
|
头文件 |
#include |
|
原型 |
FILE *fopen(const char *path, const char *mode); |
|
参数
|
path |
要打开的文件的路径名称 |
mode |
“r” : 以只读方式打开文件,要求文件必须存在。 | |
“r+” : 以读写方式打开文件,要求文件必须存在。 |
||
“w” : 以只写方式打开文件,文件如果不存在将会创建新文件,如果存 在将会将其内容清空。 |
||
“w+” : 以读写方式打开文件,文件如果不存在将会创建新文件,如果存在将会将其内容清空。 |
||
“a” : 以只写方式打开文件,文件如果不存在将会创建新文件,且文件位置偏移量被自动定位到文件末尾(即以追加方式写数据)。 |
||
“a+” : 以读写方式打开文件,文件如果不存在将会创建新文件,第一次用于写数据则文件位置偏移量被自动定位到文件末尾(即以追加方式写数据),如果第一次用于读数据,文件位置偏移位置会定位到文件开始。 |
||
返回值 |
成功 | 文件指针 |
失败 | NULL | |
备注 | 返回的文件指针是一种指向结构体 FILE{}的指针,文件描述符就被封装在FILE结构体里面 |
程序一开始默认打开3个文件
设备 |
文件描述符(int) |
文件指针(FILE *) |
|
标准输入设备(键盘) |
0 |
STDIN_FILENO |
stdin |
标准输出设备(屏幕) |
1 |
STDOUT_FILENO |
stdout |
标准出错设备(屏幕) |
2 |
STDERR_FILENO |
stderr |
3.2 fclose()函数
功能 |
关闭指定的文件并释放其资源 |
|
头文件 |
#include |
|
原型 |
int fclose(FILE *fp); |
|
参数 |
fp |
即将要关闭的文件 |
返回值 |
成功 |
0 |
失败 |
EOF |
|
备注 |
fclose( )不能对一个文件重复关闭 |
3.3 fread()函数
功能 |
从指定文件读取若干个数据块 |
|
头文件 |
#include |
|
原型 |
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); |
|
参数
|
ptr |
自定义缓冲区指针,(通俗的说就是要存放 读取出来的数据 的地方) |
size |
数据块大小 | |
nmemb |
数据块个数 | |
stream |
即将被读取数据的文件指针 | |
返回值 |
成功 |
读取的数据块个数,等于 nmemb |
|
失败 |
读取的数据块个数,小于 nmemb 或等于 0 |
备注 |
当返回小与 nmemb 时,文件 stream 可能已达末尾,或者遇到错误 |
3.4 fwrite()函数
功能 |
将若干块数据写入指定的文件 |
|
头文件 |
#include |
|
原型 |
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream); |
|
参数 |
ptr:自定义缓冲区指针(要写入的数据的首地址) |
|
|
size:数据块大小 |
|
|
nmemb:数据块个数 |
|
|
stream:即将被写入数据的文件指针 |
|
返回值 |
成功 |
写入的数据块个数,等于 sinmembze |
|
失败 |
写入的数据块个数,小于 nmemb 或等于 0 |
备注 |
无 |
3.5 fseek()函数
功能 |
设置指定文件的当前位置偏移量 |
||
头文件 |
#include |
||
原型 |
int fseek(FILE *stream, long offset, int whence); |
||
参数 |
stream:需要设置位置偏移量的文件指针 |
||
|
offset:新位置偏移量相对基准点的偏移 |
||
|
whence:基准点 |
SEEK_SET:文件开头处 |
|
|
|
SEEK_CUR:当前位置 |
|
|
|
SEEK_END:文件末尾处 |
|
返回值 |
成功 |
0 |
|
|
失败 |
-1 |
|
备注 |
无 |
3.6 ftell()函数
功能 |
获取指定文件的当前位置偏移量 |
|
头文件 |
#include |
|
原型 |
long ftell(FILE *stream); |
|
参数 |
stream:需要返回当前文件位置偏移量的文件指针 |
|
返回值 |
成功 |
当前文件位置偏移量 |
|
失败 |
-1 |
备注 |
无 |
3.7rewind()函数
功能 |
将指定文件的当前位置偏移量设置到文件开头处 |
头文件 |
#include |
原型 |
void rewind(FILE *stream); |
参数 |
stream:需要设置位置偏移量的文件指针 |
返回值 |
无 |
备注 |
该函数的功能是将文件 strean 的位置偏移量置位到文件开头处。 |
简单例子:
#include
int main(int argc,char* argv[])
{
int wr_ret;
int rd_ret;
FILE *fp;
unsigned long file_size;
char wr_buf[100] = "hello world";
char rd_buf[100];
fp = fopen( "a.txt", "a+" );//文件追加,可读可写,文件不存在则创建
if(fp == NULL)
{
perror("open file error:");//只有上面的函数设置了error全局错误号,才可使用,会根据error输出对应的错误信息
return -1;
}
wr_ret = fwrite( wr_buf, sizeof(wr_buf), 1, fp);
printf("wr_ret = %d\n", wr_ret);
rewind(fp);//上面的写操作,文件位置偏移量也会相应的移动,此处将文件偏移到文件开始位置,然后才能读取刚刚输入的内容
rd_ret = fread(rd_buf, sizeof(rd_buf), 1, fp);
printf("rd_ret = %d\n",rd_ret);
printf("content=%s\n", rd_buf);
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
printf("file_size = %lu\n", file_size);
fclose(fp);//关闭文件
return 0;
}
运行结果