"Hello World!",是大多数程序员学习一门语言的时候写的第一个程序,我学C++的时候也不例外。当随着学习的深入,我们会发现不得不回头重新审视这个简单的程序,因为它背后隐藏着许多细节方面的知识,对于高手来说,也许是不值一提,但对于我们这些菜鸟来说,这些细枝末节的知识点却是我们走向大牛必须迈过的艰难,因为很少有书会讲这些。
讲的啰嗦了点,本文是讲输入输出的小细节的,前几篇文章陆陆续续讲了缓冲区,文本文件和二进制文件,回车换行等等。。。这篇文章就对上面的知识做个总结贯通。
输入输出其实是狭义的信息交换,一端的信息通过管道流到另一端,并展示或者保存起来。因为计算机是以位保存信息的,所以计算机传递信息可以以位为单位传播,这毫无疑问是没问题的。但是人类能够看懂的并不是位,而是一个一个字符(仅以英文为例),所以C++的输入输出是以字节为单位的,以为ASCII是用一个字节表示一个字符的。稍微提前点讲一句,即使是二进制文件,也是以字节为单位的。
前几篇文章有一篇是介绍缓冲区的,缓冲区是存放了要输出或输入的字节信息。处理器并不直接与IO设备交换信息,而是直接与缓冲区打交道。
正文:
C++的输入输出主要包括三个部分:标准输入输出流、文件流和字符串流,大多数书上是这么说的,但解释清楚或者解释的比较直白的比较少。标准输入输出其实是与控制台(屏幕)和键盘打交道要用到的一系列类和对象,文件流则是与文件相关的一系列类和对象,同理字符串流,但其实这三类都属于输入输出流。你可以这样想象,这三个是的出发点都是处理器,当然中间都有缓冲区,但终点或目的地不同,分别是(屏幕和键盘)、文件和字符串。
我们先讲解目的地或终点是屏幕和键盘的这类流,我们知道计算机存储是以二进制的方式存储的,我们人类读东西是以字符读取的,当然我们也可以看懂二进制,但是只有纯数字的我们才能转换为相等的二进制表示,例如10=0x0000000A(int是四个字节),但是A却没有纯粹的二进制表示,这时候我们就自己定义了一套表示方法ASCII,人为把A用二进制表示为0x41。看样子到这时候天下太平统一,但是不然,这时候会产生一个混乱,那我要存储10ABCD45678到内存或外存的时候(外存和内存是一样的),遇到数字的时候,例如遇到10我是看做0x00000000A这形式,将它存到内存或文件,还是将10看做两个字符,分别是1和0字符,以0x31和0x30(1和0的ASCII值)方式存到内存或外存。其实这个问题就是二进制文件和文本文件的区别的由来,其实字母字符ABSHTY等这些,只能用ACSII来表示,但是数字却有两种方式,int、float和字符表示。那这和标准输入输出什么关系啊,这不应该是文件流要讲的么?对的,文件流才会遇到这个问题,标准输入输出流不会遇到这个问题,其实不是不会遇到,而是编译器或者库文件帮我们统一了,即标准输入输出流是文本文件的形式,即将所有的输入输出看做是字符形式。例如cout<<10<<endl;这个语句是输入int型的10这个数值到屏幕显示,当然,首先它输入到缓冲区,缓冲区可以看做是内存,那10是以什么形式存放到缓冲区的呢?答案是字符形式,即缓冲区存放的是0x3130,然后屏幕在读取缓冲区的内容,显示出来。因为屏幕读取缓冲区的内容,默认或只能以字符形式读取,就是,以每个字节为单位读取,然后根据ASCII翻译成字符,输出这个字符的图形。那怎么证明是这样的呢?我们可以以二进制的方式将内容输出到缓冲区,即cout.write((char*)&a,4);,a是int型,是10。缓冲区存放的不再是两个字节的0x3130,而是四字节的0x0000000A,但是屏幕不管你以什么方式写到缓冲区,它读取缓冲区的时候,仍然会以字节为单位读取,并翻译成字符,那么0x0000000A就代表四个字符,分别是回车和三个空字符。键盘呢?键盘也是类似的,你键盘输入的也是字符,然后将字符的ASCII码值放在缓冲区,等待cin去提取,有人会问,cin是可以直接提取整型的,例如:int a;cin>>a。如果我输入的是10,那么其实缓冲去存放的是0x3130,那么我提取会出错,即使我能提取到3130,但int是四个字节的,也还差两个字节,其实这个时候cin>>帮你完成了转换,从字符到整型的转换。
而遇到文件的话,你得自己定义,你是以二进制方式打开还是以文本方式打开,其实 以二进制文件形式打开,读写只能用成员函数write和read,如果你以文本文件的形式打开,其实也是可以以文本文件的方式读取的。
总结起来就是这样子的,当你和屏幕或键盘传消息,和屏幕的话,肯定是输出流,如果你用插入符<<,则你插入缓冲区的都是字符流,即例如你cout<<10,这是插入缓冲区的是两个字符1和0,内存是两个字节是0x31和0x30,然后当缓冲区刷新的时候,你看到是1和0两个字符;当你不用<<想缓冲区输入流,而是以write函数,则在缓冲区流进的数据不会改变,还是二进制的10,即四个字节的内容(当然,write函数的用法自己去百度),然后缓冲区刷新,还是每个字节翻译成字符的读取,10的话,在屏幕上显示的是一个换行符,即回车换行的操作。因为10是回车的ASCII值,当然,还有三个为0的字节,都是空字节。
然后是键盘输入,你输入流进入的缓冲区都是字符流,即你输入A,在缓冲区的形成一个字节的数是0x41,你输入1,则缓冲区还是一个字节的数0x31,并不会是四个字节的int型的内存表现形式。主要取决于你怎么去读取缓冲中的内容,你是以字符流的方式提取,还是以二进制的方式提取。以字符流的方式则是根据cin>>a,a的类型来决定,例如,你键盘输入了123ABC(空格)3d,这个在缓冲区的内存存储形式是0x31_32_33_41_42_43_20_33_64,下划线为人为添加的分隔符号,如果a是int型,那么提取符号一直提取到A为止,其实是fail函数为真(可不用理解),那么你提取了123,但你不能把123在缓冲区对应的内容直接搬迁到存储a的内存中去,因为a是四个字节的,123在缓冲区也只占3个字节,即使假设你提取的是1230是四个字节,也不行,那么最终显示a的值不是1230而是0x30313233这四个字节组成的int型,是一个很大的值,所以,cin>>a,提取了123三个字符以后,经过转换将123变为int的123,在传给a。如果二进制的方式读取的话,则是将缓冲区的内容原封不动的搬迁到你你要读取的地方。cin.write((char*)&a,4)。中间不会进行转换。
文件流的其实也是相似的,只根据你以什么方式写到文件中去(而并不依赖你以什么方式打开文件),如果文本方式的话,将你所写的东西都先转换成字符,放到缓冲区,然后在放到文件的内存中去,例如out<<123,虽然123 是int型的,但是先将123转换成"123",然后放到缓冲区,即缓冲区的是0x313233,而并不是123的四个字节的int型的,当你以二进制的方式写到文件的时候,才是将原封不动的123的int型放到缓冲,然后再从缓冲放到文件中去。
你以什么方式打开文件的话,这个就行屏幕,屏幕是怎么解释缓冲区传来的内容的?以字符流的方式,将每个字节翻译成一个字符,在屏幕上显示,举个例子,如何以二进制的方式输出到屏幕,显示A,则你得cout.write((char*)&a,1),a=65;你以文本文件的形式打开文件就行屏幕显示一样,其实也就是屏幕显示,打开文件是看文件,它也是将里面的内容以每个字节为单位根据ASCII翻译成字符,显示出来,如果当初你是以<<写的文件,那么就能对应起来,如果是以write函数写的文件,如果你写的字母,那么还好没什么影响,如果是write(65),那么显示的是A这个字符和三个空字符。
所以,综上,文本文件存储还是二进制文件存储取决于你用<<还是write()成员函数?如果,以<<的符号的方式,则将要存储的先翻译成字符,123翻译成"123",存放到缓冲区,如果以writ(),则将write的变量地址的值原封不动的放到缓冲区。如果是读取的话,也是不管文件的打开方式是什么,只取决于你用什么读取,用>>还是read()函数,如果用,当然键盘输入特殊了,你输什么,都是翻译成字符型放到缓冲区,如果你以>>方式的话,取决于你>>a,a的类型,如果是read的,则将缓冲区的内容原封不动的读取出来,放到a的内存中去。
所以<<和>>是一个相反过程,<<将东西翻译成字符放到缓冲区,>>a将缓冲区的内容看成是字符类型,然后根据a的类型,将字符翻译成a相应的类型。
read和write则是原封不动的将缓冲区的内容搬迁一下。
啰嗦了,不知道有没有讲明白,以后在进行更详细明了的总结改正吧。
1814



被折叠的 条评论
为什么被折叠?



