前段时间工作时需要使用 C/C++ 来做一些压缩、加密等操作,其中经常要用到一些文件操作的函数,自己也封装了一些便利的函数。现在这部分工作内容已完结,就对这些文件操作做一次总结,将来再用到时减少学习成本。
打开文件
fopen 是 C 标准库用来操作文件的函数,拥有良好的移植性,而 open 是 UNIX 的系统调用,移植性有限,我基本都使用 fopen 函数:
FILE *fopen(char *filename, char *mode);
在这里,filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:
模式 | 描述 |
---|---|
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则会被截断为零长度,重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
返回值FILE *
是指向这个文件的指针,后续用它来做文件读写等操作。
读写文件
下面两个函数用于二进制文件输入和输出:
size_t fread(void *ptr, size_t size_of_elements,
size_t number_of_elements, FILE *a_file);
size_t fwrite(const void *ptr, size_t size_of_elements,
size_t number_of_elements, FILE *a_file);
需要注意的是返回值是成功读取的对象个数,即number_of_elements
,而不是读取内容的size
。若出现错误或到达文件末尾,则可能小于number_of_elements
。
关闭文件
文件操作完成后,请使用fclose()
函数关闭文件。函数的原型如下:
int fclose(FILE *fp);
如果成功关闭文件,fclose()
函数返回零,如果关闭文件时发生错误,函数返回 -1。
文件权限
文件权限标志可以使用加权数字表示,这组数字被称为 umask 变量,它的类型是 mode_t,是一个无符号八进制数。umask 变量的定义方法如下表所示。umask 变量由 3 位数字组成,数字的每一位代表一类权限。用户所获得的权限是加权数值的总和。例如 764 表示所有者拥有读、写和执行权限,群组拥有读和写权限,其他用户拥有读权限。
加权数值 | 第1位 | 第2位 | 第3位 |
---|---|---|---|
4 | 所有者拥有读权限 | 群组拥有读权限 | 其他用户拥有读权限 |
2 | 所有者拥有写权限 | 群组拥有写权限 | 其他用户拥有写权限 |
1 | 所有者拥有执行权限 | 群组拥有执行权限 | 其他用户拥有执行权限 |
文件类型
Linux 中一切皆文件,有以下 7 种文件类型。
普通文件类型:Linux 中最多的一种文件类型, 包括 纯文本文件(ASCII)、二进制文件 (binary)、数据格式的文件 (data) 和各种压缩文件。使用ls -l
命令查看时第一个属性为 [-]。
目录文件:就是目录, 能用 cd 命令进入的。第一个属性为 [d],例如 [drwxrwxrwx]。
块设备文件: 就是存储数据以供系统存取的接口设备,简单而言就是硬盘。例如一号硬盘的代码是 /dev/hda1 等文件。第一个属性为 [b]。
字符设备文件:即串行端口的接口设备,例如键盘、鼠标等等。第一个属性为 [c]。
套接字文件:这类文件通常用在网络数据连接。可以启动一个程序来监听客户端的要求,客户端就可以通过套接字来进行数据通信。第一个属性为 [s],最常在 /var/run 目录中看到这种文件类型。
管道文件:FIFO也是一种特殊的文件类型,它主要的目的是,解决多个程序同时存取一个文件所造成的错误。FIFO 是 first-in-first-out (先进先出) 的缩写。第一个属性为 [p]。
链接文件:类似 Windows 下面的快捷方式。第一个属性为 [l],例如 [lrwxrwxrwx]。
文件信息
access()
函数用于检查文件是否存在和访问权限,原型如下:
// mode 可选值
// R_OK 测试读许可权
// W_OK 测试写许可权
// X_OK 测试执行许可权
// F_OK 测试文件是否存在
int access(const char * pathname, int mode)
成功执行时,返回0,失败返回-1。
stat
函数用于获取文件的详细信息,保存在标准库的struct stat
中,原型如下:
int stat(const char * pathname, stat *buf);
struct stat
定义如下:
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //节点
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; //文件字节数(文件大小)
unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
便利函数
获取文件内容和大小
static FILE *OpenFileWithPath(const char *path)
{
const char *fileMode = "rb";
return fopen (path, fileMode);
}
static char *ReadStringFromFile(const char *pathName, int *size)
{
FILE *file = OpenFileWithPath (pathName);
if (file == NULL)
{
return 0;
}
fseek (file, 0, SEEK_END);
int length = ftell(file);
fseek (file, 0, SEEK_SET);
if (length < 0)
{
fclose (file);
return 0;
}
*size = length;
char *outData = malloc (length);
int readLength = fread (outData, 1, length, file); fclose(file);
if (readLength != length)
{
free (outData);
return 0;
}
return outData;
}
从绝对路径中提取文件名
char* FileName(char *absolutePath)
{
if (absolutePath[0] != '/') {
return 0;
}
char *s = strrchr(absolutePath, '/');
size_t size = strlen(s);
char *des = malloc(size);
memcpy(des, s + 1, size);
return des;
}
获取绝对路径的文件所在的目录
char* DirName(char *absolutePath)
{
if (absolutePath[0] != '/') {
return 0;
}
const size_t len = strlen(absolutePath);
char *path = malloc(len + 1);
strncpy(path, absolutePath, len);
if (path[len - 1] == '/') {
path[len - 1] = 0;
}
for(char * p = path + strlen(path) - 1; *p; p--){
if (*p == '/'){
*p = '\0';
break;
}
}
return path;
}
递归创建一个目录
#define DEFAULT_DIR_MODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH // 0755
int RecursiveMkdir(const char * dir, const unsigned int mode){
const size_t len = strlen(dir);
if (!len){
return 0;
}
char * path = calloc(len + 1, sizeof(char));
strncpy(path, dir, len);
if (path[len - 1] == '/'){
path[len - 1] = 0;
}
for(char * p = path + 1; *p; p++){
if (*p == '/'){
*p = '\0';
if (access(path, 0) < 0) {
mkdir(path, mode?mode:DEFAULT_DIR_MODE);
}
*p = '/';
}
}
free(path);
return 0;
}
文档信息
- 本文作者:Ning Zhang
- 本文链接:https://sunsetroads.github.io/2020/06/15/file/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)