CC++之标准输入输出

    xiaoxiao2021-03-25  111

    Tips:  1. 本人当初学习C/C++的记录。  2. 资源很多都是来自网上的,如有版权请及时告知!  3. 可能会有些错误。如果看到,希望能指出,以此共勉!

    文件

      在此之前,有必要先了解一下文件是什么东西!所谓“文件”是指一组相关数据的有序集合。这个数据集有一个名称,叫做文件名。例如源程序文件、目标文件、可执行文件、库文件等。文件通常是驻留在外部介质(如磁盘等)上的,在使用时才调入内存中来。

    从用户的角度看,文件可分为普通文件和设备文件两种。 普通文件是指驻留在磁盘或其它外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序;也可以是一组待输入处理的原始数据,或者是一组输出的结果。 设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看作是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。从文件编码的方式来看,文件可分为ASCII码文件和二进制码文件两种。

    ASCII文件也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。 二进制文件是按二进制的编码方式来存放文件的。二进制文件虽然也可在屏幕上显示,但其内容无法读懂。   这两种方式打开文件并没有太大的区别,仅仅只有一点区别就是在处理某些特殊字符的时候。

    以文本方式打开文件,若将数据写入文件,如果遇到换行符’\n’(ASCII 值为0A),则会转换为回车+换行’\r\n’(ASCII值为0D0A)存入到文件中,同样读取的时候,若遇到回车+换行,即连续的ASCII值0A0D,则自动转换为换行符。而以二进制方式打开文件时,不会进行这样的处理。还有如果以文本方式打开文件时,若读取到ASCII码为26(^Z)的字符,则停止对文件的读取,会默认为文件已结束;而以二进制方式读取时不会发生这样的情况。由于正常情况下我们手动编辑完成的文件是不可能出现ASCII码为26的字符,所以可以用feof函数去检测文件是否结束。以上所述的两点区别只在windows下存在,在unix下没有区别。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。因此也把这种文件称作“流式文件”。 执行如下代码,然后以二进制方式查看生成的wb.txt和wt.txt两个文件: 从上图可知,写入方式的不同,导致文件存储的不一样!

      以文本方式读取时,对‘\r\n’进行了转换,而二进制方式读取时却没有进行这样的转换(要注意,windows和linux下的换行符是不一样的,在windows下换行符是\r\n,即0x0D0x0A,而在linux下换行符是\n,即0x0A。)

    缓冲区

      缓冲区buffer是内存中的一个临时存储区,用来匹配不同部件数据传输率的差异。通过使用缓冲的方式可以更高效地处理输入输出,信息从设备和程序之间传输时可临时存储在缓冲区中,等待累积成数据块或合适的事机再传送的目的地。

    缓冲区中的内容不一定非要一次全部取出,即:可以分多次取出

    缓冲区的类型

      缓冲区分为三种类型:全缓冲、行缓冲和不带缓冲。

    全缓冲

      在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。

    行缓冲

      在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是标准输入(stdin)和标准输出(stdout)。

    不带缓冲

      也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。   ANSI C( C89 )要求缓存具有下列特征:

    标准出错是不带缓存的。当且仅当标准输入和标准输出并不涉及交互设备时,它们才是全缓存的。但是,这并没有告诉我们如果标准输入和输出涉及交互作用设备时,它们是不带缓存的还是行缓存的,以及标准输出是不带缓存的,还是行缓存的。大部分系统默认使用下列类型的缓存:如果是涉及终端设备的流,则它们是行缓存的;否则是全缓存的。

      我们经常要用到标准输入输出流,而ANSI C对stdin、stdout和stderr的缓存特征没有强行的规定,以至于不同的系统可能有不同的stdin、stdout和stderr的缓存特征。目前主要的缓存特征是:stdin和stdout是行缓存;而stderr是无缓存的。

    缓冲区的大小

      如果我们没有自己设置缓冲区的话,系统会默认为标准输入输出设置一个缓冲区,这个缓冲区的大小通常是512个字节的大小。缓冲区大小由 stdio.h 头文件中的宏 BUFSIZ 定义   缓冲区的大小是可以改变的,也可以将文件关联到自定义的缓冲区,详情可以查看setvbuf()和setbuf()函数。

    void setbuf(FILE * restrict stream, char * restrict buf); int setvbuf(FILE * restrict stream, char * restrict buf, int mode, size_t size);

    setvbuf的mode参数有:

    _IOFBF(满缓冲):缓冲区空时读入数据;缓冲区满时向流写入数据。_IOLBF(行缓冲):每次从流读入一行数据或向流写入数据。如:stdio,stdout_IONBF(无缓冲):直接从流读入数据,或者直接向流写入数据,而没有缓冲区。如:stderr

    setbuf(stream, buf);在:

    buf == NULL:等价于(void)setvbuf(stream, NULL, _IONBF, 0);buf指向长度为BUFSIZ的缓冲区:等价于(void)setvbuf(stream, buf, _IOFBF, BUFSIZ);

      这里还要提一下传说中的setbuf的经典错误,在《C陷阱和缺陷》上有提到:

    int main() { int c; char buf[BUFSIZ]; setbuf(stdout,buf); while((c = getchar()) != EOF) putchar(c); return 0; }

      程序交回控制给操作系统之前C运行库必须进行清理工作,其中一部分是刷新输出缓冲,但是此时main函数已经运行完毕,buf缓冲区作用域在main函数中,此时buf字符数组已经释放,导致输出诡异乱码。可以将buf设置为static,或者全局变量,或者调用malloc来动态申请内存。

    缓冲区的刷新(清空)

      下列情况会引发缓冲区的刷新: - 缓冲区满时 - 行缓冲区遇到回车时 - 关闭文件 - 使用特定函数刷新缓冲区。(例如:C中的fflush(stdin)、C++中的endl)   C++支持两种I/O,第一种是从C语言继承来的,一种是由C++定义的面向对象I/O系统。

    flush(stdin);式

    由C99标准文档中:   If stream points to an output stream or an update stream in which the most recent operation was not input, the fflush function causes any unwritten data for that stream to be delivered to the host environment to be written to the file; otherwise, the behavior is undefined. 可以看出fflush对输入流为参数的行为并未定义。但由MSDN上的fflush定义:   If the file associated with stream is open for output, fflush writes to that file the contents of the buffer associated with the stream. If the stream is open for input,fflush clears the contents of the buffer.   可以看出fflush(stdin)在VC上还是有效地!鉴于各编译器对fflush的未定义行为实现不一样,不推荐使用fflush(stdin)刷新输入缓冲区。

    setbuf(stdin, NULL);式

    由前面对setbuf函数的介绍,可以得知,setbuf(stdin, NULL);是使stdin输入流由默认缓冲区转为无缓冲区。都没有缓冲区了,当然缓冲区数据残留问题会解决。但这并不是我们想要的。

    scanf(“%*[^\n]”);式(《C语言程序设计 现代方法 第二版》中提到)

    这里用到了scanf格式化符中的“*”,即赋值屏蔽;“%[^集合]”,匹配不在集合中的任意字符序列。这也带来个问题,缓冲区中的换行符’\n’会留下来,需要额外操作来单独丢弃换行符。

    C输入输出

      当我们提到输入时,这意味着要向程序填充一些数据。输入可以是以文件的形式或从命令行中进行。C 语言提供了一系列内置的函数来读取给定的输入,并根据需要填充到程序中。   当我们提到输出时,这意味着要在屏幕上、打印机上或任意文件中显示一些数据。C语言提供了一系列内置的函数来输出数据到计算机屏幕上和保存数据到文本文件或二进制文件中。

    标准输入输出

      C 语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。通常把显示器定义为标准输出文件,一般情况下在屏幕上显示有关信息就是向标准输出文件输出。键盘通常被指定标准的输入文件,从键盘上输入就意味着从标准输入文件上输入数据。与此同时,C语言还定义了一个标准错误输出,其对象也是屏幕。   以下三个文件会在程序执行时自动打开,以便访问键盘和屏幕。

    标准文件文件指针设备标准输入stdin键盘标准输出stdout屏幕标准错误stderr您的屏幕

      在C语言中,所有的数据输入/输出都是由库函数完成的。C的标准输入输出库函数主要由以下这些:

    int getchar(void);与int putchar(int c);

      getchar从stdin中读去一个字符字符,相当于getc(stdin)。返回类型为int型(返回值为用户输入的ASCⅡ码)。也可以指定这个值为char变量,因为这个字符包含于低位字节中( 高位字节通常为0 ),如果有错,getchar()返回EOF。正常情况下,getchar()缓存输入,直到键入了回车键,这称为行缓冲输入,在键入的字符实际传送给程序之前必须敲入一个回车键。   虽然putchar()带一个整数参数,通常可以用一个字符的变元调用它,但是只有其低位字节被实际输出到屏幕上,若操作失败,返回EOF( 宏EOF被定义于stdio.h中,通常其值为-1 )。

    int getch(void);与int getche(void);

      两个最常用的交互式函数,对于大多数编译器,这些函数的原型都可在头文件conio.h中找到,对于某些编译器中这些函数前面有一下划线。如getch()和_getche();这就是为什么在VS2008中经常提示要在前面加一个’‘.   getche()函数会由键盘输入一个字符,返回给调用者,并在屏幕上显示读入的字符。由于它并不读取缓冲区的字符,只要用户输入字符,getche()函数会立刻读取,而不需等待按【Enter】键。通常用于程序中只需用户输入一个字符,即可往下继续执行的情形。   getch()同样不经过缓冲区,与getche()的区别是,getch()不需将所输入的字符显示到屏幕上。

    char* gets( char* str );与int puts( const char* str );

      gets读取从键盘上输入的字符串并把它存放在由其他变元所指的地址中,它从键盘读入字符,直到遇到回车键为止。回车键不是输入串的一部分,相反,将空结束符放在字符串尾来代替,并且由gets()返回字符串的头指针。但是使用gets()是要小心,因为它不对正在接受输入的字符数组执行边界检查。因此,用户可以键入比数组能够容纳的更多的字符。在商用代码中一般不是用它。它的替代物是fgets()。   puts将str指向的字符串变元写到屏幕上,输出完成后光标自动移到下一行。它的调用比printf();开销小,因为puts()只能输出字符串,不能输出数字或进行格式转换,因而puts()用的空间少且速度比printf()快。因此函数puts()经常用于代码优化。操作失败时返回EOF,正常返回非负值。

    int printf(const char* control_string, …);与int scanf(const char* control_string…);

      printf函数是一个标准库函数,它的函数原型在头文件“stdio.h”中。但作为一个特例,不要求在使用 printf 函数之前必须包含stdio.h文件。printf函数调用的一般形式为:printf(“格式控制字符串”, 输出表列)   输出表列:给出了各个输出项,要求格式字符串和各输出项在数量和类型上应该一一对应。   格式控制字符串:用于指定输出格式。格式控制串可由以%开头的格式字符串和非格式字符串两种组成。非格式字符串原样输出,在显示中起提示作用。格式字符串在%后面跟有各种格式字符,以说明输出数据的类型、形式、长度、小数位数等。

    类型

    格式字符意义d以十进制形式输出带符号整数(正数不输出符号)o以八进制形式输出无符号整数(不输出前缀0)x,X以十六进制形式输出无符号整数(不输出前缀Ox)u以十进制形式输出无符号整数f以小数形式输出单、双精度实数e,E以指数形式输出单、双精度实数g,G以%f或%e中较短的输出宽度输出单、双精度实数c输出单个字符s输出字符串p指针的值特殊字符\n换行\f清屏并换页\r回车\tTab符\xhh表示一个ASCII码用16进表示, 其中hh是1到2个16进制数

    标志

      可以控制输出左对齐或右对齐, 即在”%”和字母之间加入一个”-” 号可说明输出为左对齐, 否则为右对齐。默认补空格。 例如:%-7d 表示输出7位整数左对齐 %-10s 表示输出10个字符左对齐   标志字符为 -、+、# 和空格四种,其意义下表所示:

    标 志意义-结果左对齐,右边填空格+输出符号(正号或负号)空格输出值为正时冠以空格,为负时冠以负号#对c、s、d、u类无影响;对o类,在输出时加前缀o;对x类,在输出时加前缀0x;对e、g、f 类当结果有小数时才给出小数点。

    精度

      精度格式符以“.”开头,后跟十进制整数。本项的意义是:如果输出数字,则表示小数的位数;如果输出的是字符,则表示输出字符的个数;若实际位数大于所定义的精度数,则截去超过的部分。

    输出最小宽度

    可以在”%”和字母之间插进数字表示最大场宽。如果字符串的长度、或整型数位数超过说明的场宽, 将按其实际长度输出。但对浮点数, 若整数部分位数超过了说明的整数位宽度, 将按实际整数位输出; 若小数部分位数超过了说明的小数位宽度, 则按说明的宽度以四舍五入输出。   例如: = 表示输出3位整型数, 不够3位右对齐。   %9.2f 表示输出场宽为9的浮点数, 其中小数位为2, 整数位为6, 小数点占一位, 不够9位右对齐。   %8s 表示输出8个字符的字符串, 不够8个字符右对齐。 另外, 若想在输出值前加一些0, 就应在场宽项前加个0。 例如: d 表示在输出一个小于4位的数值时, 将在前面补0使其总宽度为4位。 如果用浮点数表示字符或整型量的输出格式, 小数点后的数字代表最大宽度, 小数点前的数字代表最小宽度。 例如: %6.9s 表示显示一个长度不小于6且不大于9的字符串。若大于9, 则第9个字符以后的内容将被删除。可以在”%”和字母之间加小写字母l, 表示输出的是长型数。 例如: %ld 表示输出long整数   %lf 表示输出double浮点数   scanf() 函数用来格式化输入数据,即按用户指定的格式从键盘上把数据读入到指定的变量中。其原型为: int scanf ( char * format [ ,argument, ... ]);
    格式化说明符
    格式字符说明%a 读入一个浮点值(仅C99有效)%A同上%c读入一个字符%d读入十进制整数%i读入十进制,八进制,十六进制整数%o读入八进制整数%x读入十六进制整数%X同上%c读入一个字符%s读入一个字符串%f读入一个浮点数%F同上%e同上%E同上%g同上%G同上%p读入一个指针%u读入一个无符号十进制整数%n至此已读入值的等价字符数%[]扫描字符集合%%读%符号

      另外,还有附加格式说明字符,用于追加在上面的格式说明符后面。

    修饰符说明L/l长度修饰符,输入”长”数据h长度修饰符,输入”短”数据W整型常数,指定输入数据所占宽度*星号,空读一个数据hh同 h,但仅对C99有效ll同 l,但仅对C99有效
    空白字符

      空白字符会使scanf()函数在读操作中略去输入中的一个或多个空白字符,空白符可以是space,tab,newline等等,直到第一个非空白符出现为止。 例如:scanf(“%d %d”, &i, &j); // 先读一个整型数, 然后略去一个或多个空格,最后读入另一个整型数。输入形式:值1 空白符(space、tab、回车等)值2

    非空白字符

      一个非空白字符会使scanf()函数在读入时剔除掉与这个非空白字符相同的字符。   例如:scanf(“%d,%d”, &i, &j); // 先读一个整型数, 然后把接着输入的逗号剔除掉,最后读入另一个整型数。如果”,”这一特定字符没有找到, scanf()函数就终止。 【返回值】成功则返回被赋值的参数的个数。如果 format 和 argument 匹配错误,或者遇到结束符,那么返回值可能小于参数的个数。如果读取  发生错误,将会返回 EOF,并设置错误标识,后续可以通过 ferror() 检测。   如果遇到结束符,将会设置文件结束标识,后续可以通过 feof() 检测。 如果在读取宽字符的时候发生编码错误,那么将会把 errno 设置为EILSEQ。 以下主要用于文件操作   首先,区分一下位置指针和文件指针:   文件指针:指向存储文件信息的一个结构体的指针,在stdio.h中定义   位置指针:对文件进行读写操作时移动的指针,实际是文件指针结构体中的一个变量   比如用FILE *fp定义了一个文件指针,并成功打开一个文件之后,fp只是指向该结构体,而在对文件进行读写操作时,fp的值并不会改变,改变的是结构体中_ptr的值,这个_ptr就是位置指针。

    FILE* fopen( const char*filename, const*char* mode )

    参数说明

    对于文件路径,只需注意若未明确给出绝对路径,则默认该文件在工程的目录下。若需给出绝对路径,则注意转义字符’\’,比如有文件test.txt存放在C盘根目录下,则文件路径参数值应为C:\test.txt。对于mode,主要由r,w,a,+,b,t六个字符组合而成。   r: 只读方式,文件必须存在   w: 只写方式,若文件存在,则原有内容会被清除;若文件不存在,则会建立文件   a: 追加方式打开只写文件,只允许进行写操作,若文件存在,则添加的内容放在文件末尾;若不存在,则建立文件   +: 可读可写   b: 以二进制方式打开文件   t: 以文本方式打开文件(默认方式) 下面是常见的组合:   r: 以只读的方式打开文件,只允许读,此文件必须存在,否则返回NULL,打开成功后返回文件指针,位置指针指向文件头部   r+: 以可读可写的方式打开文件,允许读写,此文件必须存在,否则返回NULL,打开成功后返回文件指针,位置指针指向文件头部   rb+: 以可读可写、二进制方式打开文件,允许读写,此文件必须存在,否则返回NULL,打开成功后返回文件指针,位置指针指向文件头部   rt+: 以可读可写、文本方式打开文件,允许读写,此文件必须存在,否则返回NULL,打开成功后返回文件指针,位置指针指向文件头部   w: 以只写的方式打开文件,只允许写,若文件存在,文件中原有内容会被清除;若文件不存在,则创建文件,打开成功后返回文件指针,位置指针指向文件头部   w+: 以读写的方式打开文件,允许读写,若文件存在,文件中原有内容会被清除;若文件不存在,则创建文件,打开成功后返回文件指针,位置指针指向文件头部   a: 以追加、只写的方式打开文件,只允许写。若文件存在,则追加的内容添加在文件末尾,若文件不存在,则创建文件。打开成功后返回文件指针,位置指针指向文件头部(注意很多书上或资料上讲述追加方式打开成功后位置指针指向文件末尾是错误的)   a+: 以追加、可读写的方式打开文件,允许读写。若进行读操作,则从头开始读;若进行写操作,则将内容添加在末尾。若文件不存在,则创建文件。打开成功后返回文件指针,位置指针指向文件头部。   从上图中可以看出,在仅仅是打开文件,没有进行文件的读写操作时,文件指针的各个属性都是初值,只有在真正读写之后,文件指针中的各个属性才会改变。例如:_cnt 4表示,还有4个字符没有读取。

      只写文件时,和读差不多,只是_cnt 参数不同。   从上图可以看出,在追加方式时,程序并不会考虑文件中原有的部分。图中的乱码是因为字符串后面没有结束符导致的。

    int fclose( FILE* fp );

      关闭一个由fopen()打开的文件,把留在磁盘缓冲区的数据写入文件并由操作系统级正式关闭文件。关闭流文件失败会产生各种麻烦,如:丢失数据,破坏文件和程序中出现间歇的错误等。   flose()也释放与流文件控制块,使它可以重用。有时,由于操作系统一次同时打开的文件数量有限,因此必须在关闭一个文件后再打开另一个文件。   返回0标志着文件关闭成功。如果关闭失败,则返回EOF。可用标准函数ferror()来确定和报告出错消息。   通常,fclose()仅在磁盘驱动器中过早移走或磁盘上没有更多的空间时报错。

    int putc( int ch,FILE* fp );

    把一个字符写到文件中, 如果操作成功,则函数返回被输出的字节;否则,返回EOF

    int getc( FILE* fp );

    从某一文件读一个字符, 函数getc()读到文件尾时返回EOF标志,如果发生错误,也返回EOF。

    fgetc()

    同getc()

    int fgets( const char*str, int length, FILE* fp );

      从某一文件中读取一个字符串,直到读到换行符或读完length-1个字符,如果读到新行,它是原字符串的一部分( 不像gets()那样另起新串 ),结果字符串将以NULL终止。如果操作成功,则函数返回str,否则返回空指针。

    int fputs( const char*str, FILE*fp )

      把str指向的字符串写到指定的流中,如果失败,则返回EOF。其中str可以是字符串常量,也可以是字符数组名或指针变量

    int fseek(FILE *fp,long offset,int origin);

      在文件中查找一个特定的字节

    long ftell(FILE *fp)

      返回文件位置指针的当前位置相对于文件首的偏移字节数

    fprintf()

      输出到磁盘文件上

    fscanf()

      从磁盘中读数据

    int feof( FILE* fp );

      若到文件尾,返回真值.既可用于二进制文件,也可应用于文本文件 eg: while( !feof( fp ) ) ch = getc( fp );

    int ferror( FILE* fp );

      函数确定是否在文件操作期间出错。fp是有效的文件指针。在文件操作期间如果有错,则函数返回true,否则返回false。由于每个文件操作均设置错误条件,因而应在每个文件中操作后立即调用ferror(),否则会丢失错误信息。

    void rewind( FILE* fp ),

      把文件位置指针重新置于文件的起始位置,fp是有效的文件指针,

    int remove( const char*filename )

      清除一个文件, 操作成功,返回0,操作失败返回非零值。

    int fflush( FILE* fp );

      清空一个输出流的内容,将任何缓冲区的内容写到与fp相关的文件中,如果在fp为空时调用fflush(), 则所有为输出打开的文件被清空。操作成功返回0,否则返回EOF。

    size_t fread( void* buffer, size_t num_byte, size_t count, FILE* fp );

      buffer 是一个指针,指向一个接收文件中数据的存储区,count的值指出要写多少项。返回读入的项的数目,如遇到文件的结尾或操作失败。这个值可能少于count。

    size_t fwrite( const void * buffer, size_t num_byte, size_t count , FILE* fp );

      buffer 是一个指针,指向要写入文件中的信息快。count的值指出要写多少项。返回写入项的数目,这个值永远等于count,除非操作失败。

    size_t类型被定义为某种无符号整数,fp是一个指向已经打开流的文件指针。fwrite()和fread()最大的用途之一是可以写用户自定义的数据类型,特别是结构体类型

    int fseek( FILE* fp, long int numbytes, int origin );

      fp是由fopen()返回的指针,numbytes是从文件的origin位置到当前位置的字节,是下面的宏之一:

    origin宏名文件开始处SEEK_SET当前位置SEEK_CUR文件末尾SEEK_END

      可以用fseek()来寻找任何数据类型的倍数,方法是用想要的项数乘以数据的长度。eg:fseek( fp, 9*sizeof( struct myStruct ), SEEK_SET );

    long int ftell( FILE* fp );

    决定一个文件的当前位置,返回与fp关联的文件的当前位置的地址。如果失败,返回-1.

    int fprintf( FILE* fp, const char* control_string… );

    int fscanf( FILE* fp, const char* control_string… );

      注意:尽管fprintf()和scanf()是从磁盘文件中读写数据最容易的方法,但却并不是最有效的方法。由于格式化的ASCⅡ数据写入文件的格式与在屏幕上显示的相同( 而不是二进制方式 ),因而调用时要引起额外的开销。如果要考虑速度与文件长度,最好使用fread()和fwrite()

    C++风格

      I/O类库包括的类主要有ios、istream、ostream、iostream、ifstream、ofstream、fstream、istrstream、ostrstream、strstream等。其中ios为根基类,其余都是它的直接或间接派生类。

    C++ I/O类库

      在C++中输入输出流被定义为类,I/O库中的类称为流类,编译系统提供了用于输入输出的iostream类库。 (1)标准流   提供通用输入输出操作, 作为其他I/O流基类   系统指定的标准设备的I/O操作。 (2)文件流   以外存中的文件为对象进行输入和输出。    以文件为对象的输入输出,包括从磁盘文件输入数据, 或将数据输出到磁盘文件。 (3)字符串流   对内存中指定空间进行输入和输出。   通常指定一个字符数组作为存储空间。   C++中的iostream库主要包含下图所示的几个头文件:

    OSstream库fstreamiomainipiosiosfwdiostreamistreamostreamsstreamstreambufstrstream

    iostream.h:包含操作所有输入/输出流所需的基本信息 istream.h , ostream.h iomanip.h:包含格式化I/O操纵算子,用于指定数据输入输出的格式 fstream.h:处理文件信息,包括建立文件,读/写文件的各种操作接口   每一种C++版本通常还包含其他一些与I/O相关的库,提供特定系统的某些功能。我们所熟悉的输入输出操作分别是由istream(输入流)和ostream(输出流)这两个类提供的,为了允许双向的输入/输出,由istream和ostream派生出了iostream类。 类的继承关系见下图:   输出主要由重载的左移操作符(<<)来完成,输入主要由重载的右移操作符(>>)完成: 例如:一句输出语句:cout<<”ZCShou”;,事实上调用的就是 ostream & operator<< (ostream &temp,char *ps);   这个运算符重载函数,由于返回的是流对象的引用,引用可以作为左值使用,所以当程序中有类似 cout<<” ZCShou “<<””;这样的语句出现的时候,就 能够构成连续输出

    C++标准流

    流含义默认设备cin标准输入键盘cout标准输出屏幕cerr标准错误输出(非缓冲)屏幕clogcerr的缓冲版本(缓冲)屏幕

      C++中的cin、cout、cerr和C的stdin、stdout、stderr相对应。   其中cin为istream_withassign流类的对象,后3个为ostream_withassign流类的对象。预定义的流对象只能进行有格式的输入、输出。利用cin和cout可实现DOS下的管道和输入、输出转向功能。   C++附加流:win、wout、werr、wlog它们都是宽字符版本的标准流,宽字符类型为wchar_t,一般为16位。

    格式控制函数

      使用方法: 对象.函数名(); // 注:同样适用于文件流和字符串流

    函数功能long flags( long lFlags );用参数lFlags更新标志字long flags() const;返回标志字long setf( long lFlags );设置lFlags指定的标志位long unsetf( long lFlags );将参数lMask指定的标志位清0int width( int nw );设置下一个输出项的显示宽度为nwchar fill( char cFill );空白位置以字符参数cFill填充int precision( int np );用参数np设置数据显示精度

      各函数的详细说明:

    fmtflags setf( fmtflags flags );

      该函数返回格式标记先前的并开启由flags指定的那些标记。 eg: cout.setf( ios::showpoint ); cout.setf( ios::showpos ); 或者cout.setf( ios::showpoint | ios::showpos );

    void unsetf( fmtflags flags );

      flags指定的标记被清除 eg: cout.setf( ios::uppercase | ios::scientfic ); cout.unsetf( ios::uppercase );

    fmtflags flags();

      返回每个格式标记的当前位置。

    fmtflags flags( fmtflags f );

      设置某个流的所有标记。 eg: ios::fmtflags f = ios::showpos | ios::showbase | ios::oct | ios::right; cout.flags( f );//set all flags

    streamsize width( streamsize w );

      修改最小域宽,w是将要改成的域宽,先前的域宽返回。

    streamsize precision( streamsize p );

      当输出浮点型时,可以使用precision()函数来确定数字的精确位数。

    char fill( char ch );

      填充指定字符,默认情况下是空格

    C++操作算子(区别于以上部分成员函数)

    //注:同样适用于文件流和字符串流///

    操作算子用途输入/输出boolalpha开启boolapha标记输入/输出dec开启dec标记输出endl输出一个换行符并刷新流输出ends输出一个null输出fixed开启fixed标记输出flush刷新一个流输出hex开启hex标记输出/输入internal开启internal标记输出left开启left标记输出noboolalpha关闭noboolalpha标记输入/输出noshowbase关闭showbase标记输出noshowpoint关闭showpoint标记输出noshowpos关闭showpos标记输出noskipws关闭skipws标记输入nounitbuf关闭unitbuf标记输出nouppercase关闭uppercase标记输出oct开启oct标记输入/输出resetiosflags( fmtflags f )关闭f中指定的标记输入/输出right开启right标记输出scientific开启scientific标记输出setbase( int base )将基数设为base输入/输出setfill( int ch )将填充字符设置为ch输出setiosflags( fmtflags f )开启f中指定的标记输入/输出setprecision( int p )设置字符精度输出setw( int w )将域宽设置为w输出showbase开启showbase标记输出showpoint开启showpoint标记输出showpos开启showpos标记输出skipws开启skipws标记输入unitbuf开启unitbuf标记输出uppercase开启uppercase标记输出ws跳过开始的空格输入

    注意: 1. 在访问带参数的操作算子,必须在程序中包括

    ostream& operator<<( ostream& stream, class_type obj ) { //body of inserter return stream; }

    创建自己的析取器:

    istream& operator>>( istream& stream, class_type obj ) { //body of extractor return stream; }

    C++文件流

    数据的层次结构   Bit – Byte – Field – Record – File – DBMS   和文件有关系的输入输出类主要在fstream.h这个头文件中被定义,在这个头文件中主要被定义了三个类,由这三个类控制对文件的各种输入输出操作,他们分别是ifstream、ofstream、fstream,其中fstream类是由iostream类派生而来,他们之间的继承关系见下图所 示。   由于文件设备并不像显示器屏幕与键盘那样是标准默认设备,所以它在fstream.h头文件中是没有像cout那样预先定义的全局对象,所以我们必须自己定义一个该类的对象,我们要以文件作为设备向文件输出信息(也就是向文件写数据),那么就应该使用ofstream类。   ofstream类的默认构造函数原形为: ofstream::ofstream( const char *filename, int mode = ios::out, int openprot = filebuf::openprot );

    filename:  要打开的文件名 mode:   要打开文件的方式 prot:    打开文件的属性   其中mode和openprot这两个参数的可选项表见下表: 属性含义ios::app以追加的方式打开文件ios::ate文件打开后定位到文件尾,ios:app就包含有此属性ios::binary以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文ios::in文件以输入方式打开ios::out文件以输出方式打开ios::trunc如果文件存在,把文件长度设为0

    可以用“或”把以上属性连接起来,如ios::out|ios::binary。 openprot属性表

    属性含义0普通文件,打开访问1只读文件2隐含文件4系统文件

      可以用“或”或者“+”把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。   注意:ios::app为追加模式,在使用追加模式的时候同时进行文件状态的判断是一个比较好的习惯。 示例如下:

    #include <iostream> #include <fstream> using namespace std; int main() { ofstream myfile("c:\\1.txt",ios::app,0); if(!myfile)//或者写成myfile.fail() { cout<<"文件打开失败,目标文件状态可能为只读!"; system("pause"); exit(1); } myfile<<"ZCShou"<<endl<<""<<"http://blog.csdn.net/zcshoucsdn"<<endl; myfile.close(); }

    读写文件

      读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式

    文本文件的读写

      文本文件的读写很简单:用插入器(<<)向文件输出;用析取器(>>)从文件输入。假设file1是以输入方式打开,file2以输出打开。示例如下: file2<<”I Love You”;//向文件写入字符串”I Love You” int i; file1>>i;//从文件输入一个整数值。 这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些

    操纵符功能输入/输出dec格式化为十进制数值数据输入和输出endl输出一个换行符并刷新此流输出ends输出一个空字符输出hex格式化为十六进制数值数据输入和输出oct格式化为八进制数值数据输入和输出setpxecision(int p)设置浮点数的精度位数输出

      比如要把123当作十六进制输出:file1<

    二进制文件的读写

    ①put()   put()函数向流写入一个字符,其原型是ofstream &put( char &ch ),使用也比较简单,如file1.put(‘c’);就是向流写一个字符’c’。 ②get()   get()函数比较灵活,有3种常用的重载形式: 一种就是和put()对应的形式: ifstream &get( char &ch );   功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x);表示从文件中读取一个字符,并把读取的字符保存在x中。 另一种重载形式的原型是: int get();   这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get();和上例功能是一样的。 还有一种形式的原型是: ifstream &get( char *buf, streamsize num, char delim=’n’ );   这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符’n’。例如: file2.get(str1,127,’A’);//从文件中读取字符到字符串str1,当遇到字符’A’或读取了127个字符时终止。 ③读写数据块   要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下: istream & read( unsigned char *buf, streamsize num ); ostream & write( const unsigned char *buf, streamsize num );   read()从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 streamsize gcount();来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。   streamsize是由C++库定义的类型–是某种类型,它可以存储能够被任何一种I/O操作转换的最大字符数。

    例: unsigned char str1[]="I Love You"; int n[5]; ifstream in("xxx.xxx"); ofstream out("yyy.yyy"); out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中 in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换 in.close();out.close();

    各成员函数的详细和说明:

    注:除open函数外同样适用于文件流和字符串流// 1、 void ifstream::open( const char*filename, ios::opennode mode = ios::in ); void ofstream::open( const char*filename, ios::openmode mode = ios::out | ios::trunk ); void fstream::open( const char* filename, ios::openmode mode = ios::in | ios::out ); mode的含义:

    属性含义ios::app使所有输出到相应文件的内容都添加到文件末尾,只能用于具有输出功能的文件。ios::ate使得在打开文件时能够定位到文件末尾ios::binary可以以二进制文件打开,默认情况下,所有文件都以文本方式打开。ios::in指定为输入ios::out指定我输出ios::trunc销毁具有相同名字的先前文件的内容,并且将文件长度截断为0,当使用ofstream创建一个输出流时任何先前存在的具有该文件名的文件将被自动截断 eg: if( !mystream ) { cout<<"Cannot open file.\n"; //handdle error } if( !mystream.is_open() ) { cout<<"Cannot open file.\n"; //handdle error }

    get()的三种重载版本:

    istream& get( char* buf, streamsize num );   把字符读入由buf指向的数组,直到读取到第num-1个字符,发现了一个换行符或者遇到了文件尾。指针buf所指向的数组以null字符结束。 istream& get( char* buf, streamsize num, char delim );   把字符读入由buf指向的数组,直到读取到第num-1个字符,发现了由delim指定的字符或者是遇到了文件尾。指针buf所指向的数组以null字符结束。如果在输入流中遇到分隔符字符,则不会提取该字符。 int get();   返回相应流的下一个字符,如果遇到文件尾,则返回EOF。get()函数的这种形式类似于C的函数。eg: char ch = in.get();

    istream& getline( char* buf, streamsize num );

      把字符读入由buf指向的数组,直到读取到第num-1个字符,发现了一个换行符或者遇到了文件尾。指针buf所指向的数组以null字符结束。如果在出输入流中遇到换行符是,则提取该字符,但是不会将其放入buf

    istream& getline( char* buf, streamsize num, char delim );

      把字符读入由buf指向的数组,直到读取到第num-1个字符,发现了由delim指定的字符或者是遇到了文件尾。指针buf所指向的数组以null字符结束。如果在输入流中遇到分隔符字符,则提取该字符,但是不将其放入buf

    bool eof();

    到达文件尾时,该函数返回true,否则返回false

    istream & ignore( streamsize num = 1, int_type delim = EOF );

      该函数读取和放弃字符,直到num个字符被忽略( 默认值为1 )或者遇到delim指定为字符( 默认值为EOF )。常用来清除以回车结束的输入缓冲区的内容,消除上一次对下一次的影响。Eg:cin.ignore( 1024, ‘\n’ ); 通常把第一个参数设置的足够大

    int_type peek();

      返回流中的下一个字符,如果遇到文件尾,则返回EOF( int_type 被定义为某种整数型类型 )

    istream& putback( char c );

      返回流中的最后一个字符,c是读取最后一个字符

    ostream& flush();

      在缓冲区被写满数据前强行将数据写到磁盘

    istream& seekg( off_type offset, seekdir origin );

    ostream& seekp( off_type offset, seekdir origin );

      seekg()函数可以把相关文件当前的获取指针从指定origin出偏移offset个字符,正数表示向后移动,负数表示向前移动。origin必须是一下三个值中的一个:

    值含义ios::beg文件头ios::cur当前位置ios::end文件尾

      seekp()函数可以把相关文件当前的获取指针从指定origin出偏移offset个字符,origin必须是以上三个值之一

    off_type是ios定义的一个整数类型,可以包含offset具有的最大有效值,seekdir是一个ios定义的枚举类型,用来决定查找方式。

    pos_type tellg();

    pos_type tellp();

      确定每一个文件指针的位置。pos_type是ios定义的类型,它存储函数可以返回最大值。

    int sync();

    清空输入缓冲区的内容

    streamsize gcount() const

    C++字符串流

    strstream: ostrstream 和 istrstream(标准发布后被删掉,已被取代)

      简单的理解就是能够控制字符串类型对象进行输入输出的类,C++不光可以支持C++风格的字符串流控制,还可以支持C风格的字符串流控制。   我们先看看看C++是如何对C风格的字符串流进行控制的,C中的字符串其实也就是字符数组,字符数组内的数据在内存中的位置的排列是连续的,我 们通常用 char str[size]或者char *str的方式声明创建C风格字符数组,为了能让字符数组作为设备并提供输入输出操作,C++引入了ostrstream、istrstream、 strstream这三个类,要使用他们创建对象就必须包含strstream.h头文件。

    istrstream类用于执行C风格的串流的输入操作,也就是以字符串数组作为输入设备。 ostrstream类用于执行C风格的串流的输出操作,也就是以字符串数组作为输出设备。 strstream类同时可以支持C风格的串流的输入输出操作。

      istrstream类是从istream(输入流类)和strstreambase(字符串流基类)派生而来,ostrstream是从 ostream(输出流类)和strstreambase(字符串流基类)派生而来,strstream则是从iostream(输入输出流类)和和 strstreambase(字符串流基类)派生而来。他们的继承关系如下图所示:

      串流同样不是标准设备,不会有预先定义好的全局对象,所以不能直接操作,需要通过构造函数创建对象。 类istrstream的构造函数原形如下: istrstream::istrstream(constchar *str, int size);   参数1表示字符串数组,而参数2表示数组大小,当size为0时,表示istrstream类对象直接连接到由str所指向的内存空间并以\0结尾的字符串。   下面的示例代码就是利用istrstream类创建类对象,制定流输入设备为字符串数组,通过它向一个字符型对象输入数据。代码如下:

    #include <iostream> #include <strstream> using namespace std; int main() { char *name = "ZCShou"; int arraysize = strlen(name)+1; istrstream is(name,arraysize); char temp; is>>temp; cout<<temp; system("pause"); }

      类ostrstream用于执行串流的输出,它的构造函数如下所示: ostrstream::ostrstream( char *_Ptr, int streamsize, int Mode = ios::out ); 第一个参数是字符数组,第二个是说明数组的大小,第三个参数是指打开方式。

    #include <iostream> #include <strstream> usingnamespace std; int main() { int arraysize=1; char *pbuffer=new char[arraysize]; ostrstream ostr(pbuffer,arraysize,ios::out); ostr<<arraysize<<ends;//使用ostrstream输出到流对象的时候,要用ends结束字符串 cout<<pbuffer; delete[] pbuffer; system("pause"); }

      上面的代码中,我们创建一个c风格的串流输出对象ostr,我们将arraysize内的数据成功的以字符串的形式输出到了ostr对象所指向的 pbuffer指针的堆空间中,pbuffer也正是我们要输出的字符串数组,在结尾要使用ends结束字符串,如果不这么做就有溢出的危险。

    ZC·Shou 认证博客专家 砖家 码字员 没了 进步始于交流,收获源于分享!进步始于交流,收获源于分享!进步始于交流,收获源于分享!进步始于交流,收获源于分享!进步始于交流,收获源于分享!
    转载请注明原文地址: https://ju.6miu.com/read-23756.html

    最新回复(0)