linux - 文件操作

文件操作

文件IO

由操作系统实现并提供给外部应用程序的编程接口。(Application Programming Interface,API)。是应用程序同系统之间数据交互的桥梁。

在这里插入图片描述

文件描述符

一个进程启动之后,默认打开三个文件描述符:

#define  STDIN_FILENO     		0
#define  STDOUT_FILENO    	    1
#define  STDERR_FILENO    	    2

新打开文件返回文件描述符表中未使用的最小文件描述符, 调用open函数可以打开或创建一个文件, 得到一个文件描述符.

open

打开或新建一个文件;

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

函数参数:

  • pathname参数是要打开或创建的文件名,和fopen一样, pathname既可以是相对路径也可以是绝对路径。
  • flags参数有一系列常数值可供选择, 可以同时选择多个常数用按位或运算符连接起来, 所以这些常数的宏定义都以O_开头,表示or。
    • O_RDONLY 只读打开
    • O_WRONLY 只写打开
    • O_RDWR 可读可写打开
    • O_APPEND 表示追加。如果文件已有内容, 这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容。
    • O_CREAT 若此文件不存在则创建它。使用此选项时需要提供第三个参数mode, 表示该文件的访问权限。
    • O_EXCL 如果同时指定了O_CREAT,并且文件已存在,则出错返回。
    • O_TRUNC 如果文件已存在, 将其长度截断为为0字节。
    • O_NONBLOCK 对于设备文件, 以O_NONBLOCK方式打开可以做非阻塞I/O(NonblockI/O),非阻塞I/O。

close

关闭文件;

close(int fd);

fd代表文件描述符;

需要说明的是,当一个进程终止时, 内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close, 在终止时内核也会自动关闭它打开的所有文件。但是对于一个长年累月运行的程序(比如网络服务器), 打开的文件描述符一定要记得关闭, 否则随着打开的文件越来越多, 会占用大量文件描述符和系统资源。

read

从打开的设备或文件中读数据

ssize_t read(int fd, void *buf, size_t count);

fd:文件描述符

buf:读上来的数据保存在缓冲区buf中

count:buf缓冲区存放的最大字节数

write

向打开的设备或文件中写数据

 ssize_t write(int fd, const void *buf, size_t count);

fd:文件描述符

buf:读上来的数据保存在缓冲区buf中

count:buf缓冲区存放的最大字节数

lseek

所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为cfo. cfo通常是一个非负整数, 用于表明文件开始处到文件当前位置的字节数. 读写操作通常开始于 cfo, 并且使 cfo 增大, 增量为读写的字节数. 文件被打开时, cfo 会被初始化为 0, 除非使用了 O_APPEND.

off_t lseek(int fd, off_t offset, int whence);

fd:文件描述符

  • 参数 offset 的含义取决于参数 whence。
    • 如果 whence 是 SEEK_SET,文件偏移量将设置为 offset。
    • 如果 whence 是 SEEK_CUR,文件偏移量将被设置为 cfo 加上 offset,offset 可以为正也可以为负。
    • 如果 whence 是 SEEK_END,文件偏移量将被设置为文件长度加上 offset,offset 可以为正也可以为负

Demo代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


int main(int argc,char* args[])
{

        int fd = open(args[1],O_RDWR | O_CREAT , 0777);
        if(fd<0)
        {
                perror("open error\n");	//
            	printf("open error is %s\n",strerror(errno));
                return 0;
        }
        write(fd,"hello,world",strlen("hello,world"));
        //移动文件指针
        //off_t lseek(int fd, off_t offset, int whence);
        lseek(fd,0,SEEK_SET);
        char buffer[1024];
        memset(buffer,0,sizeof(buffer));
        read(fd,buffer,sizeof(buffer));
        //printf("%s\n",buffer);
        close(fd);
    	return 0;
}

errno

errno是一个全局变量,当系统调用后若出错后将会对errno进行设置,perror则可以将errno的值打印出来;

perror(errno);
printf("%s\n",strerror(errno));	这两种办法都可以得到错误信息

dup

复制一个文件描述符

int dup(int oldfd);

oldfd -要复制的文件描述符;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VK0mF6jl-1607298523860)(img/文件操作/0.png)]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
int main(int argc,char* args[])
{
        int fd = open(args[1],O_RDWR);		//旧文件描述符
        if(fd<0)
        {
                perror("open error");
                return -1;
        }
        int newfd = dup(fd);			//新文件描述符
        printf("fd = %d newfd = %d\n",fd,newfd);

        char buf[1024];
        memset(buf,0,sizeof(buf));
        write(fd,"hello,world\n",sizeof("hello,world\n"));	//使用旧的文件描述符写入文件
        //SEEK_SET
        lseek(fd,0,SEEK_SET);					//移动文件指针到最开始
        int n = read(newfd,buf,sizeof(buf));	//使用新的文件描述符读
        printf("read over:%d %s\n",n,buf);
        close(fd);
        close(newfd);
    return 0;
}

