2017-03-31 33 views
4

我学习Android开发(我在通用编程初学者),并了解HTTP网络和看到这个代码的教训:InputStream,InputStreamReader和BufferedReader如何在Java中一起工作?

private String readFromStream(InputStream inputStream) throws IOException { 
    StringBuilder output = new StringBuilder(); 
    if (inputStream != null) { 
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8")); 
    BufferedReader reader = new BufferedReader(inputStreamReader); 
    String line = reader.readLine(); 
    while (line != null) { 
     output.append(line); 
     line = reader.readLine(); 
    } 
    } 
    return output.toString(); 
} 

我不明白到底是什么的InputStream,InputStreamReader和BufferedReader类做。它们都有一个read()方法,并且在BufferedReader的情况下也有readLine()。为什么我不能只使用InputStream或只添加InputStreamReader?为什么我需要添加BufferedReader?我知道这与效率有关,但我不明白。

我一直在研究和documentation for the BufferedReader试图解释这一点,但我还是不明白谁在做什么:

在一般情况下,每个读请求做了一个读者会导致相应的 读请求由底层字符或字节流组成。因此建议使用 将BufferedReader包装在read()操作可能代价高昂的任何Reader 周围,例如FileReaders和InputStreamReaders。例如,

BufferedReader in = new BufferedReader(new FileReader("foo.in")); 将缓冲来自指定文件的输入。如果没有缓冲,每个调用read()或readLine()都可能导致文件从 中读取字节,转换为字符,然后返回,这可能是效率非常低的 。

所以,我明白的InputStream只能读一个字节,使用InputStreamReader单个字符,和BufferedReader类一整行,它也确实对一些效率,这是我不明白。我想更好地了解谁在做什么,以便理解为什么我需要他们三个人,以及没有他们中的哪一个会有什么不同。

我在网上的其他地方研究了很多,似乎没有找到任何解释,我可以理解,几乎所有的教程只是重复文档信息。这里有一些相关的问题可能开始解释这一点,但不要深入并解决我的困惑:Q1,Q2,Q3,Q4。我认为这可能与最后一个问题关于系统调用和返回的解释有关。但我想明白这一切是什么意思。

难道是BufferedReader的readLine()调用InputStreamReader的read()方法,该方法又调用InputStream的read()方法?并且InputStream返回转换为int的字节,每次返回一个字节,InputStreamReader读取足够的这些数据以生成单个字符并将其转换为int并一次返回一个字符,并且BufferedReader读取足够多的这些字符用整数表示来组成一个整行?并将整行返回为String,只返回一次而不是几次?我不知道,我只是想知道事情是如何运作的。

提前感谢!

+3

http://stackoverflow.com/questions/32175221/what-is-the-relation-between-inputstream-buffreredinputstream-inputstreamreade?rq=1? – 2017-03-31 18:09:17

+0

感谢您的建议@RC。我已经看到了这个问题,并在我自己的问题中提到了这个问题。我正在寻找一些更具体的事情,以了解他们之间发生了什么。 – schv09

回答

10

这个Streams in Java concepts and usage的链接,给出了很好的解释。

This

流,读者,作者,BufferedReader类,但是BufferedWriter - 这些都是你在Java中处理的术语。Java中提供了用于输入和输出操作的类。真正值得了解这些是如何相关的以及如何使用它们。本文将详细探讨Java中的Streams和其他相关类。因此,让我们开始:

让我们定义每个这些高层次然后深入挖掘。


用来处理字节级数据

读/写
用来对付人物等级。它也支持各种字符编码。

BufferedReader/BufferedWriter
提高性能。要读取的数据将被缓存到内存中以便快速访问。

虽然这些是用于输入,但只有相应的类也存在输出。例如,如果有一个InputStream旨在读取字节流,并且OutputStream将有助于写入字节流。

InputStreams
java提供了很多类型的InputStreams。每个连接到不同的数据源,如字节数组,文件等。

例如FileInputStream连接到文件数据源,可用于从文件中读取字节。虽然ByteArrayInputStream可用于将字节数组视为输入流。

的OutputStream
这有助于书面字节的数据源。对于几乎每个InputStream,都有一个对应的OutputStream,无论哪里合理。


UPDATE

什么是缓冲流?

在这里,我从Buffered Streams报价,Java文档(随着技术的解释):

缓冲流

多数时候我们到目前为止看到使用无缓冲I例子/ O 。这意味着 每个读或写请求都由底层操作系统直接处理。 这可能会使程序效率低得多,因为每个这样的请求都会触发磁盘访问,网络活动或其他操作相对昂贵的其他操作 。

