Linux之基础IO文件系统讲解(上)

简介: 在C语言中,读文件和写文件是常见的操作,用于从磁盘读取数据到内存或将数据从内存写入磁盘文件中。这些操作需要使用标准库中的文件I/O函数。下面我将详细解释如何在C语言中进行读文件和写文件操作,并举例说明。

回顾C语言读写文件

在C语言中,读文件和写文件是常见的操作,用于从磁盘读取数据到内存或将数据从内存写入磁盘文件中。这些操作需要使用标准库中的文件I/O函数。下面我将详细解释如何在C语言中进行读文件和写文件操作,并举例说明。

读文件操作

在C语言中,读取文件的过程涉及以下步骤:

  1. 打开文件:使用fopen()函数打开一个文件,该函数需要指定文件名和打开模式("r"表示只读模式)。
  2. 读取数据:使用fread()fgets()函数从打开的文件中读取数据。
  3. 关闭文件:使用fclose()函数关闭已经打开的文件。

下面是一个读取文件的简单示例:

#include <stdio.h>
int main() {
    FILE *file = fopen("example.txt", "r");  // 打开文件
    if (file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    char buffer[100];
    while (fgets(buffer, sizeof(buffer), file) != NULL) {
        printf("%s", buffer);  // 打印读取的内容
    }
    fclose(file);  // 关闭文件
    return 0;
}

写文件操作

写文件的过程如下:

  1. 打开文件:使用fopen()函数打开一个文件,需要指定文件名和打开模式("w"表示写入模式,如果文件不存在则创建,如果存在则清空原内容)。
  2. 写入数据:使用fwrite()fprintf()函数将数据写入已打开的文件。
  3. 关闭文件:使用fclose()函数关闭已经打开的文件。

以下是一个写文件的简单示例:

#include <stdio.h>
int main() {
    FILE *file = fopen("output.txt", "w");  // 打开文件
    if (file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    char data[] = "这是要写入文件的数据。\n";
    fprintf(file, "%s", data);  // 写入数据
    fclose(file);  // 关闭文件
    return 0;
}

输出信息到显示器的方法

在C语言中,你可以使用多种方法将信息输出到显示器(终端或控制台)。以下是几种常见的方法:

  1. printf函数:printf 是C语言标准库中的函数,用于格式化输出到标准输出(通常是终端或控制台)。它允许你使用格式字符串指定输出的样式和内容。
    示例:
#include <stdio.h>
int main() {
    printf("Hello, world!\n");
    int num = 42;
    printf("The number is: %d\n", num);
    return 0;
}
  1. puts函数:puts 函数用于输出一个字符串,并自动添加换行符。它不支持格式化输出。
    示例:
#include <stdio.h>
int main() {
    puts("Hello, world!");
    return 0;
}
  1. putc和putchar函数:putc 函数用于输出一个字符,而 putchar 函数用于输出一个字符并添加换行符。
    示例:
#include <stdio.h>
int main() {
    putc('H', stdout);
    putc('i', stdout);
    putchar('!');
    putchar('\n');
    return 0;
}
  1. fprintf函数:fprintf 函数允许你将格式化的输出写入到文件流中,包括标准输出流。
    示例:
#include <stdio.h>
int main() {
    FILE *file = stdout;  // 标准输出流
    fprintf(file, "Hello, world!\n");
    return 0;
}

这些方法可以根据你的需求和偏好来选择。通常情况下,你会使用 printf 来输出信息到控制台,因为它提供了丰富的格式化选项,使输出更加灵活和易读。

stdin & stdout & stderr

在C语言中,stdinstdoutstderr是三个标准的I/O流,用于处理标准输入、标准输出和标准错误输出。它们是在标准库中预定义的文件指针,在C/C++程序中是默认打开的

  1. stdin:
  • stdin 是标准输入流,用于从用户(或其他来源)读取输入数据。通常情况下,stdin 关联着键盘输入,但在重定向或管道等情况下,它可以来自其他来源。
  1. 示例:
#include <stdio.h>
int main() {
    int num;
    printf("请输入一个数字:");
    scanf("%d", &num);  // 从标准输入读取一个数字
    printf("你输入的数字是:%d\n", num);
    return 0;
}
  1. stdout:
  • stdout 是标准输出流,用于将程序的输出信息显示给用户。通常情况下,stdout 关联着终端或控制台。
  1. 示例:
#include <stdio.h>
int main() {
    printf("Hello, world!\n");  // 将信息输出到标准输出
    return 0;
}
  1. stderr:
  • stderr 是标准错误输出流,用于输出错误信息或警告信息。通常情况下,stderr 也关联着终端或控制台。与 stdout 不同的是,stderr 通常不会被重定向,这样可以确保错误信息能够及时显示给用户。
  1. 示例:
#include <stdio.h>
int main() {
    fprintf(stderr, "这是一个错误消息。\n");  // 将错误消息输出到标准错误输出
    return 1;  // 返回非零值表示程序出错
}

总之,stdinstdoutstderr 是在C语言中处理输入和输出的标准流。通过使用它们,你可以实现用户输入的处理、程序输出的显示以及错误消息的输出。

总结

r Open text file for reading.
  The stream is positioned at the beginning of the file.
r+ Open for reading and writing.
  The stream is positioned at the beginning of the file.
w Truncate(缩短) file to zero length or create text file for writing.
  The stream is positioned at the beginning of the file.
w+ Open for reading and writing.
  The file is created if it does not exist, otherwise it is truncated.
  The stream is positioned at the beginning of the file.
a Open for appending (writing at end of file).
  The file is created if it does not exist.
  The stream is positioned at the end of the file.
a+ Open for reading and appending (writing at end of file).
  The file is created if it does not exist. The initial file position
  for reading is at the beginning of the file,
  but output is always appended to the end of the file.

系统文件IO

在Linux中,可以通过调用系统提供的系统调用接口来进行文件的读写操作。系统调用是用户程序与操作系统之间的接口,允许用户程序直接与操作系统内核进行通信。下面是使用系统调用进行文件读写的简单示例,其中主要涉及到的系统调用包括 openreadwriteclose。其实,大多数编程语言的标准库中的 I/O 函数实际上会在底层调用操作系统提供的系统 I/O 接口。这是因为底层的文件操作、网络通信等需要与操作系统内核交互,而不同的操作系统可能在 I/O 处理方面有不同的实现方式。因此,编程语言的标准库提供了一种抽象层,使开发人员无需关注不同操作系统的细节,而可以使用统一的 API 进行文件和数据的读写。

这种抽象层的使用使得跨平台开发变得更加容易,因为开发人员可以在不同的操作系统上使用相同的函数调用,而不必关心操作系统的差异。

文件读取的示例:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main() {
    int fd = open("example.txt", O_RDONLY);  // 打开文件以只读模式
    if (fd == -1) {
        perror("无法打开文件");
        return 1;
    }
    char buffer[100];
    ssize_t bytesRead = read(fd, buffer, sizeof(buffer));  // 从文件中读取数据
    if (bytesRead == -1) {
        perror("读取文件错误");
        close(fd);
        return 1;
    }
    printf("读取的内容:%.*s", (int)bytesRead, buffer);
    close(fd);  // 关闭文件
    return 0;
}

文件写入的示例:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main() {
  umask(0);
    int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);  // 打开文件以写入模式(如果不存在则创建,存在则清空)
    if (fd == -1) {
        perror("无法打开文件");
        return 1;
    }
    char data[] = "这是要写入文件的数据。\n";
    ssize_t bytesWritten = write(fd, data, sizeof(data) - 1);  // 写入数据
    if (bytesWritten == -1) {
        perror("写入文件错误");
        close(fd);
        return 1;
    }
    printf("成功写入 %zd 字节数据\n", bytesWritten);
    close(fd);  // 关闭文件
    return 0;
}

这两个示例中,我们使用了 open 函数打开文件,分别用 readwrite 函数进行读取和写入操作,最后使用 close 函数关闭文件。需要注意的是,系统调用返回的错误码通常为负值,因此检查返回值是否小于0 来判断是否发生了错误。另外,perror 函数可以打印出对应的错误信息。

IO接口介绍

open 是一个在 Unix/Linux 系统中用于打开文件的系统调用接口。它是进行文件操作的重要接口之一,用于打开文件以进行读取、写入或其他操作。下面是关于 open 函数的详细介绍:

函数原型:

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

参数说明:

  • pathname:要打开的文件路径。
  • flags:打开文件的标志,用于指定打开模式和行为。这些标志可以使用按位或运算组合起来。
  • mode:当使用 O_CREAT 标志时,指定新文件的权限。这个参数通常需要八进制形式的权限值,如 0644

返回值:

  • 成功时,返回文件描述符(一个非负整数),用于以后的文件操作。
  • 失败时,返回 -1,并设置全局变量 errno 表示错误类型。

常用的 flags 参数:

  • O_RDONLY:只读模式打开文件。
  • O_WRONLY:只写模式打开文件。
  • O_RDWR:读写模式打开文件。
  • O_CREAT:如果文件不存在,则创建文件。
  • O_TRUNC:如果文件已存在,在打开时清空文件内容。
  • O_APPEND:在写入时追加到文件末尾。
  • O_EXCL:与 O_CREAT 一起使用,如果文件已存在,返回错误。
  • O_NONBLOCK:以非阻塞模式打开文件,读取和写入不会阻塞进程。
  • O_SYNCO_DSYNC:在每次写入操作后进行物理磁盘同步。

示例:

#include <stdio.h>
#include <fcntl.h>
int main() {
    umask(0);
    int fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("无法打开文件");
        return 1;
    }
    // 打开文件后可以进行写入操作
    close(fd);  // 关闭文件
    return 0;
}

