Linux基础知识点(三-文件I/O)

目录

一、文件操作(C标准库)

1.1 fopen函数

1.2 fread函数

1.3 fwrite函数

1.4 fclose函数

1.5 fflush函数

1.6 fseek函数

二、文件操作(系统调用)

 2.1 open函数

 2.2 read函数

 2.3 write函数

 2.4 close函数

2.5 lseek函数

三、 文件描述符fd

3.1 文件描述符介绍

3.2 文件描述符分配规则

四、文件描述符相关操作

4.1 重定向

​4.2 dup和dup2函数


一、文件操作(C标准库)

1.1 fopen函数

fopen库函数用于打开或创建文件,返回相应的文件流。

#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
  • pathname参数用于指定要打开或创建的文件名。

  • mode参数用于指定文件的打开方式,注意该参数是一个字符串,输入时需要带双引号:

  • "r":以只读方式打开,文件指针位于文件的开头。

  • "r+":以读和写的方式打开,文件指针位于文件的开头。

  • "w":以写的方式打开,不管原文件是否有内容都把原内容清空掉,文件指针位于文件的开头。

  • "w+":同上,不过当文件不存在时,前面的“w”模式会返回错误,而此处的“w+”则会创建新文件。

  • "a":以追加内容的方式打开,若文件不存在会创建新文件,文件指针位于文件的末尾。与“w+”的区别是它不会清空原文件的内容而是追加。

  • "a+":以读和追加的方式打开,其它同上。

  • fopen的返回值是FILE类型的文件文件流,当它的值不为NULL时表示正常,后续的fread、fwrite等函数可通过文件流访问对应的文件。

1.2 fread函数

 fread库函数用于从文件流中读取数据。

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

stream是使用fopen打开的文件流,fread通过它指定要访问的文件,它从该文件中读取nmemb项数据,每项的大小为size,读取到的数据会被存储在ptr指向的数组中。fread的返回值为成功读取的项数(项的单位为size)。

1.3 fwrite函数

 fwrite库函数用于把数据写入到文件流

#include <stdio.h>
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);

它的操作与fread相反,把ptr数组中的内容写入到stream文件流,写入的项数为nmemb,每项大小为size,返回值为成功写入的项数(项的单位为size)。

1.4 fclose函数

fclose库函数用于关闭指定的文件流,关闭时它会把尚未写到文件的内容都写出。因为标准 库会对数据进行缓冲,所以需要使用fclose来确保数据被写出。它的函数原型如下:

#include <stdio.h>
int fclose(FILE* stream);

1.5 fflush函数

fflush函数用于把尚未写到文件的内容立即写出。常用于确保前面操作的数据被写 入到磁盘上。fclose函数本身也包含了fflush的操作。fflush的函数原型如下:

#include <stdio.h>
int fflush(FILE *stream);

1.6 fseek函数

fseek函数用于设置下一次读写函数操作的位置。它的函数原型如下:

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);

其中的offset参数用于指定位置,whence参数则定义了offset的意义,whence的可取值如下:

  • SEEK_SET:offset是一个绝对位置。

  • SEEK_END:offset是以文件尾为参考点的相对位置。

  • SEEK_CUR:offset是以当前位置为参考点的相对位置。

#include <stdio.h>
#include <string.h>

//要写入的字符串
const char buf[] = "filesystem_test:Hello World!\n";
//文件指针
FILE *fp;
char str[100];

int main(void)
{ 
    //打开一个文件,正常返回文件指针,异常返回NULL
    fp = fopen("filesystem_test.txt", "w+"); 
    if(NULL == fp){
        printf("Fail to Open File\n");
        return 0;
    }
    //将buf的内容写入文件,每次写入1个字节,总长度由strlen给出
    fwrite(buf, 1, strlen(buf), fp); 

    //写入InsertString,每次写入1个字节,总长度由strlen给出
    fwrite("InsertString\n", 1, strlen("InsertString\n"),fp);

    //把缓冲区的数据立即写入文件
    fflush(fp);

    //此时的文件位置指针位于文件的结尾处,使用fseek函数使文件指针回到文件头
    fseek(fp, 0, SEEK_SET);

    //从文件中读取内容到str中,每次读取100个字节,读取1次
    fread(str, 100, 1, fp);
    printf("File content:\n%s \n", str);
    fclose(fp);                          
    return 0;
}

二、文件操作(系统调用)

 Linux提供的文件操作系统调用常用的有open、write、read、lseek、close等,C标准库中的操作文件函数可以认为是对系统调用的封装,方便二次开发。

 2.1 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);

Linux使用open函数来打开文件,并返回该文件对应的文件描述符。函数参数说明如下:

  • pathname:要打开或创建的文件名;

  • flag:指定文件的打开方式,具体有以下参数,见下表 flag参数值。

标志位

含义

O_RDONLY

以只读的方式打开文件,该参数与O_WRONLY和O_RDWR只能三选一

O_WRONLY

以只写的方式打开文件

O_RDWR

以读写的方式打开文件

O_CREAT

创建一个新文件

O_APPEND

将数据写入到当前文件的结尾处

O_TRUNC

如果pathname文件存在,则清除文件内容

 C库函数fopen的mode参数与系统调用open的flags参数有如下表中的等价关系。

r

O_RDONLY

w

O_WRONLY | O_CREAT | O_TRUNC

a

O_WRONLY | O_CREAT | O_APPEND

r+

O_RDWR

w+

O_RDWR | O_CREAT | O_TRUNC

a+

