FileInputStream和FileReader的区别

50
FileReader rd=new FileReader("new.mp4");
FileWriter wr=new FileWriter("output.mp4");
int ch;
while((ch=rd.read())!=-1)
  wr.write(ch);

wr.flush();
wr.close();

当我使用FileReaderFileWriter读写mp4文件时,生成的output.mp4文件无法正常渲染。但当我改用FileInputStreamFileOutputStream时,它可以正常工作。

所以我可以得出结论:FileReaderFileWriter只适用于读写文本文件吗?


1
http://download.oracle.com/javase/tutorial/essential/io/charstreams.html - Yaneeve
6个回答

58
是的,你的结论是正确的,ReaderWriter的子类用于读取/写入文本内容。InputStream/OutputStream则用于二进制内容。如果您查看文档:

Reader - 用于读取字符流的抽象类

InputStream - 抽象类,是表示字节输入流的所有类的超类。


3
我可以只阅读 .txt 文件,因为 PDF、Word 文件还包含图像文件,对吧? - saravanan
3
是的,只能处理文本文件。PDF和DOC是比较复杂的格式,需要使用特殊库才能有意义地解析它们。 - Bozho
@Bozho:我在哪里可以找到这些库来在我的程序中使用,Java的创建者是否采取了仅用于读取.txt文件的字符流创建的方法? - saravanan
@saravanan 这取决于您的确切要求。您可以提出另一个问题,明确您想从PDF和/或Doc中获取什么。但首先搜索一下 - 已经有相关的问题了。 - Bozho
我编辑了我的上一个评论...你能回答第二个问题吗? - saravanan
@Saravanan,这是一个不同的问题。 - Bozho

41

FileReader(以及任何扩展Reader的内容)是用于处理文本的。从 Reader文档中可以看到:

  

用于读取字符流的抽象类。

(重点在于“字符流”.) 查看API,你会发现它与文本有关-使用char而不是byte

InputStreamOutputStream用于二进制数据,例如mp4文件。

个人建议完全避免使用FileReader,因为它总是使用系统默认字符编码-至少在Java 11之前如此。相反,当您要处理文本时,请使用InputStreamReader包装FileInputStream...或者使用Files.newBufferedReader

顺便提一句,这是从输入复制到输出的非常低效的方式...使用readwrite的重载版本,以将读取或写入缓冲区 - 可以是byte[]char[]。否则,每个字节/字符都会调用readwrite

您还应该在finally块中关闭IO流,以便在处理它们时抛出异常时它们也会关闭。


Herbert Schildt在他的书中建议使用字符流,以便代码可以国际化。你的意思是使用字节流也不会影响国际化吗? - Amal K
@AmalK:如果你正在读取二进制数据,比如视频文件,那么国际化的上下文就不存在了。任何建议都是有条件限制的,必须考虑到具体的上下文环境才能作出推荐。 - Jon Skeet
@John Skeet 我特别指的是文本数据,建议也是如此。使用字节流写文件是否会影响国际化,因为那里没有字符编码的上下文?你在回答中提到这个怎么样?请注意,自Java 11以来,FileReader允许指定编码。另外,如果您能回答我的最新问题,那将意义重大 :) - Amal K
1
@AmalK:这个问题不是关于文本数据的。它是关于mp4文件的。我已经明确指出“相反,只有在你想处理文本时才使用FileInputStream周围的InputStreamReader...”了。 - Jon Skeet

7

FileInputStream被用于读取原始字节流的数据,例如原始图像。另一方面,FileReader则被用于读取字符流。

FileInputStreamFileReader的区别在于:FileInputStream按字节读取文件,而FileReader按字符读取文件。

因此,当您尝试读取包含字符"Č"的文件时,在FileInputStream中会得到结果196 140,因为ČASCII值为268

而在FileReader中将得到结果268,这是字符ČASCII值。


