I/O篇(3)——各I/O类的用法
来源:互联网 发布:zuk z2 网络较慢 编辑:程序博客网 时间:2024/06/05 17:39
一.常用节点流
1.FileInputStream/FileOutputStream(FileReader/FileWriter类似,所以只给出一组类的例子)
举例:用FileInputStream/FileOutputStream实现文件的拷贝
public static void copyFile(String srcPath,String destPath) { //建立源文件(存在且为文件) File src = new File(srcPath); //建立目的文件(文件可以不存在) File dest = new File(destPath); if(!src.isFile()){ throw new RuntimeException("给定的源不是文件"); } InputStream is = null; OutputStream os = null; try { is = new FileInputStream(src); os = new FileOutputStream(dest); byte[] temp = new byte[1024]; int len = 0; //循环读取,写出 while(-1 != (len = is.read(temp, 0, temp.length))){ os.write(temp, 0, len); } //结束前刷新,把缓冲中的内容全部写出 os.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ //关闭流 try { if(null != os){ os.close(); } if(null != is){ is.close(); } } catch (IOException e) { e.printStackTrace(); } } }
2.ByteArrayInputStream/ByteArrayOutputStream(CharArrayReader/CharArrayWriter类似,所以只给出一组类的例子)(有新增方法,一般不用多态)以数组为物理节点的节点流,字节流以字节数组为节点ByteArrayInputStream/ByteArrayOutputStream,字符流以字符数组为节点CharArrayReader/CharArrayWriter, 这种数组流除了在创建节点流需要传入数组外,用法上与其他节点流完全类似.数组流类似还可以使用字符串作为物理节点,用于实现从字符串中读取字符串(String)java.io.StringReader, 或将内容写入字符串(StringBuffer)java.io.StringWriter.
(1)把文件写入字节数组
public static byte[] readFile2ByteArray(String srcPath) { File src = new File(srcPath); InputStream is = null; ByteArrayOutputStream bos = null; byte[] dest = null; try { is = new BufferedInputStream(new FileInputStream(src)); bos = new ByteArrayOutputStream(); byte[] temp = new byte[1024]; int len = 0; while (-1 != (len = is.read(temp))) { bos.write(temp, 0, len); } bos.flush(); dest = bos.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if(null != bos){ bos.close(); } if(null != is){ is.close(); } } catch (IOException e) { e.printStackTrace(); } } return dest; }
(2)把字节数组输出文件
public static void writeFileFromByteArray(byte[] src, String destPath) { File dest = new File(destPath); InputStream is = null; OutputStream os = null; try { is = new BufferedInputStream(new ByteArrayInputStream(src)); os = new BufferedOutputStream(new FileOutputStream(dest)); byte[] buf = new byte[1024]; int len = 0; while (-1 != (len = is.read(buf))) { os.write(buf, 0, len); } os.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (null != os) { os.close(); } if (null != is) { is.close(); } } catch (IOException e) { e.printStackTrace(); } } }
二.常用处理流
1.BufferedInputStrean/BufferedOutputStream(BufferedReader/BufferedWriter类似,所以只给出一组类的例子)(BufferedReader和BufferedWrite类有新方法,一般不用多态)
这四个缓冲流增加了缓冲功能, 可以提高IO效率, 但是需要使用flush()才可以将缓冲区的内容写入实际的物理节点.
计算机访问外部设备非常耗时。访问外存的频率越高,造成CPU闲置的概率就越大。为了减少访问外存的次数,应该在一次对外设的访问中,读写更多的数据。为此,除了程序和流节点间交换数据必需的读写机制外,还应该增加缓冲机制。缓冲流就是每一个数据流分配一个缓冲区,一个缓冲区就是一个临时存储数据的内存。这样可以减少访问硬盘的次数,提高传输效率。
举例:用BufferedInputStrean/BufferedOutputStream实现文件的拷贝
public static void copyFile(String srcPath, String destPath) { File src = new File(srcPath); File dest = new File(destPath); BufferedReader is = null;//用子类的方法就不能用多态了 BufferedWriter os = null; try { is = new BufferedReader(new FileReader(src)); os = new BufferedWriter(new FileWriter(dest)); String line = null; while(null != (line = is.readLine())){ os.write(line); os.newLine();//换行符 } os.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(null != os){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if(null != is){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } }
2.InputStreamReader/OutputStreamWriter
Java I/O流体系提供了两个转换流, 用于实现将字节流转换成字符流:
- java.io.InputStreamReader将字节输入流转换成字符输入流
- java.io.OutputStreamWriter将字节输出流转换成字符输出流
一般用来处理乱码问题,这里插入一个编码与解码的概念:
- 编码:字符–编码字符集–>二进制(字节)
- 解码:二进制(字节)–解码字符集–>字符
如果二者用的字符集不相同,那么就会出现乱码问题
举例:用InputStreamReader/OutputStreamWriter实现文件的拷贝(如果不用转换流,文件的拷贝可能会出现乱码问题,用了转换流就可以避免)
public static void main(String[] args) { String srcPath = "D:/test01.txt"; String destPath = "D:/testcp10.txt"; File src = new File(srcPath); File dest = new File(destPath); BufferedReader br = null; BufferedWriter bw = null; try { br = new BufferedReader(new InputStreamReader( new FileInputStream(src),"UTF-8")); bw = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(dest),"UTF-8")); String info = null; while(null != (info = br.readLine())){ bw.write(info); bw.newLine(); } bw.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(null != bw){ try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if(null != br){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
3.RandomAccessFile和SequenceInputStream
(1)RandomAccessFile
java.io.RandomAccessFile与普通的Java I/O流不同的是, 他可以支持随机访问文件, 程序可以直接跳转到文件的任意地方读写数据(因此如果只是访问文件的部分内容,或向已存在的文件追加数据, RandomAccessFile是更好的选择).但RandomAccessFile也有一个局限就是只能读写文件, 不能读写其他IO节点.
RandomAccessFile对象包含了一个记录指针, 用以标识当前读写位置, 当程序新建一个RandomAccessFile对象时, 记录指针位于文件头, 当读/写n个字节后, 指针将会向后移动n个字节.除此之外RandomAccessFile还可以(前/后)自由移动该记录指针,RandomAccessFile提供了如下方法来操作文件记录指针:
RandomAccessFile既可以读文件, 也可以写文件, 所以他提供了完全类似于InputStream的read()方法, 也提供了完全类似于OutputStream的write()方法, 此外他还包含了一系列的readXxx() writeXxx()来完成IO(其实这几个操作是DataInput DataOutput接口来提供的, DataInputStream/DataOutputStream ObjectInputStream/ObjectOutputStream也实现了这两个接口).
在构造一个RandomAccessFile时, 需要提供一个String mode参数, 用于指定RandomAccessFile的访问模式, 该参数有如下取值:
注意: RandomAccessFile不能直接向文件的指定位置插入数据,不然新插入的数据会覆盖文件的原内容.如果需要向指定的位置插入数据,程序需要先把指定插入点后的内容读入缓冲区, 等把需要插入的数据写入完成后, 再将缓冲区的内容追加到文件后面.
(2)SequenceInputStream
这是一个合并流,将多个输入流合并成一个SequenceInputStream流对象,然后再对SequenceInputStream流对象进行操作,也就是将多个InputStream合并合成一个InputStream
该对象的其中一个构造方法:SequenceInputStream(Enumeration
public class SplitFile16 { public static void main(String[] args) throws IOException { String destPath = "D:/test16cp.txt"; String srcPath = "D:/test01.txt"; String destPath2 = "D:/test16cp2.txt"; File src = new File(srcPath); SplitFile16 sf = new SplitFile16(src, 6); try { sf.split(); } catch (IOException e) { e.printStackTrace(); } sf.mergeFile(destPath); sf.mergeFile2(destPath2); } // 要被切割的文件(源文件) private File src; // 源文件的长度 private int srcLength; // 源文件的文文件名 private String srcName; // 要切割成的块数 private int blockNum; // 每块子文件的长度 private int destLength; // 切割文件指针,表示当前文件头的位置 private int current; // 记录当前切割文件的块数 private int currentBlock; // 最后一块的文件长度 private int finalLength; // 子文件名 private ArrayList<String> destName; // 分割后的文件集合,只是为了方便合并 private ArrayList<File> destFiles; public SplitFile16(File src, int blockNum) { if (blockNum < 1) { throw new RuntimeException("分割块数必须大于1"); } if (!src.exists()) { throw new RuntimeException("传入的文件必须存在"); } if (!src.isFile()) { throw new RuntimeException("传入的必须是文件"); } this.src = src; this.blockNum = blockNum; init(); } // 初始化所有参数 private void init() { srcLength = (int) src.length(); srcName = src.getName(); destLength = (int) Math.ceil(srcLength * 1.0 / blockNum); finalLength = srcLength - destLength * (blockNum - 1); current = 0; currentBlock = 1; destFiles = new ArrayList<File>(); creatDestName(); } // 生成子文件名列表 private void creatDestName() { destName = new ArrayList<String>(); String ParentPath = src.getParent(); for (int i = 0; i < blockNum; i++) { destName.add(ParentPath + srcName + i); } } // 分割文件 public void split() throws IOException { while (currentBlock <= blockNum) { File dest = new File(destName.get(currentBlock - 1)); destFiles.add(dest); //这是为了合并文件方便,所以加的 RandomAccessFile raf = new RandomAccessFile(src, "rw"); OutputStream os = new BufferedOutputStream(new FileOutputStream(dest)); raf.seek(current); //设置下次读取文件时的开头位置 byte[] temp = new byte[destLength]; int length = destLength; // 再写最后一块文件的时候,应该判断文件剩余量是否比计算出的小 if (currentBlock == blockNum) { length = finalLength; } while (-1 != raf.read(temp)) { os.write(temp, 0, length); os.flush(); currentBlock++; current += destLength; break; } os.close(); raf.close(); } } // 合并文件 public void mergeFile(String destPath) throws IOException { for (int i = 0; i < destFiles.size(); i++) { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(destFiles.get(i))); //FileOutputStream构造时的二个参数应改为true,可以在文件结尾追加写入 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath,true)); byte[] temp = new byte[1024]; int len = 0; while(-1 != (len = bis.read(temp))){ bos.write(temp, 0, len); } bos.flush(); bos.close(); bis.close(); } } //合并文件,这是通过SequenceInputStream来合并 public void mergeFile2(String destPath) throws IOException { Vector<InputStream> vc = new Vector<InputStream>(); for(int i = 0 ; i < blockNum ; i++){ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(destFiles.get(i))); vc.add(bis); } //这里用SequenceInputStream来合并文件 SequenceInputStream sis = new SequenceInputStream(vc.elements()); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath,true)); byte[] temp = new byte[1024]; int len = 0; while(-1 != (len = sis.read(temp))){ bos.write(temp, 0, len); } bos.flush(); bos.close(); sis.close(); }}
4.PrintStream和PrintWriter
System类中有三个属性in,out,err
(1)输入流(由键盘输入):System.in(是一个InputStream)
(2)输出流(都是PrintStream流):
- 标准输出流(输出到控制台):System.out
- 标准错误输出流(输出到控制台):System.err
public class PrintStreamTest14 { public static void main(String[] args) throws FileNotFoundException { //这是测试System.out的类型是PrintStream System.out.println("锄禾日当午"); PrintStream ps = System.out; ps.println("码农真辛苦"); //用PrintStream流输出到文件 ps = new PrintStream(new BufferedOutputStream(new FileOutputStream(new File("D:/text14.txt")))); ps.println("一本小破书"); ps.flush(); //这是测试System.in的类型是InputStream和键盘输入方法 InputStream is = System.in; Scanner sc = new Scanner(is); System.out.println("请输入:"); System.out.println(sc.nextLine()); //上面是键盘输入,这里我们要用文件进行输入 is = new BufferedInputStream(new FileInputStream(new File("D:/text14.txt"))); Scanner sc2 = new Scanner(is); System.out.println(sc2.nextLine()); //把System.out重定向也就是输出重定向,让它输出到文件中(System类中有相应的方法,setIn()和setOut()) System.setOut(new PrintStream(new FileOutputStream(new File("D:/text1402.txt")),true)); System.out.println("锄禾日当午"); //把重定向后的System.out重定向回控制台,这里注意把FileDescriptor理解为控制台,java中把控制台也写成了一个类,FileDescriptor.out是一个OutputStream,而FileDescriptor的建立必须借助FileInputStream或FileOutputStream System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); System.out.println("一读一上午"); }}
5.DataInputStream/DataOutputStream(这两个类有新增方法一般不用多态)
基本数据类型处理流,提供了可以存取所有java基本类型数据和String类型的方法,他们允许按照java的基本数据类型读写流中的数据,读取的是java的基本数据类型(FileInputStream读取的是字节)
public class DataByteTest12 { public static void main(String[] args) {// String destPath = "D:/test12.txt";// File dest = new File(destPath);// writeTest(dest);// readTest(dest); //上面是测试另一组方法 String str = "这个东西第一次写的时候我纠结了1个小时"; byte[] b = write2ByteArray(str); readFromByteArray(b); } //把基本数据类型写入Byte数组 public static byte[] write2ByteArray(String str){ byte[] dest = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos)); try { dos.writeUTF(str); dos.flush(); dest = baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); }finally{ if(null != dos){ try { dos.close(); } catch (IOException e) { e.printStackTrace(); } } } return dest; } //从字节数组中按照基本数据类型读取数据 public static void readFromByteArray(byte[] src){ ByteArrayInputStream bais = new ByteArrayInputStream(src); DataInputStream dis = new DataInputStream(new BufferedInputStream(bais)); try { System.out.println(dis.readUTF()); } catch (IOException e) { e.printStackTrace(); }finally{ if(null != dis){ try { dis.close(); } catch (IOException e) { e.printStackTrace(); } } } } //用DataInputStream把文件中的数据按照基本数据类型读取出来 public static void readTest(File src){ DataInputStream dis = null; try { dis = new DataInputStream(new BufferedInputStream(new FileInputStream(src))); System.out.println(dis.readDouble()); System.out.println(dis.readInt()); System.out.println(dis.readChar()); System.out.println(dis.readUTF()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(null != dis){ try { dis.close(); } catch (IOException e) { e.printStackTrace(); } } } } //用DataOutputStream把基本数据类型写入一个文件中 public static void writeTest(File dest){ double d = 3.1415926; int i = 233; char c = 'a'; String s = "abcd"; DataOutputStream dos = null; try { dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dest))); dos.writeDouble(d); dos.writeInt(i); dos.writeChar(c); dos.writeUTF(s); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ try { if(null != dos){ dos.close(); } } catch (IOException e) { e.printStackTrace(); } } }}
6.ObjectInputStream/ObjectOutStream(用法和DataInputStream/DataOutputStream类似)(这两个类有新增方法一般不用多态)(序列化详细介绍见《I/O篇(4)——对象序列化》)
只有序列化之后的对象才能用ObjectInputStream/ObjectOutStream流读取,不是所有的对象都可以序列化,只有实现了java.io.Serializable接口的类创建的对象才能序列化
7.PipedInputStream/PipedOutputStream(PipedReader/PipedWriter类似)
与线程有关,在后序线程相关博客中详细介绍
- I/O篇(3)——各I/O类的用法
- I/O流的用法
- iomem—I/O映射方式的I/O端口和内存映射方式的I/O端口
- iomem—I/O映射方式的I/O端口和内存映射方式的I/O端口
- java I/O流——File类的基本用法
- i/o——Java I/O底层
- asynchronous I/O——异步I/O
- 自定义类型的I/O输出用法
- Java I/O —— File类
- Day17—File 类、I/O流
- Java——I/O
- IO_STACK_LOCATION — I/O堆栈
- IO_STACK_LOCATION — I/O堆栈
- 黑马程序员—I/O
- IO_STACK_LOCATION — I/O堆栈
- Java学习—I/O
- I/O 操作文件的类——File
- vmstat 的I/O
- 一篇文章告诉你,该学R还是Python
- 菜鸟网络工程师的成长笔记——第2天(2016.08.19)
- 干活
- android 访问data目录、6.0模拟器读写sdcard、相关sdcard路径
- javascript学习路线level分级
- I/O篇(3)——各I/O类的用法
- 栈和队列面试题(二)
- jsp基础之--使用Session完成简单的登陆操作
- Strusts2原理解析
- 粉书练习之表达式树的实现
- 会议总结20160819
- java学习之路 之 Java常用类-Data类、Math类、BigInteger类、BigDecimai类 及 练习题
- pygame.key 键值说明
- 【Spark Java API】Transformation(6)—aggregate、aggregateByKey