Linux文件操作[简单函数]

简介: 简单对文件操作的函数介绍

@TOC

前言

在学习LinuxC语言的时候首先需要学习的就是文件的操作,因为在Linux中一切皆为文件,所以所以C语言对文件操作是Linux开发的基础内容,这篇文章主要是讲解一下C语言对Linux文件操作的一些基本函数和操作。

一、文件描述符

在学习C语言对文件的操作的时候首先我们来讲解一下什么是文件描述符。
当我们在Linux中打开文件的时候,系统(内核)会返回一个文件描述符,文件描述符用来指定已经打开的文件。这个文件描述符相当于这个文件已经打开文件的符号,文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件,所以可以理解为文件描述符就是那一个文件的另一种显示方式,而这个文件描述符是一些整数。
而在程序运行起来后(每个进程)都有一张文件描述符的表,标准输入、标准输出、标准错误输出设备文件被打开,对应的文件描述符0、1、2记录在表中。程序运行起来后这三个文件描述符是默认打开的。
这三个文件描述符都有对应的宏定义和值:
|宏定义| 文件描述符 | 描述 |
|--|--|--|
|STDIN_FILENO | 0 | 标准输入的文件描述符 |
| STDOUT_FILENO | 1 | 标准输出的文件描述符 |
| STDERR_FILENO | 2 | 标准错误的文件描述 |

文件描述符有个数限制的,我们可以通过下面的命令查看一下当前系统能够打开的最大文件描述符的个数:

cat /proc/sys/fs/file-max

当然我们也是可以修改默认设置最大打开文件个数:

ulimit -n 4096

查看当前默认设置最大打开文件个数1024

ulimit -a

二、常用的文件IO函数

学习完文件标识符的基本概念后就可以学习文件操作的IO函数了。

1.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:文件的路径及文件名
    flags:打开文件的行为标志,必选项 O_RDONLY O_WRONLY O_RDWR
    mode:这个参数,只有在文件不存在时有效,指新建文件时指定文件的权限
返回值:
    成功:返回打开的文件描述符
    失败:-1

这个函数有两个参数的和三个参数的,两个参数的一般是对文件进行读取或者是写入存在的函数中,三个参数主要是进行一些其它操作的。
标识flags中填写的内容如下:
| 取值 | 含义 |
| -------- | ---------------------- |
| O_RDONLY | 以只读的方式打开 |
| O_WRONLY | 以只写的方式打开 |
| O_RDWR | 以可读、可写的方式打开 |

还可以写一些可选项:
| 取值 | 含义 |
| ---------- | ------------------------------------------------------------ |
| O_CREAT | 文件不存在则创建文件,使用此选项时需要使用mode说明文件的权限 |
| O_EXCL | 如果同时指定了O_CREAT,则文件已经存在,则会出错 |
| O_TRUNC | 如果文件存在,则清空文件内容 |
| O_APPEND | 写文件时,数据添加到文件末尾 |
| O_NONBLOCK | 对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O |

如果需要将这些选项联合使用就需要在之间加上|来进行连接.
如果这个文件不存在那就需要创建一下,而在Linux中创建文件是需要给这个文件一个权限(mode)的,而权限的宏选项如下:
| 取值 | 八进制 | 含义 |
| ------- | ------ | -------------------------------------- |
| S_IRWXU | 00700 | 文件拿使用者的读、写、可执行权限 |
| S_IRUSR | 00400 | 文件所有者的读权限 |
| S_IWUSR | 00200 | 文件所有者的写权限 |
| S_IXUSR | 00100 | 文件所有者的可执行权限 |
| S_IRWXG | 00070 | 文件所有这同组用户的读、写、可执行权限 |
| S_IRGRP | 00040 | 文件所有者同用户组的读权限 |
| S_IWGRP | 00020 | 文件所有者同组用户的写权限 |
| S_IXGRP | 00010 | 文件所有者同组的可执行权限 |
| S_IRWXO | 00007 | 其它用户的读、写、可执行权限 |
| S_IROTH | 00004 | 其它用户的读权限 |
| S_IWOTH | 00002 | 其它用户的写权限 |
| S_IXOTH | 00001 | 其它用户的可执行权限 |