O_RDWR | O_CREAT | O_APPEND

  • mode:当open函数的flag值设置为O_CREAT时,必须使用mode参数来设置文件与用户相关的权限。mode可用的权限如下表所示,表中各个参数可使用 " | "来组合。

  • 返回值:成功返回文件描述符fd,失败返回-1。

\

标志位

含义

当前用户

S_IRUSR

用户拥有读权限

\

S_IWUSR

用户拥有写权限

\

S_IXUSR

用户拥有执行权限

\

S_IRWXU

用户拥有读、写、执行权限

当前用户组

S_IRGRP

当前用户组的其他用户拥有读权限

\

S_IWGRP

当前用户组的其他用户拥有写权限

\

S_IXGRP

当前用户组的其他用户拥有执行权限

\

S_IRWXG

当前用户组的其他用户拥有读、写、执行权限

其他用户

S_IROTH

其他用户拥有读权限

\

S_IWOTH

其他用户拥有写权限

\

S_IXOTH

其他用户拥有执行权限

\

S_IRWXO

其他用户拥有读、写、执行权限

 2.2 read函数

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

read函数用于从文件中读取若干个字节的数据,保存到数据缓冲区buf中,并返回实际读取的字节数,具体函数参数如下:

  • fd:文件对应的文件描述符,可以通过open函数获得。

  • buf:指向数据缓冲区的指针。

  • count:读取多少个字节的数据。

 2.3 write函数

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

write函数用于往文件写入内容,并返回实际写入的字节长度,具体函数参数如下:

  • fd:文件对应的文件描述符,可以通过fopen函数获得。

  • buf:指向数据缓冲区的指针;

  • count:往文件中写入多少个字节

 2.4 close函数

#include <unistd.h>
int close(int fd);

当我们完成对文件的操作之后,想要关闭该文件,可以调用close函数,来关闭该fd文件描述符对应的文件。

2.5 lseek函数

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

lseek函数可以用与设置文件指针的位置,并返回文件指针相对于文件头的位置。

它的用法与flseek一样,其中的offset参数用于指定位置,whence参数则定义了offset的意义,whence的可取值如下:

  • SEEK_SET:offset是一个绝对位置。

  • SEEK_END:offset是以文件尾为参考点的相对位置。

  • SEEK_CUR:offset是以当前位置为参考点的相对位置。

三、 文件描述符fd

3.1 文件描述符介绍

对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负数,当打开一个现有文件或则创建一个新文件时,内核向进程返回一个文件描述符

Linux进程会默认打开3个文件描述符,分别为标准输入0(stdin)、标准输出1(stdout)、标准错误2(stderror),其对应的物理设备一般为键盘、显示器、显示器。

文件描述符就是从0开始的非负数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,所以操作系统用file结构体来描述文件,表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针变量file,指向files_struct结构体,该结构体的成员变量有一个指针数组fd_array[],每个元素都是一个指向file结构体的指针!本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。

3.2 文件描述符分配规则

在fd_array数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    close(0);
    int fd1 = open("myfile1", O_RDONLY);
    if(fd1 < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    int fd2 = open("myfile2", O_RDONLY);
    if(fd1 < 0)
    {
        perror("open");
        return 1;
    }    
    close(fd1);
    close(fd2);
    return 0;
}

四、文件描述符相关操作

4.1 重定向

如果我们把标准输出的文件描述符1关闭,然后在打开一个文件,使用printf函数会发生什么现象?

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
    close(1);
    int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
    if(fd < 0){
    perror("open");
    return 1;
    }
    printf("fd: %d\n", fd);
    fflush(stdout);
    close(fd);
    exit(0);
}

我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中。这种现象叫做输出重定向。常见的重定向有: >, >>, <。

产生这种现象的原因是因为我们把标准输出的文件描述符1关闭了,所以新打开的文件所获得的文件描述符就是1,往1号文件描述符写的内容都会写到了myfile中,而printf函数是往1号文件描述符中写内容,所以printf里的内容写到了myfile中,而不是写到标准输出中。

 4.2 dup和dup2函数

dup和dup2函数都可以用来复制一个现有的文件描述符,返回值为复制的新文件描述符。

#include <unistd>
int dup(int fd);
int dup2(int fd1, int fd2);

其中dup返回的新文件描述符一定是当前可用文件描述符中的最小值, 而dup2,可以fd2参数指定新文件描述符的值,如果指定的文件描述符的值已经被打开,则会将其关闭,如果fd1等于fd2,则dup2返回fd2,而不会去关闭它。

 

相关推荐

  1. Linux 基础IO

    2023-12-22 12:32:04       28 阅读
  2. Linux文件处理知识

    2023-12-22 12:32:04       27 阅读
  3. 2.Linux文件IO基础

    2023-12-22 12:32:04       44 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-22 12:32:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-22 12:32:04       100 阅读
  3. 在Django里面运行非项目文件

    2023-12-22 12:32:04       82 阅读
  4. Python语言-面向对象

    2023-12-22 12:32:04       91 阅读

热门阅读

  1. 华纳云:怎么用python实现进程,线程和协程

    2023-12-22 12:32:04       62 阅读
  2. AI write rust 2

    2023-12-22 12:32:04       60 阅读
  3. rust热门前后端框架

    2023-12-22 12:32:04       62 阅读
  4. 密钥盐技术简介及作用(含示例)

    2023-12-22 12:32:04       65 阅读
  5. 力扣61. 旋转链表

    2023-12-22 12:32:04       63 阅读
  6. Django5.0发布

    2023-12-22 12:32:04       54 阅读