Linux-文件I/O

创客学院

Linux下支持两种IO接口

1、文件IO(open/close/read/write)——POSIX标准
2、标准IO(fopen/fclose/fread/fwrite)——ANSI标准

两者为相互依赖的关系

概念

对设备(如硬盘、内存、网卡、键盘)中的数据进行读写
input、output

IO作用

Linux“下一切皆文件”,磁盘、硬件、网络设备等都被虚拟成了相应的文件
Linux对这些文件的操作都通过IO接口来完成的。

Linux内核中IO操作的实现机制

Linux系统内部会将文件系统,设备驱动、网络协议等复杂的操作实现好,对应用层只提供简单的应用接口,即API(application interface)应用接口

一般计算机分三层——硬件-操作系统-APP

Linux内部的VFSC(虚拟文件系统层)层对不同文件系统,设备驱动、网络协议等接口做了标准化,这样在应用层就可以通过统一的接口操作不同的设备。
驱动——直接读写硬盘
在一个操作系统下,文件系统的种类也可以很多

华清创客学院

概念:

一组相关数据的集合

文件类型:

7种

  • 常规文件 r

  • 目录文件 d

  • 字符设备文件 c

  • 块设备文件 b

  • 管道文件 p

  • 套接字文件 s

  • 符号链接文件 I

强调:操作系统不同,所支持的文件类型也不同

标准I/O

介绍

标准I/O由ANSI C标准定义

主流操作系统上都实现了C库

标准I/O通过缓冲机制减少系统调用,实现更高的效率

无缓冲

有缓冲

标准I/O-流

FILE

标准IO用一个结构体类型来存放打开的文件的相关信息

标准I/O的所有操作都是围绕FILE来进行

流(stream)

FILE又被称为流(stream)

文本流/二进制流

  • Windows

二进制流:换行符 ←→ '\n'

文本流:换行符 ←→ '\r'\n'

  • Linux

换行符 ←→ '\n'

标准I/O-流的缓冲类型

  • 全缓冲

当流的缓冲区无数据或无空间时才执行实际I/O操作

  • 行缓冲

当在输入和输出中遇到换行符('\n')时,进行I/O操作

当流和一个终端关联时,典型的行缓冲

  • 无缓冲

数据直接写入文件,流不进行缓冲

标准I/O-stdin,stdout,stderr

标准I/O-打开流

下列函数可用于打开一个标准I/O流:

FILE *fopen(const char *path,const char *mode);

成功时返回流指针;出错时返回NULL

// 函数原型  
FILE *fopen(const char *path, const char *mode);  
  
// 参数解释:  
// path: 要打开的文件的路径名(包括文件名)。  
// mode: 打开文件的模式,例如 "r" 表示读取,"w" 表示写入,"a" 表示追加等。  
  
// 返回值:  
// 如果文件成功打开,返回指向该文件的 FILE 指针。  
// 如果文件打开失败,返回 NULL。

标准I/O-fopen-mode参数

标准I/O-fopen-示例

标准I/O-fopen-新建文件权限

  • fopen()创建的文件访问权限是0666(rw-rw-rw-)

  • Linux系统中umask设定会影响文件的访问权限,其规则为(0666 &~umask)

  • 用户可以通过umask函数修改相关设定

  • 如果希望umask不影响文件访问权限,该如何设定?

标准I/O-处理错误信息

extern int errno;
void perror(const char *s);
char *strerror(int errno);
  • errno 存放错误号

  • perror 先输出字符串s,再输出错误号对应的错误信息

  • strerror 根据错误号返回对应的错误信息

标准I/O-处理错误信息-示例1

标准I/O-处理错误信息-示例2

标准I/O-关闭流

int fclose(FILE *stream);
  • fclose()调用成功返回0,失败返回EOF,并设置errno

  • 流关闭时自动刷新缓冲中的数据并释放缓冲区

  • 当一个程序正常终止时,所有打开的流都会被关闭

  • 流一旦关闭后就不能执行任何操作

标准I/O-思考和练习

程序中能够打开的文件或文件或流的个数有限制,如何测试?

思路:循环打开流,成功则计数器累加,直到出错为止

答案:1021 + stdin + stdout + stderr = 1024

在大多数Unix和Linux系统中,每个进程默认都有一个文件描述符表,该表的大小通常限制为1024个条目。这些条目用于跟踪进程打开的所有文件、套接字和其他I/O资源。
  • stdin(标准输入)通常是文件描述符0。

  • stdout(标准输出)通常是文件描述符1。

  • stderr(标准错误)通常是文件描述符2。

因此,如果您尝试打开1021个额外的文件或资源,并且已经使用了stdin、stdout和stderr,那么总共会有1024个文件描述符被使用,这达到了默认限制。