这里需要注意一个问题,就是最终权限是用mode & ~umask才能得到的,你可以在你的shell中执行一下:

umask

看看你的umask的值为多少,然后用这个取反后与上你需要给的权限才能得到最终的权限。
比如说我现在要打开个文件,然后是写,如果文件不存在,那么就创建一下,创建的权限为664,如果文件存在,那就清空里面的内容,那么open函数的使用如下:

open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);

但你可能会觉得上面的方法很臃肿,那有什么办法可以让这个更简单呢?
其实可以把mode值换成八进制就可以解决这个问题:

open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);

这样也是可以的,这里就得看你的习惯了,有些人习惯写得臃肿有些喜欢写得简单。
当这个函数调用完之后会得到你打开文件后的文件描述符,操作这个文件描述符相当于操作这个文件。

2.close函数

打开文件后对应的就是关闭文件,关闭文件使用的函数是close函数,函数的原型如下:

#include <unistd.h>

int close(int fd);
功能:
    关闭已打开的文件
参数:
    fd:文件描述符,open()的返回值
返回值:
    成功:0
    失败:-1,并设置errno

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

close(fd); //fd是上面open后打开的文件描述符

3.read函数

read函数主要是用来读取文件中的内容的,但是并不是read可以随便读取你打开的文件中的内容,你需要让打开的方式为O_RDONLY或者是O_RDWR才可以读取文件中的内容,read函数的原型如下:

#include <unistd.h>
size_t read(int fd, void* buf, size_t count);
功能:
    把指定数目的数据到内存(缓冲区)
参数:
    fd:文件描述符
    buf:内存首地址
    count:读取的字节个数
返回值:
    成功:实际读取的字节个数
    失败:-1

调用完这个函数后读取的字节数和实际读取的字节数是不一样的,比如说你的文件中的字节数为100,而你这里指定读入200个字节,函数的返回值只能为100,而不能为200,毕竟这个文件中没有200个字节的文件。
如果返回为0,那证明读取完毕,就是这个文件没有数据了,所以可以通过读取的返回值是否为0来判断这个文件是否被读取完毕。
下面是读取文件中内容的例子:

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

int main(){
   
    int fd = -1; // 文件描述符
    char buf[10]; // 储存数据的变量
    int size;  // 读取字节大小
    fd = open("/tmp/filename", O_RDONLY);
    if (fd == -1){
   
        perror("open"); // 错误处理函数,后面会说的
        return -1;
    }
    size = read(fd, buf, 10);
    if (size == -1){
   
        perror("read");
        return -2;
    }
    printf("%s\n", buf);
    close(fd);
    return 0;
}

上面的代码是一个非常简单的读取文件中内容的代码,但是需要注意就是读取的文件必须得存在,否则就会报错。

这里还有一个问题,第一个参数是文件描述符,那我直接在第一个选项中写上0可以吗?

答案是可以的,因为前面说过,文件描述符是整形,而0、1、2这三个是标准的文件描述符,所以直接在这写上这些标准的文件描述符是完全可以的。

3.1 练习1

这里有一个练习,不使用scanf()输入函数,让用户输入的内容原封不动的输出到屏幕中,输入%结束,代码就如下:

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

int main(){
   
    char buf;
    int size;
    while(1){
   
        size = read(0, &buf, 1);
        if (size == -1){
   
            perror("read");
            return -1;
        }
        if (buf == '%'){
   
            break;
        }
        printf("%c", buf);
    }
    return 0;
}

4.write函数

write函数是写入函数,就是往文件中写入内容的函数,这个函数的原型如下:

