标签:
对于我一个Java小白在经历过无数次鄙视之后,痛定思痛决定好好学好Java。就从Java 的 IO 开始学起吧。
可是Java 的 IO真的很烦,如图。看到这样的继承关系,大部分初学者实在不知道从何学起。那就从最原始的部分开始试试。
第一个问题:什么是IO?
IO就是“外部世界”。
函数式编程会把IO的概念和程序的逻辑运算本身严格的区分开来。函数式语言里把含有IO的函数称为“非纯函数”。而完全没有IO,只是纯粹的运算的函数称为“纯函数”。纯函数的好处这里暂且按下不表。但是这种区分给了IO一个很宽泛却很全面的概念:IO就是纯函数与外部世界结合的途径。可以是从本地文件、网络socket、别的进程、外围设备、终端等等的地方读取数据的方式。数据一旦被读取,可能会被进一步加工,可能会被变换、映射、筛选,但是这些操作都不是IO,而是纯函数的操作。如果有一个信息总量的概念在IO里面,那么在发生IO的时候也就是产生信息的时候,后续的纯函数的操作都不能够增加信息的总量,当然,可以使数据变得更有序、更精练,这些是数据熵减小。
第二个问题:计算机世界的数据长什么样子?
0110110000110100011001111010011010100110101100101001111101100110100101011101001000010011110010101010100001100101010010100011000100100011
数据对计算机而言就长成这样子,一堆0和1的序列。当然,这一堆序列并不是数据本身,数据是一堆0和1的序列和解释这个序列的规则:
数据 = 数据编码 + 解码规则
我们的文本编辑器就是最常见的编码和解码器:我们在文本编辑器里面输入句子,点击保存,文本编辑器就把相应的句子编码成一大串01保存在硬盘,一旦我们打开这个文件,编辑器又把它解码成我们可读的文字。当然,有时候数据并不需要被解码,比如把一个文件拷贝到另一个地方,这个过程中的IO并不需要被解码,拷贝的程序简单起见就可以把一堆0和1直接输送到另一个地方去。此外,编码和解码少数情况下也可以由上层应用程序来实现。不过,Java这么强大,大部分常用的解码工作当然是有相应的IO对象来处理的。
第三个问题:Java怎么读数据?
0110110000110100011001111010011010100110101100101001111101100110100101011101001000010011110010101010100001100101010010100011000100100011
上面说计算机这样看待数据,其实也不全对。因为计算机可没耐心一个bit一个bit的读取数据,在计算机世界里,最小的操作单位是一个byte,就是由8个bit位组成的数据结构。所以,计算机其实是这样看待数据的:
01101100 00110100 01100111 10100110 10100110 10110010 10011110 10110011 01001010 11101001 00001001 11100101 01010100 00110010 10100101
Byte是计算机世界里最小的可操作对象,可以说计算机的编码是在byte上构建起的一系列更高抽象的数据结构。所谓的Java的强大的IO,就是对这些底层到更高抽象层的数据的编码和解码支持。
1,Byte
在java里,跟byte相关的数据一般称为stream,这是最小的可操作单元。Java里有InputStream和OutStream这两个抽象基类,其它的stream的类都继承自这两个基类。根据stream的源和汇的不同,可以分为FileInputStream(FileOutputStream)、ByteArrayInputStream(ByteArrayOutputStream)、PipedInputStream(PipedOutputStream)等等子类。这里用最常见的FileInputStream和FileOutputStream举例。
import java.io.*; public class ShowByte { public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("input.txt"); out = new FileOutputStream("output.txt"); int c; while ((c = in.read()) != -1) { out.write(c); System.out.print(c);
System.out.print(" "); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } } }
如果"input.txt"里面是 "a中" 这两个中英文字符的话,那么结果很可能是:
97 228 184 173 10
很显然:97对应的是‘a‘;"228 184 173"则是中文字“中”的UTF-8编码,可以看出它占了3个字节;10则是换行符。从这里可以看出,FileInputStream根本不管编码的具体意义,它做的只是最底层的将文件的每个字节依次读出来而已。至于对信息本身的解码,要么留给上层的程序,要么更常见的,用上层的Java对象进行包装。
2,Char
这里的char就已经可以看做是byte的进一步抽象,因为这里的char已经不再是没有意义的01组合,这里的char代表一个字符,人类文字的最小单元。而且,在Java里面char非常简单,它一定是两个字节的大小。因为在Java里所有的字符都是通过Unicode的编码方式,Unicode统一用两个字节编码字符。咦?那为什么刚刚那个例子里面‘中’有三个byte?那是因为文本编辑器存储在硬盘上的数据大都采用UTF-8的格式,这是一种可变字节的编码方式,对于英文字符它就是一个字节的ascii码,所以非常节省存储空间。然而同时,很多中文字符也被编码成2-4个不等的字节大小。
好了,那Java怎么读取Char呢?很显然,char是byte的进一步抽象,我们只要在上面的程序上再加一层wrapper(亦或者称为filter): InputStreamReader (OutputStreamWriter)。在java里,只要看到‘reader‘和‘writer‘之类的字眼就都是用来处理char的。这里的Reader是构建在FileInputStream之上的,闲话少说,直接上代码:
import java.io.*; public class ShowChar { public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("input.txt"); out = new FileOutputStream("output.txt"); InputStreamReader charStreamInput = new InputStreamReader(in); OutputStreamWriter charStreamOutput = new OutputStreamWriter(out); int c; while ((c = charStreamInput.read()) != -1) { charStreamOutput.write(c); System.out.print(c); System.out.print(" "); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } } }
可以看出这段程序只是在原来的基础上增加了InputStreamReader,但是同样的输入“a中”,结果就会有一点不一样:
97 20013 10
‘中’的字符变成了“20013”,而第一个程序是三个字节的“228 184 173”。什么鬼?也没啥,InputStreamReader保证是一个一个char来读的,‘中’字的unicode就是两个字节的“20013”。可是我刚刚不是说这个文件是以UTF-8编码的,读入两个字节难道不应该是“228 184” 的整数形式“58552”吗?(这里还要考虑什么Big-Endian,Small-Endian之类的编码区别),然后还有两个多余的“173”?哈哈,Java当然不会这么蠢让程序员去考虑这些细节的问题。在使用InputStreamReader之后,这个对象已经考虑了从UFT-8到Unicode之间的转换,保证每读到一个字符就一定是两个字节的Unicode的字符。也就是说,它很聪明的知道了文件是UTF-8格式的,然后知道“228 184 173”实际上就是一个字符,然后把这个字符转成了Unicode的形式,然后打出来。
读取字符这样的工作经常要用到,当然应该有一个更好用的对象来封装这两个底层的对象。当然有:FileReader是也!
FileReader就直接把读取和转化放在一件事里面做到了,使得这样的操作就更令人愉快了。代码变成:
import java.io.*; public class ShowChar { public static void main(String[] args) throws IOException { FileReader in = null; FileWriter out = null; try { in = new FileReader("input.txt"); out = new FileWriter("output.txt"); int c; while ((c = in.read()) != -1) { out.write(c); System.out.print(c); System.out.print(" "); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } } }
这下简单一点了,对吧?
.............
后面不想写了,因为发现有个博客已经写的很好了,我就姑且写到这吧。
http://blog.csdn.net/yczz/article/details/38761237
标签:
原文地址:http://www.cnblogs.com/renruyi/p/4888525.html