标准C库IO函数
标准c库的io函数开发的东西可以跨平台
标准c库io函数,带有缓冲区,能够提高效率(linux系统io函数没有缓冲区)
标准C库IO和Linux系统IO的关系
调用与被调用的关系
虚拟地址空间
文件描述符
Linux系统IO函数
open函数的使用
使用open函数打开一个文件
/*
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
//打开一个已经存在的文件
int open(const char *pathname,int flags);
参数:
pathname:要打开的文件路径
flags:对文件的操作权限设置还有其他的设置
O_RDONLY, O_WRONLY, O_RDWR 这三个设置是互斥的
返回值:返回一个新的文件描述符,如果调用失败,返回-1
errno:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号。
#include<stdio.h>
void perror(const char *s); //作用:打印errno对应的错误描述
s参数:用户描述,比如hello,最终输出的内容是 hello:xxx(实际的错误描述)
//创建一个新的文件
int open(const char *pathname,int flags,mode_t mode);
*/
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
int main(){
//打开一个文件
int fd = open("a.txt",O_RDONLY);
if(fd == -1){
perror("open");
}
//读写操作
//关闭
close(fd);
return 0;
}
使用open函数创建文件
/*
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
//创建一个新的文件
int open(const char *pathname,int flags,mode_t mode);
参数:
pathname:要创建的文件的路径
flags:对文件的操作权限和其他的设置
必选项:O_RDONLY, O_WRONLY, O_RDWR 这三个是互斥的
可选项:O_CREAT 文件不存在,创建新文件
mode:八进制的数,表示创建出的新的文件的操作权限,比如 0775
最终的权限是: mode & ~umask
读 写 执行 当前用户对文件的权限 当前用户所在组 其他组
r w x rwx rwx rwx
7 7 7 7
*/
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
int main(){
int fd = open("create.txt",O_RDWR | O_CREAT,0777);
if(fd == -1){
perror("open");
}
close(fd);
return 0;
}
调用read和write函数实现文件复制功能
/*
#include<unistd.h>
ssize_t read(int fd,void *buf,size_t count);
参数:
fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
buf:需要读取数据存放的地方,数组的地址
count:指定的数组的大小
返回值:
成功:
>0:返回实际的读取到的字节数
=0:文件已经读取完了
失败:-1,并且设置errno
#include<unistd.h>
ssize_t write(int fd,const void *buf,size_t count);
参数:
fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
buf:要往磁盘写入的数据,
count:要写的数据的实际的大小
返回值:
成功:返回被写入的实际的字节数
失败:-1,并且设置errno
*/
#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(){
//1、通过open打开english文件
int srcfd = open("english.txt",O_RDONLY);
if(srcfd == -1){
perror("open");
return -1;
}
//2、创建一个新的文件(拷贝文件)
int destfd = open("copy.txt",O_WRONLY | O_CREAT,0664);
if(destfd == -1){
perror("open");
return -1;
}
//3、频繁的独写操作
char buf[1024] = {0};
int len = 0;
while((len = read(srcfd,buf,sizeof(buf)))>0){
write(destfd,buf,len);
}
//4、关闭文件
close(destfd);
close(srcfd);
return 0;
}
通过ll,我们可以发现copy.txt和english.txt字节数是一样的
lseek的使用
/*
标准C库的函数
#include<stdio.h>
int fseek(FIFO *stream,long offset,int whence);
Linux系统函数
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int fd,off_t offset,int whence);
参数:
fd:文件描述符,通过open得到,通过这个fd操作某个文件
offset:偏移量
whence:
SEEK_SET
设置文件指针的偏移量
SEEK_CUR
设置偏移量:当前位置+第二个参数offset的值
SEEK_END
设置偏移量:文件大小+第二个参数offset的值
返回值:
返回文件指针的位置
作用:
1、移动文件指针到文件头
lseek(fd,0,SEEK_SET);
2、获取当前文件指针的位置
lseek(fd,0,SEEK_CUR);
3、获取文件长度
lseek(fd,0,SEEK_END);
4、扩展文件的长度,当前文件10b,110b,增加了100个字节
lseek(fd,100,SEEK_END);
注意:需要写一次数据才管用
*/
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
int main(){
int fd = open("hello.txt",O_RDWR);
if(fd == -1){
perror("open");
return -1;
}
int lk= lseek(fd,100,SEEK_END);
if(lk==-1){
perror("lseek");
return -1;
}
//写入一个空数据,不然虽然扩展成功了,但是没人用,还是显示之前的字节数
write(fd," ",1);
close(fd);
return 0;
}
我们可以看到,在未扩展文件之前是11个字节,扩展之后变成了112个字节,分别是原来的十一个字节+扩展的100个字节+写入的一个字节
stat的应用
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
作用:获取一个文件相关的一些信息
参数:
- pathname:操作的文件的路径
- statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
返回值:
成功:返回0
失败:返回-1 设置errno
int lstat(const char *pathname, struct stat *statbuf);
参数:
- pathname:操作的文件的路径
- statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
返回值:
成功:返回0
失败:返回-1 设置errno
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main(){
struct stat statbuf;
int ret = stat("a.txt",&statbuf);
if(ret == -1){
perror("stat");
return -1;
}
printf("size: %ld\n", statbuf.st_size);
return 0;
}
通过stat获取文件的信息,来实现ls的功能
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
//模拟实现ls -l 指令
int main(int argc, char *argv[]){
if(argc<2){
printf("%s filename\n", argv[0]);
return -1;
}
//通过stat函数获取用户传入的文件信息
struct stat st;
int ret = stat(argv[1],&st);
if(ret == -1){
perror("stat");
return -1;
}
//获取文件类型和文件权限
char perms[11] = {0}; //用于保存文件类型和文件权限的字符串
switch(st.st_mode & S_IFMT) {
case S_IFLNK:
perms[0] = 'l';
break;
case S_IFDIR:
perms[0] = 'd';
break;
case S_IFREG:
perms[0] = '-';
break;
case S_IFBLK:
perms[0] = 'b';
break;
case S_IFCHR:
perms[0] = 'c';
break;
case S_IFSOCK:
perms[0] = 's';
break;
case S_IFIFO:
perms[0] = 'p';
break;
default:
perms[0] = '?';
break;
}
// 判断文件的访问权限
// 文件所有者
perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';
// 文件所在组
perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
// 其他人
perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';
//硬连接数
int linkNum = st.st_nlink;
//文件所有者
char *fileUser = getpwuid(st.st_uid) ->pw_name;
//文件所在组
char * fileGRrp = getgrgid(st.st_gid)->gr_name;
//文件大小
long int fileSize = st.st_size;
//获取修改的时间
char *time = ctime(&st.st_mtime);
//不让他换行
char mtime[512]={0};
strncpy(mtime,time,strlen(time)-1);
char buf[1024];
sprintf(buf,"%s %d %s %s %ld %s %s",perms,linkNum,fileUser,fileGRrp,fileSize,mtime,argv[1]);
printf("%s\n",buf);
return 0;
}
文件属性操作函数
使用access判断文件是否存在
/*
#include<unistd.h>
int access(const char *pathname,int mode);
作用:判断某个文件是否有某个权限,或者判断文件是否存在
参数:
pathname:判断的文件路径
mode:
R_OK:判断是否有读权限
W_OK:判断是否有写权限
X_OK:判断是否有执行权限
F_OK:判断文件是否存在
返回值:成功返回0,失败返回-1
*/
#include<unistd.h>
#include<stdio.h>
int main(){
int ret = access("a.txt",F_OK);
if(ret == -1){
perror("access");
return -1;
}
printf("文件存在!!!! \n");
return 0;
}
chmod修改文件的权限
/*
#include<sys/stat.h>
int chmod(const char *pathname,mode_t mode);
修改文件的权限
参数:
pathname:需要修改的文件的路径
mode:需要修改的权限值,八进制的数
返回值:成功返回0,失败返回-1
*/
#include<sys/stat.h>
#include<stdio.h>
int main(){
int ret = chmod("a.txt",0775);
if(ret == -1){
perror("ret");
return -1;
}
printf("修改成功!!!\n");
return 0;
}
chown修改文件所在组
使用useradd增加一个用户,然后把a.txt所有者和所在组改为gaogao
/*
#include<unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);
参数:
owner:所有者的id
group:所在组的id
所有者和所在组的id可以通过 vim /etc/passwd 命令查看
只查看所在组: vim /etc/group
*/
#include<unistd.h>
#include<stdio.h>
int main(){
int ch = chown("a.txt",1001,1001);
if(ch == -1 ){
perror("chown");
return -1;
}
printf("修改成功\n");
return 0;
}
使用truncate缩减或者扩展文件的尺寸至指定的大小
/*
#include<unistd.h>
#include<sys/types.h>
int truncate(const char *path,off_t length);
作用:缩减或者扩展文件的尺寸至指定的大小
作用:
path:需要修改的文件的路径
length:需要最终文件变成的大小
*/
#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
int main(){
int tc = truncate("b.txt",20);
if(tc == -1){
perror("truncate");
return -1;
}
printf("success\n");
return 0;
}
目录操作函数
- mkdir:创建一个目录
- rmdir:删除一个空文件
- rename:对目录重命名
- chdir:更改当前目录的当前路径
- getcwd:获取当前路径
/*
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
作用:创建一个目录
参数:
pathname: 创建的目录的路径
mode: 权限,八进制的数
返回值:
成功返回0, 失败返回-1
*/
#include<sys/stat.h>
#include<sys/types.h>
#include<stdio.h>
int main(){
int ret=mkdir("aaa",0777);
if(ret == -1){
perror("mkdir");
return -1;
}
return 0;
}
注意:一个目录必须具有可执行文件,才能进入文件
/*
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
*/
#include <stdio.h>
int main() {
int ret = rename("aaa", "bbb");
if(ret == -1) {
perror("rename");
return -1;
}
return 0;
}
chdir修改工作目录
getcwd:获取当前路径
/*
#include <unistd.h>
int chdir(const char *path);
作用:修改进程的工作目录
比如在/home/nowcoder 启动了一个可执行程序a.out, 进程的工作目录 /home/nowcoder
参数:
path : 需要修改的工作目录
#include <unistd.h>
char *getcwd(char *buf, size_t size);
作用:获取当前工作目录
参数:
- buf : 存储的路径,指向的是一个数组(传出参数)
- size: 数组的大小
返回值:
返回的指向的一块内存,这个数据就是第一个参数
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main(){
//获取当前的工作目录
char buf[128];
getcwd(buf,sizeof(buf));
printf("当前工作目录是:%s\n",buf);
//修改工作目录
int ret = chdir("/home/nowcoder/Linux/lesson13");
if(ret == -1){
perror("chdir");
return -1;
}
//创建一个新的文件
int fd = open("chdir.txt",O_CREAT | O_RDWR,0664);
if(fd == -1){
perror("open");
return -1;
}
close(fd);
//获取当前的工作目录
char buf1[128];
getcwd(buf1,sizeof(buf1));
printf("当前工作目录是:%s\n",buf1);
return 0;
}
目录遍历函数
- opendir:打开一个目录
- readdir:读取目录里的内容
- closedir:关闭目录
/*
// 打开一个目录
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
参数:
- name: 需要打开的目录的名称
返回值:
DIR * 类型,理解为目录流
错误返回NULL
// 读取目录中的数据
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
- 参数:dirp是opendir返回的结果
- 返回值:
struct dirent,代表读取到的文件的信息
读取到了末尾或者失败了,返回NULL
// 关闭目录
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
*/
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int getFileNum(const char *path);
int main(int argc, char *argv[]){
if(argc<2){
printf("%s path\n", argv[0]);
return -1;
}
int num = getFileNum(argv[1]);
printf("普通文件的个数:%d\n",num);
return 0;
}
//用于获取目录下所有普通文件的个数
int getFileNum(const char *path){
//1、打开目录
DIR *dir = opendir(path);
if(dir == NULL){
perror("opendir");
exit(0);
}
struct dirent *ptr;
//记录普通文件的个数
int total = 0;
while((ptr = readdir(dir)) != NULL){
//获取名称
char *dname = ptr->d_name;
//忽略掉.和..
//strcmp(str1,str2) str1=str2则返回0 str1>str2则返回大于0的值 str1<str2则返回小于0的值
if(strcmp(dname,".")==0 || strcmp(dname,"..")==0){
continue;
}
//判断是否是普通文件还是目录
if(ptr->d_type == DT_DIR){
//目录,需要继续读取这个目录
char newpath[256];
sprintf(newpath,"%s/%s",path,dname);
total += getFileNum(newpath);
}
if(ptr->d_type == DT_REG){
//普通文件
total++;
}
}
//关闭目录
closedir(dir);
return total;
}
/*
#include <unistd.h>
int dup(int oldfd);
作用:复制一个新的文件描述符
fd=3, int fd1 = dup(fd),
fd指向的是a.txt, fd1也是指向a.txt
从空闲的文件描述符表中找一个最小的,作为新的拷贝的文件描述符
*/
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(){
int fd = open("a.txt", O_CREAT | O_RDWR,0664);
int fd1 = dup(fd);
if(fd1 == -1){
perror("dup");
return -1;
}
printf("fd: %d , fd1: %d \n",fd,fd1);
close(fd);
char *str="hello,world";
int ret = write(fd1,str,strlen(str));
if(ret == -1){
perror("write");
return -1;
}
close(fd1);
return 0;
}
dup2
/*
#include <unistd.h>
int dup2(int oldfd, int newfd);
作用:重定向文件描述符
oldfd 指向 a.txt, newfd 指向 b.txt
调用函数成功后:newfd 和 b.txt 做close, newfd 指向了 a.txt
oldfd 必须是一个有效的文件描述符
oldfd和newfd值相同,相当于什么都没有做
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main() {
int fd = open("1.txt", O_RDWR | O_CREAT, 0664);
if(fd == -1) {
perror("open");
return -1;
}
int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);
if(fd1 == -1) {
perror("open");
return -1;
}
printf("fd : %d, fd1 : %d\n", fd, fd1);
int fd2 = dup2(fd, fd1);
if(fd2 == -1) {
perror("dup2");
return -1;
}
// 通过fd1去写数据,实际操作的是1.txt,而不是2.txt
char * str = "hello, dup2";
int len = write(fd1, str, strlen(str));
if(len == -1) {
perror("write");
return -1;
}
printf("fd : %d, fd1 : %d, fd2 : %d\n", fd, fd1, fd2);
close(fd);
close(fd1);
return 0;
}
/*
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
参数:
fd : 表示需要操作的文件描述符
cmd: 表示对文件描述符进行如何操作
- F_DUPFD : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
int ret = fcntl(fd, F_DUPFD);
- F_GETFL : 获取指定的文件描述符文件状态flag
获取的flag和我们通过open函数传递的flag是一个东西。
- F_SETFL : 设置文件描述符文件状态flag
必选项:O_RDONLY, O_WRONLY, O_RDWR 不可以被修改
可选性:O_APPEND, O)NONBLOCK
O_APPEND 表示追加数据
NONBLOK 设置成非阻塞
阻塞和非阻塞:描述的是函数调用的行为。
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main() {
// 1.复制文件描述符
// int fd = open("1.txt", O_RDONLY);
// int ret = fcntl(fd, F_DUPFD);
// 2.修改或者获取文件状态flag
int fd = open("1.txt", O_RDWR);
if(fd == -1) {
perror("open");
return -1;
}
// 获取文件描述符状态flag
int flag = fcntl(fd, F_GETFL);
if(flag == -1) {
perror("fcntl");
return -1;
}
flag |= O_APPEND; // flag = flag | O_APPEND
// 修改文件描述符状态的flag,给flag加入O_APPEND这个标记
int ret = fcntl(fd, F_SETFL, flag);
if(ret == -1) {
perror("fcntl");
return -1;
}
char * str = "nihao";
write(fd, str, strlen(str));
close(fd);
return 0;
}