1:简介
标准IO是面向流的,也就是说我们只管读取和写入,不用管其他的内容。(eg:我们保存数据缓冲区的大小)。
2:因为我们介绍的标准IO,而标准IO对于我们来说都是FILE结构体和一些函数的调用。因此、我们从FILE结构体先讲起,这里只是简单介绍FILE结构体中的内容,并不做详细的分析。首先、我们先简单介绍一下流的一些概念。
FILE结构体:(我们京城成为FILE位流对象)
FILE结构体:该结构体中有实际I/O的文件描述符、用于执行该流缓冲区的指针、缓冲区的长度、文件结束和出错标志等。
接下来我们简单介绍一下流的概念:
我们可以简单将流理解成一条管道,我么既可以向这个管道中放东西,也可以从这个管道中取内容。这条管道我们不知道有多长,我们不知道写入管道的内容何时能够到达终点。
同时这条管道也有一些阀门,我们可以控制每注入一段内容就将管道内容放入到重点,也可以是等到管道内容满了放入重点,放入一点我们直接放入重点。这都是由我们自己控制的。
同时我们也可以控制向这条管道放入内容的基本单位,可以是单个,同时也可以是多个内容组成的合成体。
接下来介绍一些常见的流:
标准输入、输出和错误流:这几个流是相对于我们的终端而言(就是我们在登录Linux时候给出的黑色界面,如下图所示)。
标准输入:我们向终端写入内容,某些进程等可能会接受。也就是进程从终端读取一些数据。
标准输出:有一些进程的一些消息可能需要显示到终端。
标准错误:他也是一些进程需要消息到终端,但是呢,他又和标准输出有些不同、有什么不同呢?标准输入是遇到一个换行符或者缓冲区满了就输出,而标准错误不管遇到什么内容直接输出。
谈到这里了,不得不谈谈这几种管道控制方法(缓冲机制)有什么不同。
全缓冲:也就是等到缓冲区满了之后再进行I/O(也就是输入和输出)。
行缓冲:遇到换行符进程I/O。
不带缓冲:不管遇到什么内容直接进行I/O。
前面我们曾提到我们可以改变放入管道内容是单个的或者几个不同组成的,这里我们介绍一下这个概念。
在Linux中我们通常指单个字节还是或者多个字节的I/O,在我们编程中一般计算一些内容的大小用字节、可以简单理解成我们储存的基本单位。
在一般情况下、ASSCI中的字符是单个字节组成的。但是也有一些多个字节组成的,例如汉字一般用两个字节组成。具体的要看编码策略(就是我们对我们要储存内容的一个编译和解释,例如UTF-8存储或者其他的存储。在这里就不详细介绍了、如果感兴趣的可以查看一些编码相关的资料)。通常我们将多字节定义位宽字符,在wchar.h中进行定义。
我们改变读写内容是单字节还是多字节使用如下函数:
int fwide(FILE *fp, int mode);它可以设置一个流的定向(也就是设置读取时单字节的还是多字节的)。
fp:我们打开FILE对象。
mode:负:将试图指定流时单字节定向的。
正:试图指定流时宽定向的
0:不是图流的定向。并且返回该流定向的值。(返回正宽字节定向的,负:单字节定向的。0:未定向)。
注:不能改变已经定向的流。具体一次读取几个字节还是要看具体的IO函数。这里只是一个定向,类似做一个标记。
同时freopen函数可以清除一个流。这个函数在之后会做介绍。
3:到这里,一些流的概念已经介绍完了,接下我们介绍一下流相关的一些函数。
首先、自然是如何打开缓冲了。下面有三个函数可以打开缓冲
FILE* fopen(congst char* pathname, const char* type);
FILE* freopen(const char* pathname, const char* type, FILE *fp);
FILE* fdopen(int fledes, const char* type);
当然者三个函数还是有一些区别的:fopen函数打开一个指定路径名的流,freopen函数打开指定流上指定的文件(如果该文件已经打开、那么先关闭该流,如果该留已经定向、则清除该定向。一般时候我们用这个函数打开标准输入、标准输出和标准错误)。
fdopen函数可以让一个指定的流与文件描述符相结合。
type:是指我们打开的方式。如下图所示。
同时对于第二个函数最后一个参数指的是文件描述符。
接下来我们介绍一下流的控制或者操作函数。
void setbuf(FILE* stream, char* buf);
void setbuffer(FILE* stream, char* buf, size_t size);
void setlinebuf(FILE* stream);
int setvbuf(FILE* stream, char* buf, int mode, size_t size);
这几个函数还是有一些区别的
第一个函数如果buf是非空的话,buf的大小至少是BUFSIZ,告诉文件流填充满buf才能进行IO。如果buf是空的话,那么将告诉流,没有缓冲区。
第二函数设置指定大小的buf才进行IO。
第三个函数设置成行IO。
最后一函数如下图所示
最后我们在流的一些控制上介绍关闭一个打开的流。
fclose(FILE *fp);
4:读和写流:这部分是相当重要的,我们在上述都是对流的一些自动控制,这部分将真正意义的读和写。我们将指定读和写方式(如按行、字符、指定大小字节读取)。
输入函数:
一次读取一个字符:
int getc(FILE* fp);//从指定文件描述符打开的终端或者是文件一次读取一个字符。
int fgetc(FILE* fp);//同上
int getchar();//从终端一次读取一个字符。
接下来介绍读取出错或者到达文件结尾的标志。
int ferror(FILE *fp);
int feof(FILE *fp);
void clearerr(FILE *fp);
输入函数三个按字符读取返回值与EOF进行比较,看是否出错/到达文件解为(如果出错。返回EOF)。接下来这三个标志中前两个分别是代表读取出错和到达文件解为。最后一个
标志是用来清楚文件错误标识。
int ungetc(int c, FILE* fp);此函数是将指定的字符压回到文件流中,我们可以重新读取此文件流中的内容。
输出函数:
int putc(int c, FILE* fp);
int fputc(int c, FILE* fp);
int putchar(int c);
这三个函数如同三个输入函数是一样的。
这里是单个字符的IO,接下来介绍一行的IO。
输入函数,读取一行:
char* fgets(char* buf, int n, FILE fp);
char* gets(char* buf);
第一个函数指定缓冲区长度的去读取一行,如果数据没到行尾,并且缓冲区不够了,那么将不据徐读取。
第二个函数从标准输入读取一行数据。(但是这里使用这个函数需要逐一,缓冲区长度小于从标准输入中读取到的数据,造成内存溢出)。
输出函数,输出一行:
int fputs(const char* str, FILE* fp);
int puts(const char* str);
第一个函数是向指定文件描述符输出数据,第二个函数是向标准输出输出一行数据。
请注意,这两个输出函数遇到null字符便截至,因为该字符是字符串的终止字符,遇到该字符意味着这个字符串结束了。
5:至于标准IO的效率,这里不做讨论了,因为有缓冲区的肯定相对于无缓冲区的效率搞一下。但是有些时候我们有一些特殊的需求,我们需要没有缓冲区的。
6:上述都是对于字符的读写,接下来我将对二进制的读写。也就是面向字节的读写。
读和写:
size_t fread(void* ptr, size_t size, size_t nobj, FFILE *fp);
size_t fwrite(const void* ptr, size_t size, size_t nobj, FILE* fp);
通过函数名字,我们将会知道这两个函数分别用于读和写。
参数:
ptr:都或者写指向缓冲区的指针。
size:读或写一份数据的大小。
nobj:读写几分数据。
fp:指定读或者写的文件描述符。
这两个函数的返回值是读或者写指定大小字节的的内容,当返回值的大小小于nobj数目是,对于写、一定是出错了。对于读、可能时到达文件解为/也可能是出错了。
这就需要我们使用ferror或者feof进行判断了。
a:同时、我们这里注意一下,关于二进制存储的方式可能由于不同的系统而不同。有些系统可能采用紧密的保存(也就是一个字节挨着一个字节的保存),而有些系统可能采用
某种对齐方式(就像我们结构体那种对其方式,当然、这里二进制文件对齐方式可能有些不同),因此我们需要在同一个系统进行读写。或者我们在保存的内容中设置某些关键字和
关键字后面内容的长度。
b:还有些机器存储浮点数的二进制可能不同,因此、这里使用这种方式进行存储需要注意一下。
7:前面提到了流的创建、控制、读和写等内容,在这里我们要将一下流的定位,也就是我们定位到文件哪里开始进行读和写。
这里需要介绍六个定位流的函数。但是大体都是相同的。但是我这里只详细介绍其中的一个,这一个的功能已经足够了。
int fgetpos(FILE* stream, fpos_t *pos);
int fsetpos(FILE* stream, const fpos_t *pos);
off_t ftello(FILE* fp);
int fseeko(FILE* fp, off_t offset, int whence);
long ftell(FILE* fp);
int fseek(FILE* fp, long offset, int whence);
这里ftell是返回相对于文件开始位置的当前文件的位置。
fseek函数的参数。
fp:文件描述符。
offset:相对于whence的偏移量,偏移量既可以为正、同时也可以为负。
whence:相对于哪一点的偏移量,这里这个值共有三个位置可供选择。SEEK_SET:相对于文件开始位置的偏移量。SEEK_END:相对于文件结尾的偏移量。SEEK_CUR:相对于文件当前
位置的偏移量。
8:格式化输出输出,也就是将数据以指定的格式进行输入和输出。
在这里,我们介绍各种格式化输出/输出。
首先、我们在这里先介绍格式化输出。
函数原型:
int printf(const char* format, ...);
int fprintf(FILE* fp, const char* format, ...);
int sprintf(char* buf, char* format,...);
int snprintf(char* buf, size n, const char* format).
第一个函数:format指的是我们直嘀咕的格式,将内容输出到标准输出。
第二个函数:将指定格式的内容输出到文件描述符fp对应的缓冲区。
第三个和第四个函数:将指定格式内容输出到buf中。第四个函数相对来说更加安全,就是指定buf的大小,如果输出内容超出buf的大小的时候,将不会进行输出。
在这里我们介绍一个format。
[flags][fldwidth][precision][lemodifier]convtype:
flags:这是一个标记,标记着要如何输出。如下图
fldwidth:说明转换字符的最小宽度,如果转换后的字符小于这个宽度,那么将使用空格填充。字段宽度可以是一个非负十进制数,也可以是一个*。
precision:此关键字是一个至少输出的位数。
整数:至少输出几位整数。
浮点数:小数点后的精度。
字符串:转换后的最大字符串。
精度是一个.非负十进制整数。
lenmodifier:说明转换的参数长度,也就是说我们转换成多长的数据,像无符号的,size_t这种,具体的如下图。
convtype:此参数控制着如何解释参数,有无符号、几进制等。
接下来介绍格式化输入部分,关于这部分我就不一一介绍参数了,和格式化输出几乎一样。
int scanf(char* format, ...);
int fscanf(FILE* fp, char* format, ...);
int sscanf(char* buf, char* format, ...);
format如下。
[*][fldwidth][lemodifier]convtype:
9:临时文件。
char* tmpnam(char* ptr);
FILE* tmpfile(void);
第一个函数产生一个路径名,如果ptr不是空的话,那么产生的文件名存在静态存储区中。反之、存在ptr指向的内存中。当我们存在静态存储区中的时候,我们再次调用这个函数将会重写这个静态存储区。下面我解释一下这段话。
静态存储区:程序运行期间始终存在的一块存储区(eg:static char buf[1024])当我们包含stdio.h头文件时候,程序已经定义好。因此、我们无须管理这块内存。但是由于这块存储区只有一块,因此、我们再次调用这个函数将会重写这块静态存储区。我这建议存在自己的内存中。
第二个函数:他首先调用第一个函数产生一个路径名,然后创建该文件并且unlink这个文件。也就是说我们这个程序结束的时候,这个文件也就不存在了。
这里需要注意的是:第一个函数有调用次数的限制、因此我们尽可能少些调用。调用次数限制宏在stdio.h头文件中TMP_MAX。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。