(一)Linux 文件操作API
(1)Linux文件操作常见API
在 Windows 系统中,对文件的打开或是编辑都是可视化的,其具体的操作流程可归纳为:
打开或创建文件 ——> 编辑文件 ——> 保存文件 ——> 关闭文件
在 Linux 系统中,一切皆文件,包括但不限于硬件设备、管道、数据库、Socket等;操作系统对文件的处理提供了一系列API程序接口,常用的包括:
API | 功能 |
---|---|
open | 打开文件 |
creat | 创建文件 |
read | 读取文件 |
write | 写入文件 |
lseek | 移动光标 |
close | 关闭文件 |
(2)常见API使用说明
使用这些API时,并不需要准确记住用法,只需要记住名称,使用man指令进行查找即可(man手册在Linux中绝对好用,适合小白),例如我们需要了解open函数如何调用,可在Linux 终端(Ctrl+Alt+T)输入“man 2 open”,即open函数在man手册中的第2页,会弹出对应的man手册。
lamda@lamda-virtual-machine:~/Desktop/code/Linux/File$ man 2 open
手册中关于open函数的描述如下,附带需要的头文件以及函数原型:
OPEN(2) Linux Programmer's Manual OPEN(2)
NAME
open, openat, creat - open and possibly create a file
SYNOPSIS
#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);
int creat(const char *pathname, mode_t mode);
int openat(int dirfd, const char *pathname, int flags);
int openat(int dirfd, const char *pathname, int flags, mode_t mode);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
openat():
Since glibc 2.10:
_POSIX_C_SOURCE >= 200809L
Before glibc 2.10:
_ATFILE_SOURCE
DESCRIPTION
The open() system call opens the file specified by pathname. If the specified file does not exist, it may optionally (if
O_CREAT is specified in flags) be created by open().
open 函数的原型常用的有两个,再加上creat函数:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
其中,pathname是指文件路径,是指向char类型的指针,可用字符串表示,mode表示文件权限,flags为文件操作使用权限,包括:
flags | 描述 |
---|---|
O_RDONLY | 只读 |
O_WRONLY | 只写 |
O_RDWR | 可读可写 |
O_CREAT | 若文件不存在,可创建新文件,并需要提供第三个参数mode描述文件权限 |
O_EXCL | 如果同时指定了OCREAT,而文件已经存在,则出错 |
O_APPEND | 每次写时都加到文件的尾端 |
O_TRUNC | 如果这个文件中本来是有内容的,而且为只读或只写成功打开,则将其长度截短为0 |
显然,前三个只能指定一个,函数中第三个参数mode表示文件权限,包括:
宏表示 | 数字 | 描述 |
---|---|---|
S_IRUSR | 4 | 可读 |
S_IWUSR | 2 | 可写 |
S_IXUSR | 1 | 可执行 |
S_IRWXU | 7 | 可读、可写、可执行 |
open函数会返回一个int类型的值,通常称为文件描述符,read或write函数则是通过这个文件描述符对文件进行编辑,文件描述符只能在当前进程下使用。同样通过man手册查阅如何调用read或write函数:
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
off_t lseek(int fd, off_t offset, int whence);
(3)Linux文件操作常见API使用示例
接下来我们简单使用C语言程序完成上述API完成文件的创建以及编辑。
// 需要的头文件
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
//ssize_t write(int fd, const void *buf, size_t count);
//ssize_t read(int fd, void *buf, size_t count);
//off_t lseek(int fd, off_t offset, int whence);
int main()
{
char buf[128] = "Write something for test!";
int fd;
fd = open("./file_demo",O_RDWR); //可读可写方式
if(fd==-1){ //检测是否存在,若不存在则创建
printf("This file maybe not exist.\n");
fd = open("./file_demo",O_CREAT,S_IRWXU); //0600,文件可读可写
}
int num_write = write(fd,buf,strlen(buf)); //写入内容
printf("Write %d bytes.\n",num_write);
int num_seek = lseek(fd,0,SEEK_SET);
//移动光标,因为写入内容后光标并不在文件头,需要重新调整到文件头进行读取
char *readbuf;
readbuf = (char *)malloc(num_write*sizeof(char));
int num_read = read(fd,readbuf,num_write);
printf("Read %d bytes.\n",num_read);
printf("%s\n",readbuf);
num_seek = lseek(fd,0,SEEK_CUR);
printf("num_seek = %d\n",num_seek);
close(fd); //关闭文件
return 0;
}
(二)C语言标准库文件操作API
(1)C语言标准库文件操作常见API
这里简单描述标准库里的API
API | 功能 |
---|---|
fopen | 打开文件 |
fseek | 移动光标 |
fread | 读取文件 |
fwrite | 写入文件 |
fclose | 关闭文件 |
fgetc | 读取字符 |
fputc | 写入字符 |
feof | 判断是否到达文件尾,否则返回0 |
(2)open与fopen的区别
参考博文:总结open与fopen的区别
1. 来源
从来源的角度看,两者能很好的区分开,这也是两者最显而易见的区别:
open是UNIX系统调用函数(包括LINUX等),返回的是文件描述符(File Descriptor),它是文件在文件描述符表里的索引。
fopen是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api。返回的是一个指向文件结构的指针。
2. 移植性
这一点从上面的来源就可以推断出来,fopen
是C标准函数,因此拥有良好的移植性;而open
是UNIX系统调用,移植性有限。如windows下相似的功能使用API函数CreateFile
。
3. 适用范围
open返回文件描述符,而文件描述符是UNIX系统下的一个重要概念,UNIX下的一切设备都是以文件的形式操作。如网络套接字、硬件设备等。当然包括操作普通正规文件(Regular File)。
fopen是用来操纵普通正规文件(Regular File)的。
4. 文件IO层次
如果从文件IO的角度来看,前者属于低级IO函数,后者属于高级IO函数。低级和高级的简单区分标准是:谁离系统内核更近。低级文件IO运行在内核态,高级文件IO运行在用户态。
5. 缓冲
缓冲文件系统
缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用;当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依此读出需要的数据。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存“缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器 而定。fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等。
非缓冲文件系统
缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符串、格式化数据,也可以读写二进制数据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。open, close, read, write, getc, getchar, putc, putchar等。
(3)C语言标准库文件操作常见API使用示例
同样地,我们使用C语言进行操作:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
//FILE *fopen(const char *pathname, const char *mode);
FILE *fp;
char *str = "Fopen:write something.";
char *readbuf = NULL;
readbuf = (char*)malloc(strlen(str));
fp = fopen("./file_fopen","r+");
printf("open file is ok\n");
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
int n_write = fwrite(str,sizeof(char),strlen(str),fp);
printf("write something is ok\n");
fseek(fp,0,SEEK_SET);
int n_read = fread(readbuf,sizeof(char),strlen(str),fp);
printf("read something: %d bytes, %s\n",n_read,readbuf);
fclose(fp);
return 0;
}
(三)总结
- 在使用write函数后一定记得使用lseek函数对光标进行定位,否则在原文件继续读取可能会出错。
- 文件描述符一般会从3开始,原因是0,1,2,分别被内核的相关操作所占用,分别为标准输入,标准输出,标准错误输出。
- read和write函数所操作的文本内容的都是无类型指针,意味着可以输入整型或是其他类型,如结构体、链表等。