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,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

相关文章
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
777 34
|
自然语言处理 监控 Linux
Linux 内核源码分析---proc 文件系统
`proc`文件系统是Linux内核中一个灵活而强大的工具,提供了一个与内核数据结构交互的接口。通过本文的分析,我们深入探讨了 `proc`文件系统的实现原理,包括其初始化、文件的创建与操作、动态内容生成等方面。通过对这些内容的理解,开发者可以更好地利用 `proc`文件系统来监控和调试内核,同时也为系统管理提供了便利的工具。
815 16
|
Linux C语言 网络架构
Linux的基础IO内容补充-FILE
而当我们将运行结果重定向到log.txt文件时,数据的刷新策略就变为了全缓冲,此时我们使用printf和fwrite函数打印的数据都打印到了C语言自带的缓冲区当中,之后当我们使用fork函数创建子进程时,由于进程间具有独立性,而之后当父进程或是子进程对要刷新缓冲区内容时,本质就是对父子进程共享的数据进行了修改,此时就需要对数据进行写时拷贝,至此缓冲区当中的数据就变成了两份,一份父进程的,一份子进程的,所以重定向到log.txt文件当中printf和fwrite函数打印的数据就有两份。此时我们就可以知道,
417 0
|
存储 Linux Shell
Linux的基础IO
那么,这里我们温习一下操作系统的概念我们在Linux平台下运行C代码时,C库函数就是对Linux系统调用接口进行的封装,在Windows平台下运行C代码时,C库函数就是对Windows系统调用接口进行的封装,这样做使得语言有了跨平台性,也方便进行二次开发。这就是因为在根本上操作系统确实像银行一样,并不完全信任用户程序,因为直接开放底层资源(如内存、磁盘、硬件访问权限)给用户程序会带来巨大的风险。所以就向银行一样他的服务是由工作人员隔着一层玻璃,然后对顾客进行服务的。
196 0
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
973 15
|
Linux API C语言
Linux基础IO
Linux基础IO操作是系统管理和开发的基本技能。通过掌握文件描述符、重定向与管道、性能分析工具、文件系统操作以及网络IO命令等内容,可以更高效地进行系统操作和脚本编写。希望本文提供的知识和示例能帮助读者更深入地理解和运用Linux IO操作。
376 14
|
Ubuntu Unix Linux
Linux网络文件系统NFS:配置与管理指南
NFS 是 Linux 系统中常用的网络文件系统协议,通过配置和管理 NFS,可以实现跨网络的文件共享。本文详细介绍了 NFS 的安装、配置、管理和常见问题的解决方法,希望对您的工作有所帮助。通过正确配置和优化 NFS,可以显著提高文件共享的效率和安全性。
4682 7
|
存储 运维 监控
Linux--深入理与解linux文件系统与日志文件分析
深入理解 Linux 文件系统和日志文件分析,对于系统管理员和运维工程师来说至关重要。文件系统管理涉及到文件的组织、存储和检索,而日志文件则记录了系统和应用的运行状态,是排查故障和维护系统的重要依据。通过掌握文件系统和日志文件的管理和分析技能,可以有效提升系统的稳定性和安全性。
470 7
|
安全 Linux 数据安全/隐私保护
深入Linux操作系统:文件系统和权限管理
在数字世界的海洋中,操作系统是连接用户与硬件的桥梁,而Linux作为其中的佼佼者,其文件系统和权限管理则是这座桥梁上不可或缺的结构。本文将带你探索Linux的文件系统结构,理解文件权限的重要性,并通过实际案例揭示如何有效地管理和控制这些权限。我们将一起航行在Linux的命令行海洋中,解锁文件系统的奥秘,并学习如何保护你的数据免受不必要的访问。