linux C —— 文件操作函数

本文详细介绍了Linux环境下C语言进行文件操作的相关函数,包括chmod、umask、stat等系统调用,以及fcntl、fopen、fgetc、fputc等带缓存的文件I/O操作。此外,还涵盖了目录文件操作如mkdir、opendir、readdir,以及链接文件的创建和管理。示例代码展示了如何使用这些函数实现具体功能,如文件权限设置、文件复制、目录遍历等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

chmod

int chmod(const char *path, mode_t mode)
将文件read的文件权限变成所有者有读写权限,其他用户只有读的权限。这和shell指令中的chmod有相同的功效。

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h> /* for chmod */
#include <sys/types.h> /* for chmod */

int main(){
    system("touch read; ls -l |grep read");
    chmod("./read",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    system("ls -l |grep read");
    return 0;
}

umask

mode_t umask(mode_t mask)
先不讨论掩码,
创建文件默认最大权限为666 (-rw-rw-rw-),默认创建的文件没有可执行权限x位。
创建目录默认最大权限777(-rwx-rwx-rwx),默认创建的目录属主是有x权限。
linux系统每一个新建的文件都被赋予某种默认的权限,这由权限掩码设置函数umask决定。umask()h函数还将返回上一个mask
umask()会将系统umask值设成参数mask&0777后的值,然后将先前的umask值返回.

$ cat file.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(){
    mode_t mask = 0755;
    mode_t oldmod = umask(mask);
    printf("old mask is 0%o\n",oldmod);
    printf("new mask is 0%o\n",umask(mask));
    return 0;
}

$ ./file
old mask is 02
new mask is 0755

$ cat file1.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(){
    system("touch f1; ls f1 -l");
    mode_t mask = 0123;
    umask(mask);
    system("touch f2; ls f2 -l");
    return 0;
}
$ ./file1
-rw-rw-r--. 1 edemon edemon 0 Nov 20 23:06 f1
-rw-r--r--. 1 edemon edemon 0 Nov 20 23:06 f2

分析: 文件默认的权限是val1= 0666,默认的mask是val2 = 02,两者进行运算:
0666&~0002 = 0666&0775 = 0664
第二次,mask是0123
0666&~0123 = 0666&0654 = 0644

stat

int stat(const char *file_name, struct stat *buf)
stat()用来将参数file_name所指的文件状态,复制到参数buf所指的结构中
关于结构体 stat:

[edemon@CentOS include]$ sed -n '39,94p' bits/stat.h
struct stat
  {
    __dev_t st_dev;         /* Device.  */
    unsigned short int __pad1;
#ifndef __USE_FILE_OFFSET64
    __ino_t st_ino;         /* File serial number.  */
#else
    __ino_t __st_ino;           /* 32bit file serial number.    */
#endif
    __mode_t st_mode;           /* File mode.  */
    __nlink_t st_nlink;         /* Link count.  */
    __uid_t st_uid;         /* User ID of the file's owner. */
    __gid_t st_gid;         /* Group ID of the file's group.*/
    __dev_t st_rdev;            /* Device number, if device.  */
    unsigned short int __pad2;
#ifndef __USE_FILE_OFFSET64
    __off_t st_size;            /* Size of file, in bytes.  */
#else
    __off64_t st_size;          /* Size of file, in bytes.  */
#endif
    __blksize_t st_blksize;     /* Optimal block size for I/O.  */

#ifndef __USE_FILE_OFFSET64
    __blkcnt_t st_blocks;       /* Number 512-byte blocks allocated. */
#else
    __blkcnt64_t st_blocks;     /* Number 512-byte blocks allocated. */
#endif
#if defined __USE_MISC || defined __USE_XOPEN2K8
    /* Nanosecond resolution timestamps are stored in a format
       equivalent to 'struct timespec'.  This is the type used
       whenever possible but the Unix namespace rules do not allow the
       identifier 'timespec' to appear in the <sys/stat.h> header.
       Therefore we have to handle the use of this header in strictly
       standard-compliant sources special.  */
    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
#else
    __time_t st_atime;          /* Time of last access.  */
    unsigned long int st_atimensec; /* Nscecs of last access.  */
    __time_t st_mtime;          /* Time of last modification.  */
    unsigned long int st_mtimensec; /* Nsecs of last modification.  */
    __time_t st_ctime;          /* Time of last status change.  */
    unsigned long int st_ctimensec; /* Nsecs of last status change.  */
#endif
#ifndef __USE_FILE_OFFSET64
    unsigned long int __unused4;
    unsigned long int __unused5;
#else
    __ino64_t st_ino;           /* File serial number.  */
#endif
  };

例子应用:

[edemon@CentOS workspace]$ cat stat.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

int main(){
    struct stat buff;
    int ret = stat("/etc/passwd",&buff);
    printf("/etc/passwd file's size is %d, last update time is %s",buff.st_size,ctime(&buff.st_mtime));
    return 0;
}
[edemon@CentOS workspace]$ ./stat
/etc/passwd file's size is 1619, last update time is Sun Nov 20 18:45:26 2016

查看一个文件的文件类型,相关的宏:

$ sed -n '131,140p' /usr/include/sys/stat.h
#define S_ISDIR(mode)    __S_ISTYPE((mode), __S_IFDIR)
#define S_ISCHR(mode)    __S_ISTYPE((mode), __S_IFCHR)
#define S_ISBLK(mode)    __S_ISTYPE((mode), __S_IFBLK)
#define S_ISREG(mode)    __S_ISTYPE((mode), __S_IFREG)
#ifdef __S_IFIFO
# define S_ISFIFO(mode)  __S_ISTYPE((mode), __S_IFIFO)
#endif
#ifdef __S_IFLNK
# define S_ISLNK(mode)   __S_ISTYPE((mode), __S_IFLNK)
#endif

不知道怎么用这些宏,明明将sys/stat.h已经include了,结果还说不认识。

基于文件描述符的文件操作:带缓存的文件I/O操作和不带缓存的文件I/O操作
不带缓存的文件I/O操作在嵌入式程序设计,socket程序设计,多路I/O操作程序设计方面应用广泛。

不带缓存的文件I/O操作

functioneffect
creat创建文件
open打开或创建文件
close关闭文件
read读文件
write写文件
lseek移动文件的读写位置
flock锁定文件或解除锁定
fcntl对文件描述符的各种操作
创建文件,文件所有者有读写可执行的权限:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(void) {
    int fd = creat("creat.txt",S_IRUSR|S_IWUSR|S_IXUSR);
    system("ls creat.txt -l");
    return EXIT_SUCCESS;
}
打开一个文件,如果不存在则创建他,存在的话内容清空。

open("open.txt",O_CREAT|O_TRUNC,0755)

使用write和read实现copy功能,将文件/etc/sysconfig/network拷贝到./cp.txt
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    int fd1 = open("/etc/sysconfig/network",O_RDONLY);
    if(fd1 < 0){
        perror("network open ");
        exit(1);
    }
    void *buff = malloc(100);
    int ret = read(fd1,buff,100);
    if(ret < 0){
        perror("read network ");
        exit(1);
    }
    int fd2 = open("cp.txt",O_WRONLY|O_TRUNC|O_CREAT,0644); 
    if(fd2 == -1){
        perror("cp.txt open ");
        exit(1);
    }
    ret = write(fd2,buff,ret);
    if(ret < 0){
        perror("write ");
        exit(1);
    }
    if(buff){
        free(buff);
        buff = 0x0;
    }
    close(fd1);
    close(fd2);
    system("cat cp.txt");
    return 0;
}

