Linux中的标准IO【上】
标准IO
- fopen()
- FILE * fopen(const char * restrict path, const char * restrict mode);
- 第一个参数表示被打开文件路径,第二个参数表示打开文件模式—模式不同,对同一个文件有不同的更改
- r和r+模式下不存在文件则也无法创建,其余模式若文件本就不存在可以先创建后读取
- const修饰用户无法修改对应参数
char *ptr = "abc"; ptr[0] = "x"; // 是否正确? // linux下不正确因为其是字符串常量因此不可用修改
- 成功后会返回一个FILE指针,如果失败则返回空指针并将errno进行设置
- errno在未被设置时是一个全局变量,出错后会将出错信息放在这个上面
- 补充两个函数—将errno的数字转换成message
- perror(“fopen:”)—报错系统具体信息,在串后加入出错信息
- char *strerror(int errno)—#include<string.h>
- fprintf(stderr, “fopen():%s\n”, strerror(errno));
- 辨析stderr报错和perror报错信息
- fprintf(stderr, “Usage…\n”);
- 这行代码使用了C语言标准库中的fprintf函数来向标准错误输出流(stderr)打印一条消息,消息内容为"Usage…"。通常,这种语句用于显示程序的使用方法或错误消息,以便用户可以了解如何正确使用程序或了解程序运行中的错误信息。在这种情况下,将消息输出到stderr流可以确保消息不会与程序的标准输出混淆,而是会被重定向到标准错误输出(通常是终端窗口)中。
- perror(“stat()”);
- 这行代码使用了C语言标准库中的perror函数,用于将最近一次发生的错误信息输出到标准错误流(stderr)中。在这个例子中,perror(“stat()”)将输出一个类似于"stat(): No such file or directory"的错误信息,指示最近一次执行的stat函数失败,并指明失败的原因为"文件或目录不存在"。
- 通常,perror函数在程序中用于处理系统调用或库函数失败的情况,以便向用户提供更有意义的错误信息。输出的错误信息中包含了错误代码和对应的错误信息,可以帮助程序员快速定位程序出错的原因。
- 两种报错方式的不同
- 输出位置不同:fprintf函数输出错误信息到标准错误流(stderr),而perror函数直接将错误信息输出到标准错误流中。
- 输出内容不同:fprintf函数可以输出任何指定格式的错误信息,包括自定义错误信息和变量值,而perror函数只输出最近一次系统调用或库函数失败的错误信息,通常包含错误代码和对应的错误信息。
- 方便程度不同:使用fprintf函数输出错误信息需要自己组织错误信息的格式和内容,而perror函数可以直接输出系统调用或库函数失败的错误信息,方便快捷。
- linux中stdin,stdout,stderr
- stdout, stdin, stderr的中文名字分别是标准输出,标准输入和标准错误
- 在Linux下,当一个用户进程被创建的时候,系统会自动为该进程创建三个数据流,也就是题目中所提到的这三个。那么什么是数据流呢(stream)?我们知道,一个程序要运行,需要有输入、输出,如果出错,还要能表现出自身的错误。这是就要从某个地方读入数据、将数据输出到某个地方,这就够成了数据流。
- 因此,一个进程初期所拥有的这么三个数据流,就分别是标准输出、标准输入和标准错误,分别用stdout, stdin, stderr来表示。对于这三个数据流来说,默认是表现在用户终端上的。
- 回顾知识点
- 什么样的数据类型在堆、栈及静态区
- 堆—一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。
- 栈—由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
- 静态区(全局区)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。有一大特点如果被多次声明,其只分配一块区域,这就需要你在访问该类变量的过程中,只能对一块区域进行操作。这就导致变量相同的只能访问一个。
- 什么样的数据类型在堆、栈及静态区
- 问题—fopen()是在哪里呢?—堆上—简单来说—一般情况下有互逆操作的都在堆上
- fclose()—关闭一个流
- int fclose(FILE *fp)
- fgetc()
- int fgetc(FILE *stream)// 与getc相同,EOF则存在报错—其实是一个函数
- 注意返回是int,不是char
- int getc(FILE *stream)// 与getchar等价,可以在任意流中获得内容—其实是一个宏
- 宏和函数的区别
- 宏只占用编译时间,不占用调用时间
- fputc()
- int fputc(int c, FILE *stream)
- int putc(int c, FILE *stream)//返回值是int
//需求分析---命令行实现将一个文件拷贝进入另一个文件 #include<stdio.h> #include<stdlib.h> int main(int argc, char** argv){ FILE *fpo, *fpc; int ch;//观察是否读取到文件内容,也可以判断是否达到文件读取的底部 if(argc < 3){ fprintf(stderr, "Usage: %s, <src_file>, <dest_file>", argv[0]); } fpo = fopen(argv[1], "r"); if(fpo == NULL){ perror("fopen()"); exit(1); } fpc = fopen(argv[2], "w"); if(fpc == NULL) { fclose(fpo);//防止内存泄漏 perror("fopen()"); exit(1); } while (1) { ch = fgetc(fpo); if (ch == EOF){ break; } fputc(ch, fpc);//将ch放入fpc } fclose(fpc);//先删除依赖 fclose(fpo); exit(0); }
- 补充知识点
- 如何将命令行参数转换为主函数的参数
//int main(int argc, char **argv) //argc是命令行有几个参数输入的总量,argv则是一个数组,下标从1开始就是第一个参数
- fgets()
- gets()不检查缓冲区的溢出情况,只约束了地址,不检查终端最后输入地址时是否够放入存储的空间。因此,使用fgets()更好
- char *gets(char *s)//只有地址不检查当前输入长度
- char *fgets(char *s, int size, FILE *stream)//有长度检查功能,读到size-1个字符,而后有一个’\n’
- fgets有两种可能结束
- 读到size-1个
- 读到’\n’
- 使用fgets输入一个"ab",则真正存储的是"ab\0",而每次读取结束的标志是要存在\n,可以理解为只有\n被读取才能证明该流被读取完
- 例
// 试判断abcd这个流需要几次才能用以下的fgets读取成功 #define SIZE 5 fgets(buf, SIZE, stream); // 答案---两次 // 第一次---abcd\0 注意但并未读到\n // 第二次---\n\0...读取完成 // 读取完成后为NULL
- fputs()
- int puts(const char *s)//遇到\0为止
- int fputs(const char *s, FILE *stream)
- fread()
- size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
- 解读—在stream读入ptr,读入nmemb个对象,每个对象大小为size,因此,ptr大小为nmemb*size
- 返回值其实就是读取了多少个对象nmemb
- size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
- fwrite()
- size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
- 这两个函数没有边界验证,只能操作比较整齐的数据,为了保险起见一般size我们选择1字节。退化为fgetc使用。可以从下面这个例子来看。
fread(buf, size, nmemb, fp); 1->数据量足够 2->只有5个字节 fread(buf, 1, 10, fp); 1->10->10字节 2->5->5字节 fread(buf, 10, 1, fp); 1->1->10字节 2->0->??
- fclose()—关闭一个流