dup2

如果调用dup2的时候,旧的文件描述符如果打开了一个文件,那么先关闭旧的文件描述符,然后指向新的文件描述符,如果旧的未指向,则全部指向新的文件描述符

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
int main(int argc,char* args[])
{
    int fd = open(args[1],O_RDWR);
    int fd1 = open(args[2],O_RDWR);
    if(fd<0)
    {
        perror("open error");
        return -1;
    }

            
    if(fd1<0)
    {
        perror("open error");
        return -1;
    }
        dup2(fd,fd1);
        printf("fd = %d fd1 = %d\n",fd,fd1);

        char buf[1024];
        memset(buf,0,sizeof(buf));
        write(fd,"hello,world\n",sizeof("hello,world\n"));
        //SEEK_SET
        lseek(fd1,0,SEEK_SET);
        int n = read(fd1,buf,sizeof(buf));
        printf("read over:%d %s\n",n,buf);
        close(fd);
        close(fd1);
    return 0;
}

fcntl

函数描述: 改变已经打开的文件的属性

//c函数原型: int fcntl(int fd, int cmd, ... /* arg */ );
  • 若cmd为F_DUPFD, 复制文件描述符, 与dup相同

  • 若cmd为F_GETFL, 获取文件描述符的flag属性值

  • 若cmd为 F_SETFL, 设置文件描述符的flag属性

函数返回值:返回值取决于cmd

如果执行成功:

  • 若cmd为F_DUPFD, 返回一个新的文件描述符

  • 若cmd为F_GETFL, 返回文件描述符的flags值

  • 若cmd为 F_SETFL, 返回0

失败返回-1, 并设置errno值.

//1 复制一个新的文件描述符:
int newfd = fcntl(fd, F_DUPFD, 0);
//2 获取文件的属性标志
int flag = fcntl(fd, F_GETFL, 0)
//3 设置文件状态标志
flag = flag | O_APPEND;
fcntl(fd, F_SETFL, flag)
//4 常用的属性标志
//O_APPEND-----设置文件打开为末尾添加
//O_NONBLOCK-----设置打开的文件描述符为非阻塞

复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
int main(int argc,char* args[])
{
    int fd = open(args[1],O_RDWR | O_CREAT, 0777);
    if(fd<0)
    {
           perror("open error");
           return -1;
    }
    int newfd = fcntl(fd,F_DUPFD,0);
    printf("fd = %d newfd = %d",fd,newfd);
    write(fd,"hello,world",sizeof("hello,world"));
    lseek(fd,0,SEEK_SET);
    char buf[64];
    memset(buf,0,sizeof(buf));
    int n = read(newfd,buf,sizeof(buf));
    printf("newfd = %d read = %s\n",n,buf);
    close(fd);
    close(newfd);
    return 0;
}
修改
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
int main(int argc,char* args[])
{
        int fd = open(args[1],O_RDWR);
        if(fd<0)
        {
                perror("open error");
                return -1;
        }
        //获取fd的flag标识,修改flag
        int flags = fcntl(fd,F_GETFL,0);
        flags = flags | O_APPEND;
        fcntl(fd,F_SETFL,flags);
        write(fd,"hello,world",sizeof("hello,world"));
        close(fd);

    return 0;
}

阻塞与非阻塞

read

//read函数读文件是否阻塞
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


int main(int argc,char* args[])
{
    int fd = open(args[1],O_RDWR);
    if(fd<0)
        {
        perror("open error\n");
        printf("error is %s\n",strerror(errno));
        return 0;
    }
    char buf[1024];
    memset(buf,0,sizeof(buf));
    int n = read(fd,buf,sizeof(buf));
    printf("first %s\n",buf);

    memset(buf,0,sizeof(buf));
    n = read(fd,buf,sizeof(buf));
    printf("second %s\n",buf);
    close(fd);
    return 0;
}

read读普通文件内容完成后,若再次read,则read函数会立刻返回!

反之如何read去读一个设备文件,则是阻塞的;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


int main(int argc,char* args[])
{
    int fd = open("/dev/tty",O_RDWR);		//打开一个设备文件,tty为终端
    if(fd<0)
    {
        perror("open error");
        return -1;
    }
    char buf[1024];
    memset(buf,0,sizeof(buf));
    int n = read(fd,buf,sizeof(buf));
    printf("%d %s\n",n,buf);
    return 0;
}

文件属性

stat

#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 {
               dev_t     st_dev;     /* ID of device containing file */
               ino_t     st_ino;     /* inode number */
               mode_t    st_mode;    /* protection */	//文件权限
               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 */
               blksize_t st_blksize; /* blocksize for file system I/O */
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
               time_t    st_atime;   /* time of last access */
               time_t    st_mtime;   /* time of last modification */	//最后一次修改时间
               time_t    st_ctime;   /* time of last status change */
           };