文件上锁,可以避免共享资源的使用冲突,这又分为强制性锁和建议性锁,分别对应者fcntl和flock。一般情况下系统使用强制性锁。

fcntl

fcntl的相关用法:http://blog.youkuaiyun.com/thearcticocean/article/details/52614395

带缓存的文件I/O操作

带缓存的文件I/O操作是基于输入/输出(I/O)流机制的文件操作(头文件stdio.h),是标准的I/O操作,它比不带缓存的I/O操作的程序更加方移植,符合ANSI C标准。
读文件:现将数据读入内存缓存区,缓存区装满了再读入变量。
写文件:现将数据写入内存缓存区,缓存区装满了再写入变量。
缓存区越大,操作外存的次数越少,执行的效率也就越高。不同版本的系统缓存区是不一样的。
带缓存的I/O函数主要有:
fopen(), fclose(), fgetc(), fputc(), fgets(), fputs(), fread(), fwrite(), fseek(), ftell(), rewind().

fopen

FILE *fopen(const char *path, const char *mode)
mode: 字符串”r”, “r+”,”w”, “w+”, “a”, “a+” 。 加号主要是多了读或者写的权限。

fgetc

int fgetc(FILE * stream);
fgetc()从参数stream所指的文件中读取一个字符。若读到文件尾而无数据时便返回EOF

