Java FileInputStream

时间:2020-01-09 10:36:02  来源:igfitidea点击:

Java FileInputStream类java.io.FileInputStream使得可以将文件的内容作为字节流读取。 JavaFileInputStream类是Java InputStream的子类。这意味着我们将Java的FileInputStream用作InputStream(FileInputStream的行为类似于InputStream)。

Java FileInputStream示例

这是一个简单的FileInputStream示例:

InputStream input = new FileInputStream("c:\data\input-text.txt");

int data = input.read();
while(data != -1) {
  //do something with data...
  doSomethingWithData(data);

  data = input.read();
}
input.close();

注意:为清楚起见,此处已跳过适当的异常处理。要了解有关正确的异常处理的更多信息,请转到Java IO异常处理。

还要注意,由于FileInputStream是InputStream的子类,因此可以将创建的FileInputStream投射到想要输入的任意位置的InputStream,就像在上面的示例中所做的那样。

FileInputStream构造函数

FileInputStream类具有三个不同的构造函数,可用于创建FileInputStream实例。我将在这里介绍前两个。

第一个构造函数采用" String"作为参数。该字符串应包含文件系统中要读取的文件所在的路径。这是一个代码示例:

String path = "C:\user\data\thefile.txt";

FileInputStream fileInputStream = new FileInputStream(path);

注意path字符串。它需要双反斜杠(\\)在String中创建一个反斜杠,因为反斜杠是Java字符串中的转义字符。要获得一个反斜杠,我们需要使用转义序列" \"。

在Unix上,文件路径可能如下所示:

String path = "/home/HymanHyman/data/thefile.txt";

请注意,使用斜杠(普通斜杠字符)作为目录分隔符。这就是我们在UNIX上写入文件路径的方式。实际上,以我的经验,如果我们在Windows上使用/作为目录分隔符(例如c:/ user / data / thefile.txt),Java也会理解,但请不要相信。在我们自己的系统上进行测试!

第二个FileInputStream构造函数将File对象作为参数。 "文件"对象必须指向我们要读取的文件。这是一个例子:

String path = "C:\user\data\thefile.txt";
File   file = new File(path);

FileInputStream fileInputStream = new FileInputStream(file);

我们应该使用哪种构造函数取决于打开FileInputStream之前我们所使用的路径的形式。如果我们已经有一个String或者File,请直接使用它。将"字符串"转换为"文件",或者先将"文件"转换为"字符串"并没有特别的好处。

read()

FileInputStream的read()方法返回一个包含读取的字节的字节值的int。如果read()方法返回-1,则FileInputStream中不再有要读取的数据,可以将其关闭。也就是说,-1作为int值,而不是-1作为字节值。这里有区别!

我们可以像使用InputStream的read()方法一样使用read()方法。这是读取JavaFileInputStream中的所有数据的示例:

FileInputStream fileInputStream =
    new FileInputStream("c:\data\input-text.txt");

int data = fileInputStream.read();
while(data != -1) {
    // do something with data variable

    data = fileInputStream.read(); // read next byte
}

当while循环终止时,所有字节均已从FileInputStream中读取。

read(byte[])

作为InputStream的FileInputStream还具有两个read()方法,可以将数据读取到byte数组中。顺便说一下,这些方法是从Java InputStream类继承的。这些方法是:

  • int read(byte [])
  • int read(byte [],int offset,int length)

第一种方法尝试使用来自FileInputStream的字节填充作为参数传递给它的byte数组。

第二种方法尝试将"长度"字节读入"字节"数组,从"字节"数组中的"偏移量"单元开始,然后从那里开始填充。

两种方法都将实际读取到字节数组中的字节数返回。如果要读取的字节数少于数组中可容纳空间的字节数,或者少于我们在" length"参数中指定的字节数,则将更少的字节读入" byte"数组。如果已经从FileInputStream中读取了所有字节,则这些read()方法将返回-1。因此,有必要检查从这些read()方法调用返回的值。

这是一个调用read(byte [])方法之一的示例:

FileInputStream fileInputStream = new FileInputStream("c:\data\input-text.txt");

byte[] data      = new byte[1024];
int    bytesRead = fileInputStream.read(data, 0, data.length);

while(bytesRead != -1) {
  doSomethingWithData(data, bytesRead);

  bytesRead = fileInputStream.read(data, 0, data.length);
}

请注意,read(data,0,data.length)等同于read(data)

doSomethingWithData()方法的实现已在本示例中省略,以使其简短。但是它表示我们要对读取的数据执行的任何一组操作。

读取的性能

一次读取一个字节数组比一次从JavaFileInputStream读取单个字节快。通过读取字节数组而不是一次读取单个字节,该差异很容易成为性能提高的10倍或者更多倍。

获得的确切速度取决于我们读取的字节数组的大小以及运行代码的计算机的OS,硬件等。在决定之前,我们应该研究目标系统的硬盘缓冲区大小等。但是,如果缓冲区大小为8KB或者更高,则可以实现很好的加速。但是,一旦字节数组超出了底层操作系统和硬件的容量,我们将无法从更大的字节数组中获得更大的加速。

我们可能必须尝试不同的字节数组大小并测量读取性能,以找到最佳的字节数组大小。

通过BufferedInputStream进行透明缓冲

我们可以使用Java BufferedInputStream从FileInputStream中添加透明,自动读取和缓冲字节数组的函数。 BufferedInputStream从底层的FileInputStream读取字节块到字节数组。然后,我们可以从" BufferedInputStream"中逐个读取字节,并且仍然可以通过读取字节数组而不是一次读取一个字节来获得很多加速。这是将JavaFileInputStream包装在BufferedInputStream中的示例:

InputStream input = new BufferedInputStream(
                      new FileInputStream("c:\data\input-file.txt"),
                        1024 * 1024        /* buffer size */
    );

注意," BufferedInputStream"是" InputStream"的子类,可以在任何可以使用" InputStream"的地方使用。

关闭一个FileInputStream

从Java FileInputStream读取完数据后,必须将其关闭。通过调用从InputStream继承的close()方法来关闭FileInputStream。这是一个打开FileInputStream,从中读取所有数据,然后关闭它的示例:

FileInputStream fileInputStream = new FileInputStream("c:\data\input-text.txt");

int data = fileInputStream.read();
while(data != -1) {
  data = fileInputStream.read();
}
fileInputStream.close();

注意while循环如何继续,直到从FileInputStream的read()方法中读取-1值为止。之后,while循环退出,并调用FileInputStreamclose()方法。

上面的代码不是100%健壮的。如果从FileInputStream读取数据时引发了异常,则永远不会调用close()方法。为了使代码更健壮,我们将必须使用Java Java尝试使用资源构造。我在Java IO异常处理的教程中也解释了使用Java IO类的正确异常处理。

这是使用try-with-resources构造关闭JavaFileInputStream的示例:

try( FileInputStream fileInputStream = new FileInputStream("file.txt") ) {

    int data = fileInputStream.read();
    while(data != -1){
        data = fileInputStream.read();
    }
}

注意,在" try"关键字之后的括号内声明了" FileInputStream"。这向Java发出信号,该" FileInputStream"将由try-with-resources构造进行管理。

一旦执行线程退出" try"块," FileInputStream"将关闭。如果从try块内部抛出异常,则捕获该异常,关闭FileInputStream,然后重新抛出该异常。因此,当在try-with-resources块中使用时,可以保证FileInputStream是关闭的。

将FileInputStream转换为Reader

JavaFileInputStream是基于字节的数据流。如我们所知,Java IO API还具有一个基于字符的输入流集,称为"读取器"。我们可以使用Java InputStreamReader将Java FileInputStream转换为Java Reader。我们可以通过单击上一句中的链接来了解有关如何使用InputStreamReader的更多信息,但这是将Java FileInputStream转换为InputStreamReader的快速示例:

InputStream inputStream       = new FileInputStream("c:\data\input.txt");
Reader      inputStreamReader = new InputStreamReader(inputStream);