前言:
在 Linux 系统中必须要使用系统提供的 IO 函数才能基于这些文件描述符完成对相关文件的读写操作。
文件相关函数说明:
open/close:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
open是一个系统函数, 只能在linux系统中使用, windows不支持
fopen 是标准c库函数, 一般都可以跨平台使用, 可以这样理解:
- 在linux中 fopen底层封装了Linux的系统API open
- 在window中, fopen底层封装的是 window 的 api
*/
// 打开一个已经存在的磁盘文件
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_APPEND: 新数据追加到文件尾部,不会覆盖文件的原来内容
O_CREAT: 如果文件不存在,创建该文件,如果文件存在什么也不做
O_EXCL: 检测文件是否存在,必须要和 O_CREAT 一起使用,不能单独使用: O_CREAT | O_EXCL
检测到文件不存在,创建新文件
检测到文件已经存在,创建失败,函数直接返回 - 1(如果不添加这个属性,不会返回 - 1)
mode: 在创建新文件的时候才需要指定这个参数的值,用于指定新文件的权限,这是一个八进制的整数
这个参数的最大值为:0777
**返回值:**
成功:返回内核分配的文件描述符,这个值被记录在内核的文件描述符表中,这是一个大于 0 的整数
失败: -1
#include <unistd.h>
int close(int fd);
read/write
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
fd: 文件描述符,open () 函数的返回值,通过这个参数定位打开的磁盘文件
buf: 是一个传出参数,指向一块有效的内存,用于存储从文件中读出的数据
传出参数:类似于返回值,将变量地址传递给函数,函数调用完毕,地址中就有数据了
count: buf 指针指向的内存的大小,指定可以存储的最大字节数
返回值:
大于 0: 从文件中读出的字节数,读文件成功
等于 0: 代表文件读完了,读文件成功
-1: 读文件失败了
write 函数用于将数据写入到文件内部,在通过 open 打开文件的时候需要指定写权限,函数原型如下:
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数:
fd: 文件描述符,open () 函数的返回值,通过这个参数定位打开的磁盘文件
buf: 指向一块有效的内存地址,里边有要写入到磁盘文件中的数据
count: 要往磁盘文件中写入的字节数,一般情况下就是 buf 字符串的长度,strlen (buf)
返回值:
大于 0: 成功写入到磁盘文件中的字节数
-1: 写文件失败了
上述函数使用
// 文件的拷贝
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int main()
{
// 1. 打开存在的文件open.txt, 读这个文件,注意文件的路径
int fd1 = open("./open.txt", O_RDONLY);
if(fd1 == -1)
{
perror("open-readfile");
return -1;
}
// 2. 打开不存在的文件, 将其创建出来, 将从open.txt读出的内容写入这个文件中
int fd2 = open("bak.txt", O_WRONLY|O_CREAT, 0664);
if(fd2 == -1)
{
perror("open-writefile");
return -1;
}
// 3. 循环读文件, 循环写文件
char buf[1024];
int len = -1;
while( (len = read(fd1, buf, sizeof(buf))) > 0 )
{
// 将读到的数据写入到另一个文件中
write(fd2, buf, len);
}
// 4. 关闭文件
close(fd1);
close(fd2);
return 0;
}
lseek
函数原型如下:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
fd: 文件描述符,open () 函数的返回值,通过这个参数定位打开的磁盘文件
offset: 偏移量,需要和第三个参数配合使用
whence: 通过这个参数指定函数实现什么样的功能
SEEK_SET: 从文件头部开始偏移 offset 个字节
SEEK_CUR: 从当前文件指针的位置向后偏移 offset 个字节
SEEK_END: 从文件尾部向后偏移 offset 个字节
返回值:
成功:文件指针从头部开始计算总的偏移量
失败: -1
**常用文件指针移动函数:**
**文件指针移动到文件头部:**
lseek(fd, 0, SEEK_SET);
**得到当前文件指针的位置:**
lseek(fd, 0, SEEK_CUR);
**得到文件总大小:**
lseek(fd, 0, SEEK_END);
truncate/ftruncate
函数原型说明如下:
// 拓展文件或截断文件
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
参数:
path: 要拓展 / 截断的文件的文件名
fd: 文件描述符,open () 得到的
length: 文件的最终大小
文件原来 size > length,文件被截断,尾部多余的部分被删除,文件最终长度为 length
文件原来 size < length,文件被拓展,文件最终长度为 length
返回值:成功返回 0; 失败返回值 - 1
stat/lstat 函数
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
int lstat(const char *pathname, struct stat *buf);
参数:
pathname: 文件名,要获取这个文件的属性信息
buf: 传出参数,文件的信息被写入到了这块内存中
返回值:函数调用成功返回 0,调用失败返回 -1
这个函数的第二个参数是一个结构体类型,这个结构体相对复杂,通过这个结构体可以存储得到的文件的所有属性信息,结构体原型如下:
struct stat {
dev_t st_dev; // 文件的设备编号
ino_t st_ino; // inode节点
mode_t st_mode; // 文件的类型和存取的权限, 16位整形数 -> 常用
nlink_t st_nlink; // 连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; // 用户ID
gid_t st_gid; // 组ID
dev_t st_rdev; // (设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; // 文件字节数(文件大小) --> 常用
blksize_t st_blksize; // 块大小(文件系统的I/O 缓冲区大小)
blkcnt_t st_blocks; // block的块数
time_t st_atime; // 最后一次访问时间
time_t st_mtime; // 最后一次修改时间(文件内容)
time_t st_ctime; // 最后一次改变时间(指属性)
};
获取文件属性实例:
#include <sys/stat.h>
int main()
{
// 1. 定义结构体, 存储文件信息
struct stat myst;
// 2. 获取文件属性 open.txt
int ret = stat("./open.txt", &myst);
if(ret == -1)
{
perror("stat");
return -1;
}
printf("文件大小: %d\n", (int)myst.st_size);
return 0;
}
opendir/readdir/closedir 函数
#include <sys/types.h>
#include <dirent.h>
// 打开目录
DIR *opendir(const char *name);
参数: name -> 要打开的目录的名字
返回值: DIR*, 结构体类型指针。打开成功返回目录的实例,打开失败返回 NULL
目录打开之后,就可以通过 readdir () 函数遍历目录中的文件信息了。每调用一次这个函数就可以得到目录中的一个文件信息,当目录中的文件信息被全部遍历完毕会得到一个空对象。先来看一下这个函数原型:
// 读目录
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
参数:dirp -> opendir () 函数的返回值
返回值:函数调用成功,返回读到的文件的信息,目录文件被读完了或者函数调用失败返回 NULL
函数返回值 struct dirent 结构体原型如下:
struct dirent {
ino_t d_ino; /* 文件对应的inode编号, 定位文件存储在磁盘的那个数据块上 */
off_t d_off; /* 文件在当前目录中的偏移量 */
unsigned short d_reclen; /* 文件名字的实际长度 */
unsigned char d_type; /* 文件的类型, linux中有7中文件类型 */
char d_name[256]; /* 文件的名字 */
};
关于结构体中的文件类型 d_type,可使用的宏值如下:
DT_BLK:块设备文件
DT_CHR:字符设备文件
DT_DIR:目录文件
DT_FIFO :管道文件
DT_LNK:软连接文件
DT_REG :普通文件
DT_SOCK:本地套接字文件
DT_UNKNOWN:无法识别的文件类型
那么,如何通过 readdir () 函数遍历某一个目录中的文件呢?
// 打开目录
DIR* dir = opendir("/home/test");
struct dirent* ptr = NULL;
// 遍历目录
while( (ptr=readdir(dir)) != NULL)
{
.......
}
目录操作完毕之后,需要通过 closedir() 关闭通过 opendir() 得到的实例,释放资源。函数原型如下:
// 关闭目录, 参数是 opendir() 的返回值
int closedir(DIR *dirp);
参数:dirp-> opendir () 函数的返回值
返回值:目录关闭成功返回 0, 失败返回 -1
目录函数实例说明–遍历目录:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
int getMp3Num(const char* path)
{
// 1. 打开目录
DIR* dir = opendir(path);
if(dir == NULL)
{
perror("opendir");
return 0;
}
// 2. 遍历当前目录
struct dirent* ptr = NULL;
int count = 0;
while((ptr = readdir(dir)) != NULL)
{
// 如果是目录 . .. 跳过不处理
if(strcmp(ptr->d_name, ".")==0 ||
strcmp(ptr->d_name, "..") == 0)
{
continue;
}
// 假设读到的当前文件是目录
if(ptr->d_type == DT_DIR)
{
// 目录
char newPath[1024];
sprintf(newPath, "%s/%s", path, ptr->d_name);
// 读当前目录的子目录
count += getMp3Num(newPath);
}
else if(ptr->d_type == DT_REG)
{
// 普通文件
char* p = strstr(ptr->d_name, ".mp3");
// 判断文件后缀是不是 .mp3
if(p != NULL && *(p+4) == '\0')
{
count++;
printf("%s/%s\n", path, ptr->d_name);
}
}
}
closedir(dir);
return count;
}
int main(int argc, char* argv[])
{
// ./a.out path
if(argc < 2)
{
printf("./a.out path\n");
return 0;
}
int num = getMp3Num(argv[1]);
printf("%s 目录中mp3文件个数: %d\n", argv[1], num);
return 0;
}
到处就完成了基本函数的说明。