fputc

int fputc(int c, FILE *stream)
int c 是由字符的ASCII码。

fgets

char *fgets(char *s, int size, FILE *stream);

fputs

int fputs(const char *s, FILE *stream)
若成功则返回写出的字符个数,返回EOF则表示有错误发生.

fread

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
读取的字符数是size*nmemb, fread()会返回实际读取到的nmemb数目,如果此
值比参数nmemb 来得小,则代表可能读到了文件尾或有错误发生,这时必须用feof()或ferror()来判断。

fwrite

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

fseek

int fseek(FILE *stream, long offset, int whence);
whence有这些取值:SEEK_SET(文件开头),SEEK_CUR(当前位置),SEEK_END(文件末尾)
正常情况下,函数执行后文件流的读写位置是whence + offset,offset可以是负数。

ftell

long ftell(FILE *stream);
用于获取文件流目前的读写位置。

rewind

void rewind(FILE *stream);
将文件流的读写位置移至文件的开头。等价于 fseek(FILE *stream, 0, SEEK_SET).

应用:使用f系列的带缓存I/O函数将file1.txt的内容和file2.txt的内容交叉写入file3.txt中

应该是下面的效果:

file1.txt (Human Nature - Eternal Flame):
Close your eyes, give me your hand, darling
Do you feel my heart beating
Do you understand
Do you feel the same
Am I only dreaming
Is this burning an eternal flame

file2.txt (美丽的神话):
梦中人熟悉的脸孔
你是我守侯的温柔
就算泪水淹没天地
我不会放手
每一刻孤独的承受
只因我曾许下承诺

file3.txt:
Close your eyes, give me your hand, darling
梦中人熟悉的脸孔
Do you feel my heart beating
你是我守侯的温柔
Do you understand
就算泪水淹没天地
Do you feel the same
我不会放手
Am I only dreaming
每一刻孤独的承受
Is this burning an eternal flame
只因我曾许下承诺

经测试,SEEK_SET is 0, SEEK_CUR is 1, SEEK_END is 2
它和ftell()的返回值是完全不同的。
下面是实现的例子:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

int main(){
    FILE *fp1 = fopen("file1.txt","r");
    if(fp1 == NULL) {
        perror("file1.txt fopen ");
        exit(1);
    }
    FILE *fp2 = fopen("file2.txt","r");
    if(fp2 == NULL) {
        perror("file2.txt fopen ");
        exit(1);
    }
    FILE *fp3 = fopen("file3.txt","w");
    if(fp3 == NULL) {
        perror("file3.txt fopen ");
        exit(1);
    }
    char *buff = (char *)malloc(50);
    char tag1 = 1, tag2 = 0;
    fseek(fp1,0,SEEK_SET);   /* 移动至文件的开头 */
    fseek(fp2,0,SEEK_SET);
    rewind(fp3);
    struct stat st1,st2,st3;
    stat("file1.txt",&st1);  /* 获取文件的信息 */
    stat("file2.txt",&st2);
    while(ftell(fp1) != st1.st_size || ftell(fp2) != st2.st_size){
        if(ftell(fp1) != st1.st_size && tag1){
            if(fgets(buff,50,fp1)){
                if(EOF == fputs(buff,fp3)){
                    perror("fputs ");
                    exit(1);
                }
            }
            tag1 = 0;
            tag2 = 1;
        }   
        if(ftell(fp2) != st2.st_size && tag2){
            if(fgets(buff,50,fp2)){
                if(EOF == fputs(buff,fp3)){
                    perror("fputs ");
                    exit(1);
                }
            }
            tag2 = 0;
            tag1 = 1;
        }
    }
    if(buff){
        free(buff);
        buff = 0x0;
    }
    fclose(fp1); 
    fclose(fp2);
    fclose(fp3);
    return 0;
}
/*
$ cat file3.txt 
Close your eyes, give me your hand, darling
梦中人熟悉的脸孔
Do you feel my heart beating
你是我守侯的温柔
Do you understand
就算泪水淹没天地
Do you feel the same
我不会放手
Am I only dreaming
每一刻孤独的承受
Is this burning an eternal flame
只因我曾许下承诺
*/