为了减少这种开销,Java平台实现了缓冲的I/O流。缓冲输入流从已知为 的存储区读取数据作为缓冲区;本地输入API仅在缓冲区为 为空时调用。同样,缓冲输出流将数据写入缓冲区,并且仅在缓冲区已满时才调用本地输出API。

有时候,我失去了我的头发阅读技术文档。所以,在这里我引用了更加人性化的解释https://yfain.github.io/Java4Kids/

一般情况下,磁盘访问比处理内存中执行 慢得多;这就是为什么访问磁盘不是一个好主意,要读取1,000字节的文件一千次。为了最小化的次数的磁盘访问次数 ,Java提供缓冲器,其作为数据的 储层。

enter image description here

在阅读文件,然后的FileInputStream的BufferedInputStream,该 类的BufferedInputStream作品的FileInputStream 和文件本身之间的中间人。它从文件中读取的字节的一大块成在一杆 存储器(缓冲器),和的FileInputStream的对象然后 读取从那里单个字节,它是快速存储器到存储器 操作。 BufferedOutputStream与类 FileOutputStream类似。

这里的主要思想是尽量减少磁盘访问。缓冲流 不改变原有数据流的类型 - 他们只是让阅读 更有效。一个程序执行流链接(或流管道) 来连接流,就像管道连接在管道中一样。

+2

这是一个很好的答案。我只是想强化Java COULD刚刚提供了一个非常简单的方法,如:String text = new File(“data.txt”)。getText()来读取所有文本。事实上,Java 8确实提供了这样的东西,但总的来说,Java只是给你一些东西,让你可以以任何你喜欢的方式将它们组合在一起(更接近于Python提供解决问题的单一方式的方法,而不是Ruby的“Make the common简单的事情和罕见的事情“ - 通常会导致多种方式来解决问题。) –

+0

谢谢,@ישואוהבאותך。我理解图的前两部分,InputStreamReader将读取由InputStream读取的字节并将它们编码为字符。但是我没有获得缓冲部分,BufferedReader如何提供帮助?这条线意味着什么:“为了提高性能,要读取的数据将被缓存到内存中以便快速访问。”它用较少的技术词汇做什么? – schv09

+1

大更新,@ישואוהבאותך!谢谢,我只会补充一点,关于这三个类如何一起工作。在进一步阅读和浏览OpenJDK源代码时,发生了以下情况:BufferedReader负责维护和填充char数组,它的read()或readLine()方法将从这里获取字符。它将通过调用InputStreamReader的read(char [],int,int)方法来填充。 InputStreamReader与StreamDecoder相关联,当它被读取(char [],int,int)被调用时,它将调用StreamDecoder的read(char [],int,int)。 (1/2) – schv09

1
  • InputStream, OutputStream, byte[], ByteBuffer二进制数据。
  • Reader, Writer, String, char文本,内部是Unicode,所以世界上所有的脚本都可以组合(比如说希腊语和阿拉伯语)。

  • InputStreamReaderOutputStreamWriter在两者之间形成桥梁。如果你有一些InputStream和知道它的字节实际上是在某些编码的文字,字符集,那么你可以用InputStream的:

    try (InputStreamReader reader = 
         new InputStreamReader(stream, StandardCharsets.UTF_8)) { 
        ... read text ... 
    } 
    

有没有字符集一个构造函数,但这是不可移植的,因为它使用默认的平台编码。

在Android StandardCharset可能不存在,则使用 “UTF-8”。

派生类FileInputStreamBufferedReader为父项添加内容InputStream resp。 Reader

FileInputStream用于从文件输入,而BufferedReader使用内存缓冲区,所以实际的物理读取不会无法读取字符(低效率)。随着new BufferedReader(otherReader)你添加缓冲到您的原始读者。

所有这一切理解,有实用类Files与方法如newBufferedReader(Path, Charset),这增加了更多的简洁性。

+0

谢谢,@Joop,我仍然没有得到最后一部分。 BufferReader如何提供帮助?我不明白这一行:BufferReader“将缓冲来自指定文件的输入,如果没有缓冲,每次调用read()或readLine()都会导致从文件读取字节,转换为字符,然后返回,这可能非常低效。“这个代码在我的问题中引用。现在使用BufferedReader时有什么区别? – schv09

+1

我已经添加了一些你的评论给其他人的答案。解释说BufferedReader最终从文件中读取。一个普通的Reader会重复地询问操作系统读取一个char /字节并检查文件结束,在一个循环中。读取代替缓冲区提高了速度,因为操作系统内部不使用int read()而是int read(byte [],int,int)。所以它通常是_faster_,尽管用例依赖。功能应该没有区别。 –

相关问题