IO总结2-字节流及流转换
------- android培训、java培训、期待与您交流! ----------
说完了字符流的主要相关类,现在总结一下字节流的主要类:
FileOutputStream
三个主要的方法:
void write(int b) 写入一个int数字对应的byte,这点很奇怪:write(65535)和write(255)得到的结果是一致的,write方法只写入int b的最后面8位(一个byte)。需要注意一下。
void write(byte[] b) 写入一个byte数组。因为是字节流,所以在想用这个方法写入字符串到文件的时候,传入的参数应该是调用String类的getBytes方法将字符串转成byte数组再传入。
void write(byte[] b, int off, int len) 写入一个byte数组的制定起始脚标的长度。这个在拷贝文件或者网络传输的时候会常用的,因为需要数出的数据通常不会正好被byte数组b的长度整除,而是有多余的,这里就需要只输出多余的字节。
一个例子:
import java.io.*; class Test5 { public static void main(String[] args) throws IOException { FileOutputStream fos1 = new FileOutputStream("D:/abc1.txt"); for (int x = 1; x < 512; x += 16) { fos1.write(x); } fos1.close(); FileOutputStream fos2 = new FileOutputStream("D:/abc2.txt"); fos2.write("我是一个字符串".getBytes()); fos2.close(); } }
用2进制的方式打开abc1.txt,可以看到
第一行是1<x<255时的输出结果
第二行是255<x<512时的输出结果
两者的结果完全一致。
FileInputStream
int read() 从此输入流中读取一个数据字节。返回读到的byte的int形式,到文件末尾返回的是-1。
int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。返回的为读到的数组的长度。到文件末尾返回-1.
int read(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。返回的为读到的数组的长度。到文件末尾返回-1.
import java.io.*; class BinFileReadDemo { public static void main(String[] args) throws IOException { FileInputStream fis1 = new FileInputStream("D:/abc1.txt"); int num1; while ((num1 = fis1.read()) != -1) { System.out.print(Integer.toHexString(num1) + " "); } fis1.close(); System.out.println(""); System.out.println("--------------------------------------------------"); FileInputStream fis2 = new FileInputStream("D:/abc2.txt"); int num2; byte[] b = new byte[1024]; num2 = fis2.read(b); System.out.write(b, 0, num2); fis2.close(); } }
一段拷贝文件的代码
import java.io.*; class BinFileCopyDemo { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("D:/abc1.mp3"); FileOutputStream fos = new FileOutputStream("D:/abc2.mp3"); byte[] b = new byte[1024 * 1024]; int len; while ((len = fis.read(b)) != -1) { fos.write(b, 0, len); } fis.close(); fos.close(); } }
对于字节流,也有对应的增强类BufferedOutputStream和BufferedInputStream。
一个拷贝文件的例子:
import java.io.*; class Test5 { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); BufferedInputStream buffis = new BufferedInputStream( new FileInputStream("D:/jdk-7u9-windows-x64.exe")); BufferedOutputStream buffos = new BufferedOutputStream( new FileOutputStream("D:/jdk-7u9-windows-x64-2.exe")); int num; while ((num = buffis.read()) != -1) { buffos.write(num); } buffis.close(); buffos.close(); long end = System.currentTimeMillis(); System.out.println((end - start) + "毫秒"); } }
拷贝jdk-7u9-windows-x64.exe这个90M的文件用了差不多4s时间。而用FileInputStream拷贝(将byte数组设定为1024*1024)的时间却可以做到0.2秒,不懂为什么,超出物理极限了。
上面的例子都是死的,现在尝试一下由用户键盘输入做一些操作:
由用户输入字符串,代码将用户的键盘输入转成大写返回。
import java.io.*; class UpperCaseUserKeyboardInputDemo1 { public static void main(String[] args) throws IOException { InputStream in = System.in; int b = 0; StringBuilder sb = new StringBuilder(); while (true) { b = in.read(); if ((char) b == '\r') continue; if ((char) b == '\n') { String str = sb.toString(); if ("over".equals(str)) break; System.out.println(str.toUpperCase()); sb.delete(0, sb.length()); } else sb.append((char) b); } in.close(); } }
以上代码较为繁琐和臃肿,考虑到处理换行,可使用BufferedReader,而且键盘输入是字节流,还要考虑到字节流向字符流的转换,所以有了下面的代码:
import java.io.*; class UpperCaseUserKeyboardInputDemo2 { public static void main(String[] args) throws IOException { BufferedReader bufr = new BufferedReader(new InputStreamReader( System.in)); String line = null; while ((line = bufr.readLine()) != null) { if ("over".equals(line)) break; System.out.println(line.toUpperCase()); } bufr.close(); } }
将键盘录入转成大写后输出到一个文件中的例子:
import java.io.*; class Test5 { public static void main(String[] args) throws IOException { BufferedReader bufr = new BufferedReader(new InputStreamReader( System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter( new FileOutputStream("d:/www.txt"))); String line = null; while ((line = bufr.readLine()) != null) { if ("over".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine(); } bufr.close(); bufw.close(); } }
第一遍看毕老师的视频的时候,觉得输入输出流的问题很纠结,有点没有思路的感觉。在自己翻看API文档后,第二遍看毕老师的视频的时候,就觉得清晰很多了。毕老师总结的很好:
1、 明确源和目的。
2、 明确是字符还是字节。
3、 想使用哪种输入输出方式(及组合)。
其实文件在硬盘上都是2进制的,应该都用FileInputStream来读取。
文件:
我们想读取一个文本文件的时候,应该先用FileInputStream来读取文件,然后把字节流转成字符流来处理。但是有一个类帮助我们做了转换,它就是InputStreamReader,而且它有一个子类,是FileReader,并提供read方法来传出字符所对应的字节。所以直接用FileReader就好了。同时增强类BufferedReader还为我们提供了readLine方法便于我们操作。要想将字符串写入文件时,也有类FileWriter帮我们做了转换。
FileReader在读取文件的时候,是按照指定的字符集(并未提供方法指定字符集)进行转换后,变成字符的,如果需要指定读取文件编码的时候,就需要用到FileInputFileStream把文件转成字节流,然后用InputStreamReader指定其他需要的编码进行转换后再进行操作。对于需要指定编码写入文件的时候同理需要OutputStreamWriter和FileOutPuteStream来操作.
对于字节文件(程序、媒体文件等),由于不涉及到字节流向字符流(或者字符流向字节流)的转换,所以相对来说比较简单。用FileInputSteam读取,将读取到的字节流存在内存中,然后用FileOutPutStream输出即可。
比较特殊的是键盘和控制台显示,也就是System.out和System.in。
查阅文档后:System.out其实是System类中的一个成员,其类型为PrintStream,而 System.in的类型为InputStream(字节流),下面比较一下两个类:
System.err默认使用的也是控制台显示,所以其类型和System.out是一致的。相对System.in,System.out要复杂一些。主要是提供多种数据类型(char、char[]、String、int、long、float、double、居然还有object)的输出方式(print和println)。
另外说一点,System提供了setErr、setIn和setOut方法可将其默认流更改流向。