目录文件的操作

有关目录文件操作的函数:mkdir, opendir, closedir, readdir, scandir等

mkdir

int mkdir(const char *path, mode_t mode)
失败返回-1,成功返回0

opendir

DIR *opendir(const char *name)
opendir()用来打开参数name指定的目录,并返回DIR*形态的目录流,失败返回NULL。

closedir

int closedir(DIR *dir)
失败返回-1,成功返回0

readdir

struct dirent *readdir(DIR *dir);
readdir()返回参数dir目录流的下个目录进入点
有关结构体dirent的信息:

$ sed -n '23,35p' /usr/include/bits/dirent.h 
struct dirent
  {
#ifndef __USE_FILE_OFFSET64
    __ino_t d_ino;
    __off_t d_off;
#else
    __ino64_t d_ino;
    __off64_t d_off;
#endif
    unsigned short int d_reclen;
    unsigned char d_type;
    char d_name[256];       /* We must not include limits.h! */
  };

d_ino 目录进入点的inode
d_off 目录文件开头至此目录进入点的位移
d_reclen d_name的长度,不包含NULL字符
d_type d_name 所指的文件类型
d_name 文件名
关于d_type:

sed -n '96,118p' /usr/include/dirent.h 
#ifdef __USE_BSD
/* File types for `d_type'.  */
enum
  {
    DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
    DT_FIFO = 1,
# define DT_FIFO    DT_FIFO
    DT_CHR = 2,
# define DT_CHR     DT_CHR
    DT_DIR = 4,
# define DT_DIR     DT_DIR
    DT_BLK = 6,
# define DT_BLK     DT_BLK
    DT_REG = 8,
# define DT_REG     DT_REG
    DT_LNK = 10,
# define DT_LNK     DT_LNK
    DT_SOCK = 12,
# define DT_SOCK    DT_SOCK
    DT_WHT = 14
# define DT_WHT     DT_WHT
  };

scandir

$ man scandir

#include <dirent.h>

       int scandir(const char *dirp, struct dirent ***namelist,
              int (*filter)(const struct dirent *),
              int (*compar)(const struct dirent **, const struct dirent **));

       int alphasort(const void *a, const void *b);

       int versionsort(const void *a, const void *b);

       The function versionsort() is a GNU extension, available since glibc 2.1.

       Since glibc 2.1, alphasort() calls strcoll(3); earlier it used strcmp(3).


RETURN VALUE
       The scandir() function returns the number of directory entries selected or -1 if an error occurs.

例子:

查看/root下所有的目录信息。
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>

void check_type(struct dirent *pdir){
    switch (pdir->d_type){
        case DT_UNKNOWN:
            printf("it's file type is unknown\n");
            break;
        case DT_FIFO:
            printf("it's file type is fifo\n");
            break;
        case DT_CHR:
            printf("it's file type is char\n");
            break;
        case DT_DIR:
            printf("it's file type is dir\n");
            break;
        case DT_REG:
            printf("it's file type is regular\n");
            break;
        case DT_LNK:
            printf("it's file type is link\n");
            break;
        case DT_SOCK:
            printf("it's file type is socket\n");
            break;
        case DT_WHT:
            printf("it's file type is whiteout\n");
            break;
        default:
            printf("????\n");
            break;
    }
}
int main(){
    DIR *dir = opendir("/root"); 
    if(dir == NULL){
        perror("opendir ");
        exit(1);
    }
    struct dirent *pdir = NULL;
    while((pdir = readdir(dir))){
        printf("dir name: %s,\t it's inode: %d,\t inode off: %d\t ",pdir->d_name,pdir->d_ino,pdir->d_off);
        check_type(pdir);
    }
    if(-1 == closedir(dir)){
        perror("closedir ");
        exit(1);
    }
    return 0;
}

output:

dir name: Music,     it's inode: 685577,     inode off: 1848726371   it's file type is dir
dir name: .tcshrc,   it's inode: 652812,     inode off: 1893546464   it's file type is regular
dir name: Public,    it's inode: 685575,     inode off: 1922090525   it's file type is dir
dir name: install.log,   it's inode: 652803,     inode off: 2076570234   it's file type is regular
查看根目录/下所有的子目录并按照字母顺序输出
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

int main(){
    struct dirent **namelist = NULL;
    int n, i;
    if((n=scandir("/",&namelist,0,alphasort)) == -1){
        perror("scandir ");
        exit(1);
    }
    for(i=0; i<n; i++){
        printf("%s\t",namelist[i]->d_name);
        free(namelist[i]);
    }
    puts("");
    free(namelist);
    return 0;
}
/*
./a.out 
.   ..  .autofsck   .dbus   .pulse  .pulse-cookie   bin boot    dev etc home 
lib lost+found  medimisc    mnt net opt proc    root    sbin    selinux srv 
sys tmp usr var 
$ ls -a /
.   .autofsck  boot   dev  home  lost+found  misc  net  proc    .pulse-cookie  sbin     srv  tmp  var
..  bin        .dbus  etc  lib   media       mnt   opt  .pulse  root           selinux  sys  usr
*/

链接文件

软连接函数
int symlink(const char *oldpath, const char *newpath);
symlink() creates a symbolic link named newpath which contains the string oldpath.

创建3个软连接,一个指向正常的文件,一个指向自己,一个指向不存在的文件。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){
    if(symlink("read","read_link") == -1){
        perror("read symlink ");
        exit(1);
    }
    if(symlink("invalid","in_link") == -1){
        perror("invalid symlink ");
        exit(1);
    }
    if(symlink("self_link","self_link") == -1){
        perror("self symlink ");
        exit(1);
    }
    system("ls *link -l");
    return 0;
}
/*
./a.out 
lrwxrwxrwx. 1 edemon edemon 7 Nov 22 22:46 in_link -> invalid
lrwxrwxrwx. 1 edemon edemon 4 Nov 22 22:46 read_link -> read
lrwxrwxrwx. 1 edemon edemon 9 Nov 22 22:46 self_link -> self_link
*/

这里写图片描述

[edemon@CentOS symlink]$ file in_link 
in_link: broken symbolic link to `invalid'
[edemon@CentOS symlink]$ file self_link 
self_link: symbolic link in a loop
[edemon@CentOS symlink]$ file read_link 
read_link: symbolic link to `read'

可以看出,软连接文件即使指向不存在的文件也能创建。不过这是空悬链接(断链)。
A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent one; the latter case is known as a dangling link.

硬连接函数
对于软连接文件来说,删除软连接文件源文件不受影响,删除源文件两者都被删除。
对于硬连接文件来说,删除硬链接文件源文件不受影响,删除源文件硬连接文件不受影响。
硬连接文件就好像是源文件的拷贝一样的。
但是,我们需要注意: 不能给目录创建硬连接,不能给不同文件系统的文件创建硬连接。

[edemon@CentOS symlink]$ ln -P ../symlink/ s_link
ln: `../symlink/': hard link not allowed for directory

关于link函数,系统调用版本的在线手册是这样解释的:
$ man 2 link

#include <unistd.h>

       int link(const char *oldpath, const char *newpath);
       DESCRIPTION
       link() creates a new link (also known as a hard link) to an existing file.

       If newpath exists it will not be overwritten.

       This  new  name  may be used exactly as the old one for any operation; both names refer to the same file (and so
       have the same permissions and ownership) and it is impossible to tell which name was the "original".
创建一个空的文件,然后创建一个指向她的硬连接,接着向链接文件中写入数据,看看源文件会发生什么,删除源文件,再查看链接文件。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(){
    system("touch read");
    link("read","read_link");
    int fd = open("read_link",O_WRONLY);
    char *str = "hello world!\n";
    int ret = write(fd,str,strlen(str));
    if(ret == -1){
            perror("write ");
            exit(1);
    }
    system("cat read; rm read; ls read_link -l");
    return 0;
}
/*
/a.out 
hello world!
-rw-rw-r--. 1 edemon edemon 13 Nov 23 00:16 read_link
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值