#include <unistd.h>
size_t write(int fd, const void* buf, size_t count);
功能:
    把指定数目的数据写到文件(fd)
参数:
    fd:文件描述符
    buf:数据首地址
    count:写入数据的长度(字节)
返回值:
    成功:实际写入数据的字节个数
    失败:-1

这个函数的返回值和写入数据的长度是相同的,这里是需要注意一下的。
简单使用write函数如下:

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

int main(){
   
    int fd = -1;
    char buf[] = "hello,world";
    fd = open("/tmp/filename", O_WRONLY);
    if (fd == -1){
   
        perror("open"); 
        return -1;
    }
    write(fd, buf, sizeof(buf));
    close(fd);
    return 0;
}

上面的代码可以将buf中的内容写入到文件中。
这里又有一个练习,和上面read差不多。

4.1 练习2

不使用scanf()输入函数和printf()输出函数,让用户输入的内容原封不动的输出到屏幕中,输入%结束,代码就如下:

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

int main(){
   
    char buf;
    int size;
    while(1){
   
        size = read(0, &buf, 1);
        if (size == -1){
   
            perror("read");
            return -1;
        }
        if (buf == '%'){
   
            break;
        }
        write(1, buf, 1);
    }
    return 0;
}

5.模仿cp命令

当讲完了write和read函数后就可以来模仿一下cp命令了。首先介绍一下cp命令,cp命令其实是复制粘贴命令,可以将文件的内容复制粘贴到另一个文件中,也可以将文件复制粘贴到文件夹中,这里只考虑文件到文件,而cp命令的用法如下:

cp [文件] [目标文件]

现在我们来使用cp命令模仿一下该命令的功能,首先需要理解这个cp功能是如何进行复制粘贴的,其实就是将文件中的内容读取出来,然后再打开目标文件后,使用写就可以将之前读取的内容写入进去了,这个就是cp命令的模仿思路。
有了思路就可以将代码写一下了:

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

#define SIZE 512

int main(int argc, char* argv[]){
   
    int fd_r, fd_w, size;
    char buf[SIZE];
    if (argv < 3){
   
        printf("参数太少\n");
        return -1;
    }
    else if (argv > 3){
   
        printf("参数太多\n");
        return -1;
    }
    fd_r = open(argv[1], O_RDONLY);
    if (fd_r == -1){
   
        perror("open fd_r");
        return -2;
    }
    fd_w = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664);
    if (fd_w == -1){
   
        perror("open fd_w");
        close(fd_r);
        return -2;
    }
    while(1){
   
        // 读取文件中的内容
        size = read(fd_r, buf, SIZE);
        // 读取失败
        if (size == -1){
   
            perror("read");
            return -3;
        }
        //读取完毕
        if (size == 0){
   
            break;
        }
        //写入目标文件中
        write(fd_w, buf, size);
    }
    // 拷贝完毕关闭文件
    close(fd_w);
    close(fd_r);
    return 0;
}

然后编译并运行后传入参数就可以了

gcc -o main main.c
./main filename filename.txt

7.lseek函数

这个是基础中最后一个函数,也是比较复杂的一个函数,其实这个函数就是对光标的移动,但是这个函数只能对普通文件中的光标进行操作。函数原型如下:

#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);
功能:
    改变文件的偏移量
参数:
    fd:文件描述符
    offset:根据whence来移动的位移量(偏移量),
    可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence
    往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果移动
    的字节数超过了文件末尾,再次写入时将增大文件尺寸。

    whence:其取值如下:
        SEEK_SET: 从文件开头移动offset个字节数
        SEEK_CUR: 从当前位置移动offset个字节数
        SEEK_END: 从文件末尾移动offset个字节数
返回值:
    若lseek成功执行,则返回新的偏移量
    如果失败,返回-1

简单理解就是offset是光标移动的位置,whence是光标从哪移动的位置即可。

7.1 练习3

