为什么在 java 中有不同的处理文件 I/O 的方法?

Why are there different methods of dealing with File I/O in java?

到目前为止,我一直在使用 Scanners 从文本文件中读取数据。 示例:

        File file = new File("path\to\file") ;

        Scanner scan = new Scanner(file) ;  

        System.out.println(scan.nextLine()) ;

并用 FileWriters 将数据写入文本文件。像这样:

        try
        {
            FileWriter writer = new FileWriter("Foo.txt") ;
            writer.write("hello there!") ;                      
            writer.close() 
        }
        catch(IOException ex) 
        {
            ex.printStackTrace() ;
        }

几天前,我正在和我的导师开会。当我检查他的代码时,我注意到他使用了 BufferedReaderBufferedWriter - 一种我以前没有使用过的读写文件的方法。然后我问他使用 BufferedReaderScanner 从文件中读取数据有什么区别。他无法向我解释。

所以我做了一些研究,发现执行这些操作的 类 是 InputStreamOutputStream。这些 类 有各自的子 类 如 FileInputStreamFileOutputStream

在我的进一步研究中,我遇到了 ReaderWriter 类,它们用于从文件读取数据和向文件写入数据。同样,与 InputStreamOutputStream 一样,这些 类 是 abstract super 类 并且有自己的子 类 来执行读写操作。

我对此并不感到困惑,但是...为什么?我的意思是,为什么做同样的事情会有不同的方法?有什么意义?哪种方法是处理文件输入和输出的最有效方式?

Java 充满方法,类 部分重叠。

一般来说,在任何情况下都没有更好的解决方案。拥有 更多备选方案使您能够针对需要解决的特定情况选择正确的解决方案

例如 java.util.List 是一个接口,许多实现 类:

  • 数组列表
  • 链表
  • 向量
  • 属性列表
  • CopyOnWriteArrayList
  • 堆栈

他们中的任何一个解决了同一个问题,专注于该问题的特定方面。 正如一个想法 ArrayList 是一个 List 在内部使用数组实现的,而 LinkedList 是一个列表,其中每个项目都有一个 link 到上一个和下一个元素。如果你需要在一个非常大的列表中间插入/删除元素,最好使用 LinkedList,如果你需要在列表中间读取,最好使用 ArrayList.

在您的特定情况下,区别在于:

Scanner:

A simple text scanner which can parse primitive types and strings using regular expressions. A Scanner breaks its input into tokens using a delimiter pattern, which by default matches whitespace. The resulting tokens may then be converted into values of different types using the various next methods.

如果你需要在阅读时拆分令牌,这将很有用

BufferedReader:

Reads text from a character-input stream, buffering characters so as to provide for the efficient reading of characters, arrays, and lines.

加速读取操作很有用,因为它使用一种缓存(缓冲区)来保存字节而不是逐一读取它们

InputStream:

An InputStreamReader is a bridge from byte streams to character streams: It reads bytes and decodes them into characters using a specified charset. The charset that it uses may be specified by name or may be given explicitly, or the platform's default charset may be accepted. Each invocation of one of an InputStreamReader's read() methods may cause one or more bytes to be read from the underlying byte-input stream. To enable the efficient conversion of bytes to characters, more bytes may be read ahead from the underlying stream than are necessary to satisfy the current read operation. For top efficiency, consider wrapping an InputStreamReader within a BufferedReader.

它的工作级别低于 Scanner,为了提高效率,应与 BufferedReader 一起使用。

Reader秒读取char秒; InputStream 秒阅读 byte 秒。 (相应地,Writers写chars;OutputStreams写bytes)。

Stringchar 的序列,而不是 byte

如果您想阅读 bytes,请使用 InputStream。如果您要从文件中读取它们,请使用 FileInputStream;但并非所有 byte 序列都来自文件,例如,ByteArrayInputStream 允许您从 byte[] 中读取 byte 序列;但因为它是一个 InputStream,所以它的使用方式与来自文件的方式完全相同。

如果要阅读 chars,请使用 Reader。如果你想把一个 InputStream 读成 chars,你可以使用一个 InputStreamReader,你指定一个 CharSet,它允许 bytes 是正确转换为 chars。

A BufferedReader 是缓冲其输入的 Reader - 它一次从源读取多个字节,而不是一次一个。 一次阅读很多而不是一个,假设您需要不止一个。它还提供了方便的方法来获取 String 而不是 char[].

对我来说,BufferedReader 允许你做的事情超过 Reader 的典型例子是 read a whole line at once

Scanner 是高级 class,它允许您从 String(或者通常是 Readable 中读取数据,它由 ReaderBufferedReader,例如),但将数据作为 String 以外的类型获取 - 例如,您可以分别将 1 2.0 true 读取为 int, double and boolean,无需自己进行解析。

这只是建立在其他东西的功能之上,基本上是通过从 Reader 内部读取。 Scanner 基本上是一个分词器,尽管也有更老的 StringTokenizer.


Scanner是一个很差的class,老实说:它在基本程序中经常使用(例如“输入你的名字,输入你喜欢的颜色”);但它的尖锐边缘让许多初学者望而却步(例如 Scanner is skipping nextLine() after using next() or nextFoo()?);而且它并不像您想象的那样容易,比如验证用户输入数字而不是一般字符串。

我发现您很快就不再使用 Scanner,而只需使用 (Buffered)Reader 即可将所有内容阅读为 Strings:它更强大。