在这个示例中,open 函数以写入模式打开一个文件,并在文件不存在时创建它。如果文件打开成功,返回的文件描述符 fd 可以用于后续的文件操作,最后通过 close 函数关闭文件。

writereadcloselseek)是在Unix/Linux系统中用于文件操作的常用系统调用。

  1. write:
  • 函数原型:ssize_t write(int fd, const void *buf, size_t count);
  • 作用:用于将数据从缓冲区写入文件。
  • 参数:
  • fd:文件描述符,指示要写入的文件。
  • buf:要写入的数据的缓冲区。
  • count:要写入的字节数。
  • 返回值:返回实际写入的字节数,如果返回值为 -1,则表示出错。
  1. read:
  • 函数原型:ssize_t read(int fd, void *buf, size_t count);
  • 作用:从文件中读取数据到缓冲区。
  • 参数:
  • fd:文件描述符,指示要读取的文件。
  • buf:存储读取数据的缓冲区。
  • count:要读取的字节数。
  • 返回值:返回实际读取的字节数,如果返回值为 0,则表示已到达文件末尾;如果为 -1,则表示出错。
  1. close:
  • 函数原型:int close(int fd);
  • 作用:关闭打开的文件。
  • 参数:fd:文件描述符。
  • 返回值:成功返回 0,出错返回 -1。
  1. lseek:
  • 函数原型:off_t lseek(int fd, off_t offset, int whence);
  • 作用:改变文件的当前偏移量,通常用于文件随机访问。
  • 参数:
  • fd:文件描述符。
  • offset:偏移量的值。
  • whence:基准位置,可以是 SEEK_SET(文件开头)、SEEK_CUR(当前位置)或 SEEK_END(文件末尾)。
  • 返回值:返回新的文件偏移量,出错返回 -1。