请注意,这个限制是可以通过系统配置进行调整的。例如,在Linux上,可以使用ulimit -n命令来查看或设置这个限制。在某些系统上,这个限制可能更高或更低,取决于系统管理员的设置和硬件资源。

如果您的程序在尝试打开第1022个文件时失败,并报告达到了打开文件的限制,那么这通常意味着您的程序或系统已经达到了文件描述符的限制。此时,您可能需要调整系统配置以增加这个限制,或者优化您的程序以减少同时打开的文件数量。
#include <stdio.h>  
#include <stdlib.h>  //这个头文件包含了各种常用的函数,如内存分配(malloc、calloc、realloc、free)、程序终止(exit、abort)、随机数生成(rand、srand)等。
#include <string.h>  //这个头文件包含了处理字符串的函数,如复制字符串(strcpy、strncpy)、连接字符串(strcat、strncat)、比较字符串(strcmp、strncmp)、查找字符串中的字符(strchr、strrchr)等。
#include <errno.h>  //这个头文件定义了全局变量errno,它用于在调用系统调用或库函数出错时存储错误码。errno的值可以通过特定的错误码宏(如ENOENT、EACCES等)来检查,以确定发生了哪种错误。
#include <unistd.h>  //这个头文件主要包含了POSIX操作系统API,它提供了对操作系统服务的访问,如进程管理(fork、exec系列函数)、文件I/O(read、write、lseek)、进程间通信(管道、信号、共享内存等)、用户账户控制(getuid、setuid等)。
  
#define MAX_FILES_TO_OPEN 1000  // 假设我们最多尝试打开1000个文件  
#define BASE_FILENAME "file"    // 基础文件名  
#define FILE_EXTENSION ".txt"   // 文件扩展名  
#define BUFFER_SIZE 1024        // 足够的缓冲区大小来构造文件名  
  
int main() {  
    FILE *fp;  
    int file_no;  
    int success_count = 0;      // 记录成功打开的文件数  
    char filename[BUFFER_SIZE];  
  
    // 创建临时文件  
    for (file_no = 1; file_no <= MAX_FILES_TO_OPEN; ++file_no) {  
        snprintf(filename, BUFFER_SIZE, "%s%d%s", BASE_FILENAME, file_no, FILE_EXTENSION);  
        fp = fopen(filename, "w");  // 以写模式打开文件来创建它  
        if (fp != NULL) {  
            fclose(fp);  // 关闭文件,此时文件已经被创建  
        } else {  
            perror("Failed to create temporary file");  
            return EXIT_FAILURE;  
        }  
    }  
  
    // 现在尝试打开这些文件  
    for (file_no = 1; file_no <= MAX_FILES_TO_OPEN; ++file_no) {  
        snprintf(filename, BUFFER_SIZE, "%s%d%s", BASE_FILENAME, file_no, FILE_EXTENSION);  
        fp = fopen(filename, "r");  // 以只读方式打开文件  
        if (fp != NULL) {  
            // 如果文件成功打开,则增加成功计数并关闭文件  
            success_count++;  
            fclose(fp);  
            printf("Successfully opened file: %s\n", filename);  
        } else {  
            // 如果文件打开失败,检查错误原因  
            if (errno == EMFILE || errno == ENFILE) {  
                printf("Reached the limit of open files: errno = %d\n", errno);  
            } else {  
                printf("Failed to open file: %s, errno = %d\n", filename, errno);  
            }  
            break;  // 退出循环  
        }  
    }  
  
    // 清理临时文件  
    for (file_no = 1; file_no <= MAX_FILES_TO_OPEN; ++file_no) {  
        snprintf(filename, BUFFER_SIZE, "%s%d%s", BASE_FILENAME, file_no, FILE_EXTENSION);  
        remove(filename);  // 删除文件  
    }  
  
    printf("Successfully opened %d files.\n", success_count);  
  
    return 0;  
}

标准I/O-读写流

流支持不同的读写方式:

  • 读写一个字符:fgetc()/fputc()一次读/写一个字符

  • 读写一行:fgets()和fputs()一次读/写一行

  • 读写若干个对象:fread()/fwrite()每次读/写若干个对象,而每个对象具有相同的长度

标准I/O-按字符输入

下列函数用来输入一个字符:

#include<stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
  • 成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1)

  • getchar()等同于fgetc(stdin)

标准I/O-fgetc-示例1

标准I/O-fgetc-示例2

这段代码的主要功能是统计并输出给定文件的大小(以字节为单位)。它接受一个命令行参数(文件名),然后打开一个文件,逐个字符地读取文件内容,并计数。最后,输出文件的总字节数。

// 函数:统计文件大小  
#include<stdio.h>  
  