st_mode
目前,st_mode使用了其低19bit. 0170000 => 1+ 3*5 = 16.
其中,最低的9(0-8)是权限,9-11是id,12-15是类型。
具体定义如下:
S_IFMT     0170000   bitmask for the file type bitfields
S_IFSOCK   0140000   socket
S_IFLNK    0120000   symbolic link
S_IFREG    0100000   regular file
S_IFBLK    0060000   block device
       S_IFDIR    0040000   directory
S_IFCHR    0020000   character device
S_IFIFO    0010000   fifo
S_ISUID    0004000   set UID bit
S_ISGID    0002000   set GID bit (see below)
S_ISVTX    0001000   sticky bit (see below)
S_IRWXU    00700     mask for file owner permissions
S_IRUSR    00400     owner has read permission
S_IWUSR    00200     owner has write permission
S_IXUSR    00100     owner has execute permission
S_IRWXG    00070     mask for group permissions
S_IRGRP    00040     group has read permission
S_IWGRP    00020     group has write permission
S_IXGRP    00010     group has execute permission
S_IRWXO    00007     mask for permissions for others (not in group)
S_IROTH    00004     others have read permission
S_IWOTH    00002     others have write permisson
S_IXOTH    00001     others have execute permission
    
    
    
//使用宏快速判断文件类型
S_ISREG(m)  is it a regular file?

S_ISDIR(m)  directory?

S_ISCHR(m)  character device?

S_ISBLK(m)  block device?

S_ISFIFO(m) FIFO (named pipe)?

S_ISLNK(m)  symbolic link? (Not in POSIX.1-1996.)

S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


int main(int argc,char* args[])
{
    //int stat(const char *path, struct stat *buf);
    struct stat st;
    stat(args[1],&st);
    /*
    //获取文件类型
    if((st.st_mode & S_IFMT) == S_IFREG)
    {
        printf("普通文件\n");
    }
    else if((st.st_mode & S_IFMT) == S_ISDIR)
    {
        printf("目录文件\n");
    }
    else if((st.st_mode & S_IFMT) == S_ISLNK)
    {
        printf("链接文件\n");
    }
    */
    
    
 switch (st.st_mode & S_IFMT) {
           case S_IFBLK:  printf("block device\n");            break;
           case S_IFCHR:  printf("character device\n");        break;
           case S_IFDIR:  printf("directory\n");               break;
           case S_IFIFO:  printf("FIFO/pipe\n");               break;
           case S_IFLNK:  printf("symlink\n");                 break;
           case S_IFREG:  printf("regular file\n");            break;
           case S_IFSOCK: printf("socket\n");                  break;
           default:       printf("unknown?\n");                break;
           }    
    
    return 0;
}

lstat

//lstat函数详解
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


int main(int argc,char* args[])
{
    //int lstat(const char *path, struct stat *buf);
    int fd = open(args[1],O_RDONLY);
    struct stat st;
    int n = lstat(args[1],&st);
    if(n<0)
    {
        perror("open file");
        return -1;
    }
    printf("%d %d %d \n",st.st_uid,st.st_gid,st.st_size);
}

stat 函数与 lstat 函数的区别: 当一个文件是符号链接时,lstat 函数返回的是该符号链接本身的信息;而 stat 函数返回的是该链接指向文件的信息。

目录

opendir

打开一个目录,打开成功返回指向目录的指针;

DIR *opendir(const char *name);

closedir

关闭目录

int closedir(DIR *dirp);

成功返回0,错误返回-1

readdir

读取目录内容–目录项

#include <dirent.h>
struct dirent *readdir(DIR *dirp);
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
int readdir(unsigned int fd, struct old_linux_dirent *dirp,unsigned int count);

 struct old_linux_dirent {
               long  d_ino;              /* inode number */
               off_t d_off;              /* offset to this old_linux_dirent */
               unsigned short d_reclen;  /* length of this d_name */
               char  d_name[NAME_MAX+1]; /* filename (null-terminated) */
           }


demo:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
int main(int argc,char* args[])
{
    //DIR *opendir(const char *name);
    DIR *pDir = opendir(args[1]);
    if(pDir==NULL)
    {
        perror("opendir error");
        return -1;
    }
    
    //struct dirent *readdir(DIR *dirp);

    //int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
    
    struct dirent *pDent = NULL;
    while((pDent=readdir(pDir))!=NULL)
    {
        if(strcmp(pDent->d_name,".")==0 || strcmp(pDent->d_name,"..")==0)		//去除.文件
        {
                continue;
        }
        printf("%s->",pDent->d_name);
        switch(pDent->d_type)
        {
            case DT_REG:
                printf("普通文件\n");
                break;
            case DT_DIR:
                printf("目录文件\n");
                break;
            case DT_LNK:
                printf("链接文件\n");
                break;
        }
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值