1.文件截短
truncate
NAME
truncate, ftruncate - truncate a file to a specified length
truncate, ftruncate用来把文件截短到指定的长度
SYNOPSIS
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
@path:要截短的路径文件名
@length:截短后文件内容的长度
int ftruncate(int fd, off_t length);
@fd:要截短的那个文件的文件描述符 -->必须open提前打开
@length:截短后文件内容的长度
length < 原文件的长度 -->"截短"
lendth > 原文件的长度 -->"拓展"
返回值:
成功返回0,失败返回-1,同时errno被设置
例子:
int main(int argc,char *argv[])
{
//atoi函数就是把一个数字字符串转化成一个整数,比如 "123"-->123
int ret = truncate(argv[1],atoi(argv[2]));
if(-1 == ret)
{
perror("truncate failed");
return -1;
}
}
(1)length < 原文件的长度,原文件后面的内容,还能不能看得到?
后面的内容会被舍弃掉
(2)length > 原文件的长度,内容会怎么变?
文件大小会改变,后面多出来的内容会填充'\0'
2. 删除一个文件
unlink 删除一个文件
rmdir 删除一个空目录
remove 删除一个文件或者一个空目录
remove 删除一个普通文件 --> unlink
remove 删除一个空目录 --> rmdir
unlink
NAME
unlink, unlinkat - delete a name and possibly the file it refers to
SYNOPSIS
#include <unistd.h>
int unlink(const char *pathname);
@pathname:要删除的那个文件的路径文件名
返回值:成功返回0,失败返回-1,同时errno被设置
----------------------------------------------
rmdir
NAME
rmdir - delete a directory
rmdir只能用来删除一个空目录
SYNOPSIS
#include <unistd.h>
int rmdir(const char *pathname);
@pathname:要删除的那个目录的路径
返回值:成功返回0,失败返回-1,同时errno被设置
----------------------------------------------
remove
NAME
remove - remove a file or directory
SYNOPSIS
#include <stdio.h>
int remove(const char *pathname);
@pathname:要删除的路径文件名或者空目录路径
返回值:成功返回0,失败返回-1,同时errno被设置
3. 获取文件属性 stat,fstat,lstat
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
(在上述的函数的参数列表中发现有一个结构体struct stat)
在linux下,有一个结构体用来保存文件的状态或属性
struct stat {
dev_t st_dev; /* ID of device containing file */
//st_dev用来表示容纳这个文件的设备的设备号
//比如该文件存在某个硬盘或者某个U盘,那么这些设备的ID就会被保存在st_dev
ino_t st_ino; /* Inode number */
//该文件的inode结点的编号
mode_t st_mode; /* File type and mode */
//st_mode 包含文件的类型和权限
//假设我们得先定义一个struct stat st;
//我们可以通过宏来解析st.st_mode得知文件的类型或者权限
// S_ISREG(st.st_mode) 为真则表示该文件是一个普通文件 -
// S_ISDIR(st.st_mode) 为真则表示该文件是一个目录文件 d
// S_ISCHR(st.st_mode) 为真则表示该文件是一个字符设备 c
// S_ISBLK(st.st_mode) 为真则表示该文件是一个块设备 b
// S_ISFIFO(st.st_mode) 为真则表示该文件是一个管道文件 p
// S_ISLNK(st.st_mode) 为真则表示该文件是一个链接文件 l
// S_ISSOCK(st.st_mode) 为真则表示该文件是一个套接字文件 s
// st_mode还包含了文件的权限位,也可以加下面的代码进行解析:
// if(st.st_mode & S_IRUSR)
// user有read的权限
// else
// user没有read的权限
nlink_t st_nlink; /* Number of hard links */
//该文件的硬链接数
uid_t st_uid; /* User ID of owner */
//文件拥有者的ID
gid_t st_gid; /* Group ID of owner */
//文件的拥有者的组ID
dev_t st_rdev; /* Device ID (if special file) */
//设备号(假如该文件就是一个设备)
off_t st_size; /* Total size, in bytes */
//对于不同的对象,这个st_size含义不一样
//对于普通文件来说,st_size表示文件内容的大小
//对于一个符号链接文件,那么文件内容又是什么?
//ln -s 1.txt 2.txt 2.txt占5个字节
//对于目录文件来说?目录项数组(后面有介绍)。
blksize_t st_blksize; /* Block size for filesystem I/O */
//块大小(这个跟具体硬件设备有关,512个字节为一块)
//这个细节自己上网搜
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
//该文件占多少块
struct timespec st_atim; /* Time of last access */
//最后访问文件的时间
struct timespec st_mtim; /* Time of last modification */
//最后修改文件的时间
struct timespec st_ctim; /* Time of last status change */
//最后修改文件属性的时间
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
a. int stat(const char *pathname, struct stat *statbuf);
stat函数是用来获取pathname指定的那个文件的属性,获取到信息用statbuf指向的结构体来保存
不需要open
@pathname:要获取属性的路径文件名
@statbuf:指向的结构体用来保存属性
返回值:成功返回0,失败返回-1,同时errno被设置
b. int fstat(int fd, struct stat *statbuf);
fstat函数与stat函数功能与返回值完全一样,只不过参数有一个文件描述符
所以需要提前open
c. int lstat(const char *pathname, struct stat *statbuf);
lstat与stat一样,只不过当pathname是一个符号链接文件的时候,lstat获取就是符号链接文件本身的
属性,stat函数获取就是符号链接文件指向的那个文件(原文件)的属性
例子:
int main(int argc,char *argv[])
{
struct stat st;
int ret = stat(argv[1],&st);
if(-1 == ret)
{
perror("stat failed");
return -1;
}
if(S_ISREG(st.st_mode))
{
printf("是普通文件\n");
printf("文件大小为:%ld\n",st.st_size);
}
else if(S_ISDIR(st.st_mode))
{
printf("是目录文件\n");
}
else
{
printf("是其它文件\n");
}
return 0;
}
4. 目录操作
a. 目录是什么?
在linux下,目录也是一个文件,同样也能通过open(只能是只读的方式)去打开,打开之后我们能够得到一个文件描述符fd,这个
文件描述符描述是一个目录,这个时候能不能用read函数去读取:
read failed: Is a directory
所以在linux下不能让用read去读一个目录
目录文件里面的内容到底是什么?
b. 目录和普通文件的区别
在linux下,任何一个文件要能存在,必须要有一个inode结点
【inode节点存放在硬盘中,inode节点可以指向文件内容】
目录文件同样也需要一个inode结点:
例子: +
比如在一个目录long下,有两个txt文件,分别为1.txt和2.txt。那么这两个普通文件肯定也有inode。
inode结点中有一个成员变量指向一块存储空间-->文件的内容。
那么目录long的inode结点也会有一个成员变量指向了一块存储空间-->目录long的内容
目录long的内容到底是什么?我们打开一个目录后,应该是能够看到它的子目录(文件名)和子文件
所以目录long对应inode结点成员变量指向的那个存储空间里面存的是目录下的子目录和子文件的一些属性信息
【目录的inode节点指向的内容存放的是子文件和子目录的信息】
那么怎么去保存所有的子文件和子目录的信息-->一个子项应该用一个结构体保存
我们应该通过目录项数组,"目录项"是什么?我们把目录下每一个子文件或子目录称之为一个目录项。
这个目录项(结构体)叫做:
struct dirent
【保存子目录和子文件的结构体:dirent,目录的本质是一个文件,在硬盘中保存inode和文件内容】
【结构体dirent节点就叫做目录项】
在家目录下ls -l查看所有的目录的大小全部是4096,原因是我们在创建一个新目录的时候,系统就会为它
预留一个"目录项数组"(4096个字节大小),当在目录下创建新文件或者目录的时候,填充一个目录项到这个
目录项数组里面去。当一个目录下子文件太多,预留的空间也会满的。这个时候就不能再创建新的文件,此时
就会再给你开辟一块同样大小的空间(4096),这个时候就能继续创建
我们想知道一个目录下的层次关系,那么就必须去读目录项!!!
c. 目录操作
1)打开一个目录 opendir
NAME
opendir, fdopendir - open a directory
在linux下用结构体DIR来用描述一个已经打开的目录
SYNOPSIS
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
【用opendir打开的目录返回目录的指针: DIR * (这个是存放在内存中的,真正的目录内容在硬盘里,放在内存中操作快)】
@name:要打开的目录的名字
返回值:
成功的话返回一个DIR的指针
失败返回NULL,同时errno被设置
2)读取目录中的目录项 readdir
NAME
readdir - read a directory
SYNOPSIS
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
readdir用来从dirp指向的那个目录中,返回下一个目录项(struct dirent)的指针,一个目录里面有很多个目录项
每次调用一次这个函数就会返回下一个目录项,直到它返回的是NULL
struct dirent {
ino_t d_ino;//inode结点的编号
off_t d_off;//目录项的偏移
unsigned short d_reclen;//该结构体的长度
unsigned char d_type;//该目录项指向的那个文件的类型,请注意这个成员并不适用所有的文件系统
char d_name[256];//该目录项指向的那个文件的名字
};
该结构体成员中,只有d_ino,d_name是所有linux系统都支持!如果你想让你的代码具有可移植性,只能用这两个
成员
返回值:
返回下一个目录项的指针,如果读完了,返回NULL
3)关闭目录 closedir
NAME
closedir - close a directory
SYNOPSIS
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
@dirp:要关闭的目录的DIR结构体
练习1:
把一个目录下所有的文件的文件名打印出来,自己选一个目录下有较多文件的目录尝试
int main(int argc,char *argv[])
{
//打开目录
DIR *dir = opendir(argv[1]);
if(dir == NULL)
{
perror("opendir failed");
return -1;
}
char filename[512]={0};
struct dirent *dirp =NULL;
while(dirp = readdir(dir))
{
printf("%s\n",dirp->d_name);//不带路径
sprintf(filename,"%s/%s",argv[1],dirp->d_name);
printf("%s\n",filename);//带路径
}
closedir(dir);
}
【注意:目录是DIR * 而目录项则是 dirent结构体】
【你打开文件和目录传入到open或者opendir中的第一个参数就是文件的路径名,可以是绝对路径,亦然可以是相对路径】
但是,能够发现我们打印出来的文件里面会有"."和"..",所以在大部分操作中比如把这两个隐藏的目录给跳过
练习:
求一个目录下所有普通文件的大小之和,如果有目录则需要递归
思路:
如果要使用 递归,必须要先封装成一个函数
int get_dir_size(char *name) :求name下面所有普通文件的大小,并通过返回值返回
1.打开目录
2.读取目录
先跳过. 和 ..
判断你读取的到目录项是什么文件?
使用stat函数判断这个是目录还是普通文件
如果是普通文件 st_zise
如果是目录 (拼凑出路径名称,传入本函数,进行递归)
再打开
【打开,操作,关闭】
进阶问题: 拷贝一个完整目录,也需要用到递归