`
songzhan
  • 浏览: 247846 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

Java IO流读写文件的几个注意点 (转)

 
阅读更多

原文:http://blog.csdn.net/swingline/article/details/5656979     作者:swingline

写IO相关代码机会挺少的,但却都知道使用BufferedXXXX来读写效率高,没想到里面还有这么多陷阱,这两天突然被其中一个陷阱折腾一下:读一个文件,然后写到另外一个文件,前后两个文件居然不一样?

     解决这个问题之后,总结了几个注意点。

 

注意点一:Reader/Writer读写二进制文件是有问题的

  1. public void copyFile1() {  
  2.         File srcFile = new File("E://atest//atest.txt");  
  3.         File dstFile = new File("E://btest//btest.txt");  
  4.         BufferedReader in = null;  
  5.         BufferedWriter out = null;  
  6.         try {  
  7.             in = new BufferedReader(new FileReader(srcFile));  
  8.             out = new BufferedWriter(new FileWriter(dstFile));  
  9.               
  10.             String line = null;  
  11.             while((line = in.readLine()) != null) {  
  12.                 out.write(line+"/r/n");  
  13.             }  
  14.         }catch (Exception e) {  
  15.             // TODO: handle exception  
  16.             e.printStackTrace();  
  17.         }finally {  
  18.             if(in != null) {  
  19.                 try {  
  20.                     in.close();  
  21.                 }catch (Exception e) {  
  22.                     // TODO: handle exception  
  23.                     e.printStackTrace();  
  24.                 }  
  25.             }  
  26.               
  27.             if(out != null) {  
  28.                 try {  
  29.                     out.close();  
  30.                 }catch (Exception e) {  
  31.                     // TODO: handle exception  
  32.                     e.printStackTrace();  
  33.                 }  
  34.             }  
  35.         }  

上面代码使用BufferedReader一行一行地读取一个文件,然后使用BufferedWriter把读取到的数据写到另外一个文件中。如果 文件是ASCCII形式的,则内容还是能够正确读取的。但如果文件是二进制的,则读写后的文件与读写前是有很大区别的。当然,把上面的 readLine()换成read(char[])仍然不能正确读写二进制文件的。读写二进制文件请接着看下面注意点。

 

注意点二:read(byte[] b, int offset, int length)中的offset不是指全文件的全文,而是字节数组b的偏移量

现在已经知道使用Reader/Writer不能正确读取二进制文件,这是因为Reader/Writer是字符流,那就改用字节流ufferedInputStream/BufferedOutputStream,网上搜索到的例子大概是这样的:

[c-sharp] view plaincopy
  1. public void copyFile() {  
  2.         File srcFile = new File("E://atest//atest.gif");  
  3.         File dstFile = new File("E://atest//btest.gif");  
  4.         BufferedInputStream in = null;  
  5.         BufferedOutputStream out = null;          
  6.         try {  
  7.             in = new BufferedInputStream(new FileInputStream(srcFile));  
  8.             out = new BufferedOutputStream(new FileOutputStream(dstFile));  
  9.               
  10.             byte[] b = new byte[1024];  
  11.             while(in.read(b) != -1) {  
  12.                 out.write(b);  
  13.             }  
  14.         }catch (Exception e) {  
  15.             // TODO: handle exception  
  16.             e.printStackTrace();  
  17.         }finally {  
  18.             if(in != null) {  
  19.                 try {  
  20.                     in.close();  
  21.                 }catch (Exception e) {  
  22.                     // TODO: handle exception  
  23.                     e.printStackTrace();  
  24.                 }  
  25.             }  
  26.             if(out != null) {  
  27.                 try {  
  28.                     out.close();  
  29.                 }catch (Exception e) {  
  30.                     // TODO: handle exception  
  31.                     e.printStackTrace();  
  32.                 }  
  33.             }  
  34.         }  
  35.     }  

每次读1024字节,然后写1024字节。这看似挺正确的,但实际写出来的文件与原文件是不同的。这样就怀疑可能是读写没有接上,因而把代码改成下面的形式:

  1. byte[] b = new byte[1024];  
  2.             int offset = 0;  
  3.             int length = -1;  
  4.             while((length = in.read(b, offset, 1024)) != -1) {  
  5.                 out.write(b, offset, length);  
  6.                 offset += length;  
  7.             }  

这是误以为:先读一段,写一段,然后改变偏移量,然后使用新的偏移量再读一段、写一段,直到文件读写完毕。但这是错误的,因为使用 BufferedXXX后,里面已经实现了这个过程。而read(byte[] b, int offset, int length)中的offset实际指的是把读到的数据存入到数组b时,从数组的哪个位置(即offset)开始放置数据;同 理,write(byte[] b, int offset, int length)就是把b中的数据,从哪个位置(offset)开始写到文件中。

 

注意点三:使用 length=read (b, 0, 1024)读数据时,应该使用write(b, 0, length)来写

第二个注意点中的第一段代码的做法虽然在网上比较常见,但是有问题的。问题在哪呢?答案是:问题在byte[] b这个数组上。由于二进制文件使用比较工具时,只知道不同、但不能知道哪些不同(是否有更先进的比较工具?)。怎样确定它的不同呢?方法很简单:就把二进 制文件改成文本文件就能看出结果了(Reader/Writer这种字符流虽然不能正确读写二进制文件,但InputStream /OutputStream这些字节流能既能正确读写二进制文件,也能正确读写文本文件)。由于使用了每次读1K(1024字节)的方式,所以会看到的结 果是:写后的文件后面多出一段,这一段的长度与原文件大小以及b数组的大小有关。为了进一步确定是什么关系,把读的文件内容改 为"1234567890123",而把b数组的大小改为10字节,这时结果就出来了:写后的文件内容变 成"12345678901234567890",就是读了两遍。多出的内容的根源在这里:b数组的大小是10字节,而要读的内容长度是13字节,那就要 读两次,第一次读了前10字节,此时b数组内的元素为前10个字符;再读第二次时,由于可读内容只有3个字符,那b数组的内容只有前3个字符被改变了,后 面7个字符仍然保持上一次读取的内容。所以直接采用write(b)的方式,在第二次写文件时,内容就多写了一段不是第二次读取到的内容。

下面是正确的读写(即每次读了多少内容,写入的是多少内容,而不是写入整个数组):

  1. public void copyFile() {  
  2.         File srcFile = new File("E://atest//atest.txt");  
  3.         File dstFile = new File("E://btest//btest.txt");  
  4.         BufferedInputStream in = null;  
  5.         BufferedOutputStream out = null;  
  6.         try {  
  7.             in = new BufferedInputStream(new FileInputStream(srcFile));  
  8.             out = new BufferedOutputStream(new FileOutputStream(dstFile));  
  9.               
  10.             int len = -1;  
  11.             byte[] b = new byte[10];  
  12.             while((len = in.read(b)) != -1) {  
  13.                 out.write(b, 0, len);  
  14.             }  
  15.         }catch (Exception e) {  
  16.             // TODO: handle exception  
  17.             e.printStackTrace();  
  18.         }finally {  
  19.             if(in != null) {  
  20.                 try {  
  21.                     in.close();  
  22.                 }catch (Exception e) {  
  23.                     // TODO: handle exception  
  24.                     e.printStackTrace();  
  25.                 }  
  26.             }  
  27.             if(out != null) {  
  28.                 try {  
  29.                     out.close();  
  30.                 }catch (Exception e) {  
  31.                     // TODO: handle exception  
  32.                     e.printStackTrace();  
  33.                 }  
  34.             }  
  35.         }  
  36.     }  

 

注意点四:flush()和close()

flush()是把写缓冲区内的内容全部”吐“到文件上,如果没有它,就有可能很多内容还存在于写缓冲区内,而不是在文件中,也就是还有丢失的可能。

close()中会调用flush()。它是文件真正完成的标志,文件内容写完成后不关闭文件流,会导致一些”古怪“的问题。这个在网络中的流更能体现。

所以,写文件完成后注意关闭文件读写流。

 
分享到:
评论

相关推荐

    Java基础篇:IO流.pdf

    Java I/O流的抽象基类主要包括几个顶层接口,如InputStream、OutputStream、Reader和Writer。这些抽象基类定义了各种流的基本操作,如read()、write()和close()等方法。 在Java I/O中,File类扮演了非常重要的角色...

    IO流pdf宝典

    ### JAVA IO流概念及其应用详解 ...通过对以上知识点的学习,我们可以了解到JAVA IO流在处理文件和其他设备上的数据时的强大功能。掌握好这些基本概念和技巧,能够帮助开发者更好地处理各种数据操作需求。

    Java流(文件读写操作)

    ### Java流(文件读写操作) #### 一、流的分类 Java中处理文件和数据时,使用流的概念来进行操作。根据不同的标准,流可以分为几种类型。 ##### 1. 按数据流动方向 - **输入流**:主要用于从数据源读取数据。输入...

    Java IO流几种经典使用方式

    ### Java IO流几种经典使用方式 #### 一、输入输出流(Input/Output Stream) 在Java中,`java.io`包提供了处理输入/输出操作的基本类,主要包括`InputStream`和`OutputStream`。 ##### 1. 字节流(Byte Stream) **...

    java基础 IO流

    java基础中的IO流是Java提供的一套用于文件读写操作的流式API,它包括字节流和字符流两种基本类型。字节流主要用在处理二进制数据,而字符流则是处理文本数据。 首先,文档中提到了`File`类,它是IO流操作中用于...

    Java 的 IO流笔记.md

    Java的IO流体系主要包括以下几种流: - **FileInputStream/FileOutputStream**:节点流,用于以字节为单位直接操作文件。 - **ByteArrayInputStream/ByteArrayOutputStream**:节点流,用于以字节为单位直接操作...

    java io流-3.pdf

    ### Java I/O流知识点概述 #### 一、Java I/O流基础 - **I/O流的概念**:Java中的所有输入/输出(I/O)设施都基于流,它提供了一种简单的方式来读取和写入不同类型的文件数据。 - **四类主要流**:Java I/O系统...

    JAVA_IO流学习总结

    Java IO流的学习主要涉及到以下几个方面的知识点: 1. Java中流的概念及其重要性:流是数据传输的一种抽象概念,在Java中通过流可以实现数据的序列化和反序列化,即以一种连续的字节流的形式进行数据的读写操作。流...

    Java学生管理系统GUI+IO流

    文件中的几个类名揭示了系统的主要功能: 1. `mainText.java`:这可能是主程序入口,包含了主方法,用于启动GUI和整个应用程序的生命周期管理。 2. `TeacherChangePassword.java`和`StudentChangePassword.java`:...

    java高级特性 - io流.pdf

    Java IO流的功能非常丰富,上述内容仅涉及了Java IO流的几个基础操作。在实际应用中,Java IO流还包括了对数据流的读写操作,例如使用FileInputStream和FileOutputStream来处理二进制文件的读写,使用FileReader和...

    使用Java实现对dbf文件的简单读写

    使用 Java 实现对 dbf 文件的简单读写需要使用以下几个类: 1. DBFWriter:用于 dbf 文件的写操作,提供了写入 dbf 文件的方法。 2. JDBField:用于表示 dbf 文件中的字段信息,包括字段名、字段类型、字段长度等。...

    JAVA中的IO流简介

    下面通过几个简单的示例来进一步理解IO流的使用: 1. **文件复制**:利用`FileInputStream`和`FileOutputStream`实现文件字节流复制。 2. **缓冲流使用**:通过`BufferInputStream`和`BufferedOutputStream`来提高...

    Java IO流的几个简单的例子.docx

    总的来说,Java的IO流提供了一种高效且灵活的方式来处理文件的读写操作。通过字节流,我们可以逐字节地读取和写入数据,适合处理任何类型的二进制数据。而字符流则更适合处理文本数据,特别是涉及多种字符编码的情况...

    java io.pdf

    Java IO体系结构主要由以下几个核心部分组成: 1. **流(Streams)**:Java IO的核心概念是流,它们代表数据的源和目的地。流可以是字节流或字符流,分为输入流和输出流。字节流处理单个字节的数据,而字符流处理...

    Java输入输出流及文件读写详解

    在Java中,字节流体系结构包括以下几个核心类: - **InputStream**: 所有字节输入流的父类,提供了基本的读取操作接口。 - **OutputStream**: 所有字节输出流的父类,提供了基本的写入操作接口。 ##### ...

    javaIO详细讲解+详细案例

    Java IO流体系中,有几个核心的抽象基类,包括`InputStream`和`OutputStream`,它们是所有其他具体输入输出流类的基类。此外,还有`Reader`和`Writer`作为字符流的基类。 1. **输入字节流** `InputStream`: - **...

    java io案例

    `整合练习2`这个文件可能包含了一些具体的IO流操作示例,比如文件的复制、过滤(去除特定字符或行)、数据的序列化与反序列化等。在分析这个案例时,我们可以关注以下几个关键点: 1. 文件的打开与关闭:使用...

Global site tag (gtag.js) - Google Analytics