这些接口是在Unix/Linux环境中进行文件操作的基本系统调用。它们提供了对文件的基本读写和定位操作,是文件处理的核心操作之一。需要注意的是,在实际使用中应该进行错误检查以确保操作的正确性和可靠性。

系统调用接口和库函数的关系,一目了然

所以,可以认为,f*系列的函数,都是对系统调用的封装,方便二次开发

文件描述符fd

在Unix/Linux系统中,文件描述符(File Descriptor,通常缩写为 fd)是一个用于标识打开文件或其他I/O资源的整数。它是操作系统内核用来跟踪文件和I/O流的一种方式。文件描述符在C语言中通常用于标识和操作文件、套接字、管道等。

以下是关于文件描述符的一些重要概念:

  1. 标准文件描述符:
  • 在Unix/Linux系统中,有三个标准的文件描述符,分别为 0(标准输入)、1(标准输出)和 2(标准错误输出)。
  • 这些标准文件描述符通常与终端或控制台相关联,用于用户输入和程序输出。
  1. 非标准文件描述符:
  • 除了标准文件描述符外,系统还为每个打开的文件、套接字等分配一个唯一的文件描述符。
  • 非标准文件描述符是非负整数,可以用于标识和操作特定的I/O资源。
  1. 文件描述符的范围:
  • 通常情况下,文件描述符从0开始递增,但并不是所有的非负整数都是合法的文件描述符。
  • 每个进程都有一定的最大文件描述符限制,可以通过 ulimit 命令查看。