int main(int argc, const char *argv[])  
{  
    /*  
    // 注释掉的代码,用于从标准输入(stdin)读取一个字符并打印  
    int ch;  
    ch = fgetc(stdin); // 从标准输入读取一个字符  
    printf("%c\n",ch); // 打印读取到的字符  
    */  
  
    FILE *fp; // 定义文件指针fp  
    int ch, count = 0; // 定义字符变量ch和计数变量count,初始化为0  
  
    // 判断是否成功打开文件,如果打开失败则输出错误信息并返回-1  
    if((fp = fopen(argv[1],"r")) == NULL) {  
        perror("fopen"); // 输出fopen函数调用的错误信息  
        return -1; // 返回-1表示程序异常退出  
    }  
  
    // 循环读取文件内容,直到遇到文件结束符EOF  
    while((ch = fgetc(fp)) != EOF) {  
        count++; // 每读取一个字符,计数器加1  
    }  
  
    // 输出文件的总字节数  
    printf("total %d bytes\n", count);  
  
    // 关闭文件,并返回0表示程序正常退出  
    fclose(fp); // 关闭文件
    return 0;  
}

标准I/O-按字符输出

下列函数用来输出一个字符:

#include<stdio.h>
int fput(int c,FILE *stream);
int putc(int c,FILE *stream);
int putchar(int c);
  • 成功时返回写入的字符;出错时返回EOF(-1)

  • putchar()等同于fputc(c, stdout)

标准I/O-fputc-示例1

标准I/O-fputc-示例2

这段代码的主要功能是:接受一个命令行参数(文件名),然后以写入模式("w")打开这个文件。如果文件不存在,它会被创建。接着,程序会将小写英文字母从'a'到'z'逐个写入到这个文件中。如果文件打开失败,程序会输出错误信息并返回-1。最后,程序正常退出时返回0。

#include<stdio.h>  
  
int main(int argc, const char *argv[])  
{  
	FILE *fp; // 定义文件指针fp  
	int ch;   // 定义字符变量ch,用于存储要写入文件的字符  
  
	// 以写入模式("w")打开文件,如果文件不存在则创建它  
	// argv[1]是命令行参数中的文件名  
	if((fp = fopen(argv[1],"w")) == NULL)  
	{  
		perror("fopen"); // 如果打开文件失败,则调用perror函数输出错误信息  
		return -1;       // 并返回-1表示程序异常退出  
	}  
  
	// 使用for循环,从字符'a'开始到字符'z'结束,逐个写入文件  
	for(ch = 'a'; ch <= 'z'; ch++)  
	{  
		fputc(ch, fp); // 使用fputc函数将字符ch写入到文件指针fp所指向的文件中  
	}  
  
	// 关闭文件,释放文件指针  
	fclose(fp);  
  
	return 0; // 程序正常执行完毕,返回0  
}

标准I/O-思考和练习

如何利用fgetc/fputc实现文件的复制?

  1. 通过命令行参数传递源文件和目标文件名

  2. 通过fgetc返回值判断是否读到文件末尾

这段代码的功能是复制一个文件的内容到另一个文件。它接受两个命令行参数:第一个是源文件的名称,第二个是目标文件的名称。程序首先检查是否提供了足够的参数,然后尝试打开源文件和目标文件。如果任一文件打开失败,程序会输出错误信息并退出。如果文件打开成功,程序会逐个字符地从源文件读取内容,并写入到目标文件中。最后,程序关闭这两个文件并正常退出。

#include<stdio.h>  
  
int main(int argc, const char *argv[])  
{  
	FILE *fps, *fpd; // 定义两个文件指针,fps指向源文件,fpd指向目标文件  
	int ch;           // 定义一个整型变量,用于存储从源文件读取的字符  
  
	// 检查命令行参数数量是否足够  
	if(argc < 3)  
	{  
		printf("Usage : %s <src_file> <dst_file>\n",argv[0]);  
		// 如果参数不足,输出使用方法,并返回-1表示程序异常退出  
		return -1;  
	}  
  
	// 以只读模式打开源文件  
	if((fps = fopen(argv[1],"r")) == NULL){  
		perror("fopen src file"); // 如果打开源文件失败,输出错误信息  
		return -1;                 // 并返回-1表示程序异常退出  
	}  
  
	// 以写入模式打开目标文件  
	if((fpd = fopen(argv[2],"w")) == NULL){  
		perror("fopen dst file"); // 如果打开目标文件失败,输出错误信息  
		return -1;                 // 并返回-1表示程序异常退出  
	}  
  
	// 循环读取源文件的内容,直到文件结束(EOF)  
	while((ch = fgetc(fps)) != EOF)  
	{  
		fputc(ch,fpd); // 将读取到的字符写入目标文件  
	}  
  
	// 关闭源文件  
	fclose(fps);  
  
	// 关闭目标文件  
	fclose(fpd);  
  
	// 程序正常结束,返回0  
	return 0;  
}

后续待更新……

  • 10
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值