学习完这个后可以做下面的练习了:
现在有一个文件,需要写一个程序让这个文件中的内容颠倒。比如说文件中是:abcdefg,颠倒后就是gfedcba。
这个看上去很难,但是其实逻辑很简单的,首先读取文件,然后将读取的内容颠倒,再写入文件即可,代码如下:

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

#define SIZE 1024

int main(int argc, char* argv[]){
   
    int fd = -1;
    char buf[SIZE], a;
    int size, i;
    if (argc != 2){
   
        printf("参数有问题\n");
        return -1;
    }
    fd = open(argv[1], O_RDWR);
    if (fd == -1){
   
        printf("文件不存在");
        return -2;
    }
    size = read(fd, buf, SIZE);
    if (size == -1){
   
        perror("read");
        return -2;
    }
    for (i = 0; i < size / 2 - 1; i++){
   
        a = buf[i];
        buf[i] = buf[size - 2 - i];
        buf[size - 2 - i] = a;
    }
    lseek(fd, SEEK_SET, 0);
    write(fd, buf, size);
    close(fd);
    return 0;
}

总结

对于文件的操作其实还有其它更高级的用法,但是这里只是最基本的操作,大家多练习练习即可学会这些内容。

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
9天前
|
人工智能 监控 Shell
常用的 55 个 Linux Shell 脚本(包括基础案例、文件操作、实用工具、图形化、sed、gawk)
这篇文章提供了55个常用的Linux Shell脚本实例,涵盖基础案例、文件操作、实用工具、图形化界面及sed、gawk的使用。
26 2
|
19天前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
20 6
|
20天前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
59 3
|
1月前
|
Linux
在Linux内核中根据函数指针输出函数名称
在Linux内核中根据函数指针输出函数名称
|
2月前
|
Linux PHP
Linux CentOS 宝塔 Suhosin禁用php5.6版本eval函数详细图文教程
【8月更文挑战第27天】本文介绍两种禁用PHP执行的方法:使用`PHP_diseval_extension`禁用和通过`suhosin`禁用。由于`suhosin`不支持PHP8,仅适用于PHP7及以下版本,若服务器安装了PHP5.6,则需对应安装`suhosin-0.9.38`版本。文章提供了详细的安装步骤,并强调了宝塔环境下与普通环境下的PHP路径差异。安装完成后,在`php.ini`中添加`suhosin.so`扩展并设置`executor.disable_eval = on`以禁用执行功能。最后通过测试代码验证是否成功禁用,并重启`php-fpm`服务生效。
27 2
|
2月前
|
Shell Linux C语言
Linux0.11 execve函数(六)
Linux0.11 execve函数(六)
19 1
|
2月前
|
Linux API
Linux源码阅读笔记07-进程管理4大常用API函数
Linux源码阅读笔记07-进程管理4大常用API函数
|
2月前
|
Linux 数据安全/隐私保护 Perl
解锁Linux高手秘籍:文件操作+命令解析大揭秘,面试场上让你光芒万丈,技术实力惊艳四座!
【8月更文挑战第5天】Linux作为服务器与嵌入式系统的基石,其文件管理和命令行操作是技术人员必备技能。本文从文件操作和基础命令两大方面,深入浅出地解析Linux核心要义,助你在面试中脱颖而出。首先探索文件系统的树状结构及操作,包括使用`ls -la`浏览文件详情、`touch`创建文件、`rm -r`慎删目录、`cp`与`mv`复制移动文件、以及利用`find`搜索文件。接着掌握命令行技巧,如用`cat`、`more`和`less`查看文件内容;借助`grep`、`sed`与`awk`处理文本;运用`ps`、`top`和`kill`管理进程;并通过`chmod`和`chown`管理文件权限。
65 8
|
2月前
|
安全 Unix Linux
Linux Clone函数
Linux Clone函数
44 3
|
2月前
|
Linux
Linux0.11 文件打开open函数(五)
Linux0.11 文件打开open函数(五)
37 0
下一篇
无影云桌面