在C语言中,当使用系统调用如 openreadwrite 等打开或操作文件时,返回的文件描述符用于后续的文件操作。例如:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main() {
    char buf[1024];  // 用于存储读取的数据
    ssize_t s = read(0, buf, sizeof(buf));  // 从标准输入读取数据
    if (s > 0) {
        buf[s] = 0;  // 在读取的数据末尾添加字符串结束符
        write(1, buf, strlen(buf));  // 将读取的数据写入标准输出
        write(2, buf, strlen(buf));  // 将读取的数据写入标准错误输出
    }
    return 0;
}

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

相关文章
|
2月前
|
缓存 安全 Linux
Linux 五种IO模型
Linux 五种IO模型
|
25天前
|
存储 Linux 索引
Linux 下最主流的文件系统格式——ext
【9月更文挑战第8天】硬盘被划分为若干相同大小的块(Block),默认大小为4K,便于灵活管理文件数据。文件数据分散存放于这些块中,提高了数据添加、删除和插入的便利性。
|
2月前
|
编解码 Linux 程序员
深度探索Linux操作系统 —— 构建根文件系统2
深度探索Linux操作系统 —— 构建根文件系统
35 12
|
2月前
|
Linux Shell 网络安全
深度探索Linux操作系统 —— 构建根文件系统1
深度探索Linux操作系统 —— 构建根文件系统
35 6
|
2月前
|
存储 人工智能 数据管理
深入理解Linux操作系统之文件系统管理探索人工智能:从理论到实践的旅程
【8月更文挑战第30天】在探索Linux的无限可能时,我们不可避免地会遇到文件系统管理这一核心话题。本文将深入浅出地介绍Linux文件系统的基础知识、操作命令及高级技巧,帮助你更有效地管理和维护你的系统。从基础概念到实践应用,我们将一步步揭开Linux文件系统的神秘面纱。
|
2月前
|
存储 监控 Linux
|
2月前
|
存储 监控 Linux
|
21天前
|
存储 缓存 Linux
Linux文件系统的功能规划
【9月更文挑战第12天】本文通过类比图书馆,形象地解释了文件系统的组织形式和管理方法。首先,文件系统需按块存储文件,并设有索引区方便查找。其次,热点文件应有缓存层提高效率,文件需分类存储以便管理。最后,Linux内核需记录文件使用情况,通过文件描述符区分不同文件,确保文件操作准确无误。
|
2月前
|
算法 Linux 索引
Linux0.11 根文件系统挂载(四)
Linux0.11 根文件系统挂载(四)
18 0
|
2月前
|
存储 Linux 网络安全
【Azure 存储服务】如何把开启NFS 3.0协议的Azure Blob挂载在Linux VM中呢?(NFS: Network File System 网络文件系统)
【Azure 存储服务】如何把开启NFS 3.0协议的Azure Blob挂载在Linux VM中呢?(NFS: Network File System 网络文件系统)
下一篇
无影云桌面