4
为了深入理解,需要先了解什么是字符流和字节流。那么让我们快速地了解一下——
字节流按字节访问文件。Java程序使用字节流执行8位字节的输入和输出。它适用于任何类型的文件,但不太适合文本文件。例如,如果文件使用Unicode编码,并且一个字符由两个字节表示,则字节流将分别处理这些字节,并且您需要自己进行转换。面向字节的流不使用任何编码方案,而面向字符的流使用字符编码方案(UNICODE)。所有字节流类都是从InputStream和OutputStream派生的。
字符流会按字符读取文件。字符流是比字节流更高级的概念。字符流实际上是一个已经包装了特定编码的逻辑以允许其输出字符的字节流。这意味着字符流需要提供文件的编码才能正常工作。字符流可以支持所有类型的字符集ASCII、Unicode、UTF-8、UTF-16等。所有字符流类都是从Reader和Writer派生的。
如果尝试读取使用默认java Unicode 8编码编写的.txt文件,则使用Reader和InputStream类读取文件将给出相同的输出。因为每个字节代表一个字符。
我创建了一些方法,将帮助您理解这两个术语之间的区别——"FileInputStream reads byte by byte"和"FileReader reads char by char"。请耐心阅读以下内容以便更好地理解。
现在您已经了解了这两种流的概念,让我们看一下示例,以了解它如何在内部工作——
使用Unicode 16编码写入文件中的一些数据的方法:

    public void unicode16Writer() throws Exception {
        try (OutputStream outputStream = new FileOutputStream("output.txt")) {
            Writer writer = new OutputStreamWriter(outputStream, Charset.forName("UTF-16"));
            writer.write("Hello World");
        }
    }

output.txt

Hello World

以下是从文件中读取数据的三种方式:第一种使用FileReader是默认方式,第二种使用FileInputStream,第三种使用带有Unicode-16字符集(编码)的InputStreamReader。

方法中的注释都是自说明的,请阅读以了解其工作原理。

FileReader

    public void fileReaderUnicode8() throws IOException {
        FileReader fr = new FileReader("output.txt");
        int i;
        int j = fr.read();
        /*
         * here it is not able to convert the 
         * int(a byte/8 bits read from the file) to a
         * char as we had used UTF-16 to encode the file so 16 bits 
         * represented one character, but we can use its super class 
         * InputStreamReader to provide the charset(what we used for encoding)
         * which for our case is UTF-16 , then we can
         * easily convert that into char.
         */
        System.out.println("Output of FileReader using default cons(default charset) : " + (char) j);
//              while ((i=fr.read()) != -1) 
//                System.out.print((char) i); 
    }

输出

使用默认构造函数(FileReader)的输出,默认字符集: þ

文件输入流

public void readBytebyByte() throws IOException {
        try (FileInputStream fis = new FileInputStream("output.txt")) {

            int i;
            int j = fis.read();
            /*
             * here it is not able to convert the 
             * int(a byte/8 bits read from the file) to a
             * char as we had used UTF-16 to encode the
             * file so 16 bits represented one
             * character.
             */

            System.out.println("Output of FileInputStream reading byte by byte : " + (char) j);
//              while ((i=fis.read()) != -1) 
//                System.out.print((char) i); 
        }
    }

输出

使用FileInputStream逐字节读取的输出:þ

InputStreamReader

/*Here we are using the parent class of FileReader so that 
     *we can set the charset(type of encoding)
     *in its constructor.
     */
    public void unicode16Reader() throws IOException {
        try (InputStream inputStream = new FileInputStream("output.txt")) {
            Reader reader = new InputStreamReader(inputStream, Charset.forName("UTF-16"));
            int data = reader.read();
            System.out.println("uni-16 ISR: " + (char) data);
        //  while(data != -1){
        //      char theChar = (char) data;
        //      data = reader.read();
        //  }

        }
    }

输出

uni-16中断服务例程:H


1

0

文本文件可以使用fileReaderfileInputStream来读取,但mp3和png只能使用fileInputStream来读取。

  1. fileReader逐个字符读取。

  2. fileInputStream逐个字节读取。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接