C/C++ 中的文件操作总结

2020/06/15 C++ 共 3804 字,约 11 分钟

前段时间工作时需要使用 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;
}

文档信息

Search

    Table of Contents