java基础知识之FileInputStream流的使用

目录
  • 一、File流概念
  • 二、FileInputStream
    • 1)FileInputStream概念
    • 2)构造方法
    • 3)FileInputStream常用API
  • 三、三种read方法效率比较

一、File流概念

JAVA中针对文件的读写操作设置了一系列的流,其中主要有FileInputStream,FileOutputStream,FileReader,FileWriter四种最为常用的流

二、FileInputStream

1)FileInputStream概念

FileInputStream流被称为文件字节输入流,意思指对文件数据以字节的形式进行读取操作如读取图片视频等

2)构造方法

2.1)通过打开与File类对象代表的实际文件的链接来创建FileInputStream流对象

public FileInputStream(File file) throws FileNotFoundException{}

若File类对象的所代表的文件不存在;不是文件是目录;或者其他原因不能打开的话,则会抛出FileNotFoundException

    /**
     *
     * 运行会产生异常并被扑捉--因为不存在xxxxxxxx这样的文件
     */
public static void main(String[] args)
    {
        File file=new File("xxxxxxxx"); //根据路径创建File类对象--这里路径即使错误也不会报错,因为只是产生File对象,还并未与计算机文件读写有关联

        try
        {
            FileInputStream fileInputStream=new FileInputStream(file);//与根据File类对象的所代表的实际文件建立链接创建fileInputStream对象
        }
        catch (FileNotFoundException e)
        {

           System.out.println("文件不存在或者文件不可读或者文件是目录");
        }
    }

2.2)通过指定的字符串参数来创建File类对象,而后再与File对象所代表的实际路径建立链接创建FileInputStream流对象

public FileInputStream(String name) throws FileNotFoundException

通过查看源码,发现该构造方法等于是在第一个构造方法的基础上进行延伸的,因此规则也和第一个构造方法一致

    public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }

2.3)该构造方法没有理解---查看api是指使用的fdObj文件描述符来作为参数,文件描述符是指与计算机系统中的文件的连接,前面两个方法的源码中最后都是利用文件描述符来建立连接的

public FileInputStream(FileDescriptor fdObj)

3)FileInputStream常用API

3.1)从输入流中读取一个字节返回int型变量,若到达文件末尾,则返回-1

public int read() throws IOException

理解读取的字节为什么返回int型变量

1、方法解释中的-1相当于是数据字典告诉调用者文件已到底,可以结束读取了,这里的-1是Int型

2、那么当文件未到底时,我们读取的是字节,若返回byte类型,那么势必造成同一方法返回类型不同的情况这是不允许的

3、我们读取的字节实际是由8位二进制组成,二进制文件不利于直观查看,可以转成常用的十进制进行展示,因此需要把读取的字节从二进制转成十进制整数,故返回int型

4、 因此结合以上3点,保证返回类型一致以及直观查看的情况,因此该方法虽然读取的是字节但返回int型

read方法读取实例--最后输出内容和字符内容一致是123

package com.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileStream
{
    /**
     *
     *
     */
    public static void main(String[] args)
    {
        //建立文件对象
        File file=new File("C:\\Users\\Administrator\\Desktop\\1.txt");
        try
        {
            //建立链接
            FileInputStream fileInputStream=new FileInputStream(file);
            int  n=0;
            StringBuffer sBuffer=new StringBuffer();
            while (n!=-1)  //当n不等于-1,则代表未到末尾
            {
               n=fileInputStream.read();//读取文件的一个字节(8个二进制位),并将其由二进制转成十进制的整数返回
               char by=(char) n; //转成字符
               sBuffer.append(by);
            }
           System.out.println(sBuffer.toString());
        }
        catch (FileNotFoundException e)
        {
           System.out.println("文件不存在或者文件不可读或者文件是目录");
        }
        catch (IOException e)
        {
           System.out.println("读取过程存在异常");
        }
    }
}

3.2)从输入流中读取b.length个字节到字节数组中,返回读入缓冲区的总字节数,若到达文件末尾,则返回-1

public int read(byte[] b) throws IOException

1. 我们先设定一个缓冲区即字节数组用于存储从流中读取的字节数据,该数组的长度为N

2. 那么就是从流中读取N个字节到字节数组中。但是注意返回的是读入的总字节数而并不是N,说明有的时候实际读入的总字节数不一定等于数组的长度

3. 文件的内容是12345.那么流中一共有5个字节,但是我们设定的字节数组长度为2.那么会读取几次?每次情况是怎么样的?

public class FileStream
{
    public static void main(String[] args)
    {
        //建立文件对象
        File file=new File("C:\\Users\\Administrator\\Desktop\\1.txt");
        try
        {
            //建立链接
            FileInputStream fileInputStream=new FileInputStream(file);

            int  n=0;
            byte[] b=new byte[2];
            int i=0;
            while (n!=-1)  //当n不等于-1,则代表未到末尾
            {
               n=fileInputStream.read(b);//返回实际读取到字节数组中的字节数
               System.out.println(n);
               System.out.println(Arrays.toString(b)); //读取后的字节数组内容
               i++;
               System.out.println("执行次数:"+i);
            }
            System.out.println(new String(b));
        }
        catch (FileNotFoundException e)
        {
           System.out.println("文件不存在或者文件不可读或者文件是目录");
        }
        catch (IOException e)
        {
           System.out.println("读取过程存在异常");
        }
    }
}

实际执行结果如下:

可以看出,数组长度为2,因此第一次读取2个字节到数组中,数组已经被填满。流中还剩余3个字节继续读取

第二次读取,仍然读取2个字节到数组中,数组内容被替换。此时流中只剩余1个字节,根据API说明,读取数组长度(2)个字节到数组中,但接下来已经无法继续读取2个字节了, 是否就应该停止了?

实际过程中并未停止,而是进行了第三次读取,只读取了剩余1个字节,并顶替到了数组的0下标位置中。

接下来第4次读取,才发现移到末尾,而后返回-1.停止读取

所以此处存疑-----为什么当剩余只有1个字节,而要求是读取2个字节时,还可以继续读取?

那么我们查看此方法源码,发现其本质是调用的其它方法readBytes(b, 0, b.length);

public int read(byte b[]) throws IOException {
        return readBytes(b, 0, b.length);
    }

继续查看readBytes(b, 0, b.length)方法是native方法代表该方法是有实现体的但不是在JAVA语言中实现的导致没办法看具体实现

但是可以理解参数b是我们设置的数组,0是int型,最后一个参数是数组的长度

 private native int readBytes(byte b[], int off, int len) throws IOException;

那么我们查看FileInputStream的父类InputStream,发现有关于这个方法的实现,

我们现在考虑第三次读取的时候方法执行情况,此时b是[51,52].off 是0,len是2。数据流中就只有一个字节存在了

if else if的这个条件判断发现都不符合,继续往下执行。

read()--该方法代表从流中读取一个字节,而流中此时刚好还有一个字节存在,该方法执行没有问题。返回值为53

继续往下执行发现b[0]=(byte)53.也就是将读取到的int型转为字节并存储在数组中的第一个位置,此时数组内容为[53,52]

继续执行进入for循环,此时流中已没有字节,那么read()方法返回未-1退出循环。返回变量i的值即是1.

也就是此次方法执行读取了1个字节到数组中。且读取到了文件的末尾,因此第4次执行的时候到int c=read()方法时就已经返回-1,并没有替换数组中的值了

  public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

读取过程图解:

4. 假设流中一共有5个字节,但是我们设定的字节数组长度为10,那么读取几次?每次情况是怎么样的?

public class FileStream
{
    public static void main(String[] args)
    {
        //建立文件对象
        File file=new File("C:\\Users\\Administrator\\Desktop\\1.txt");
        try
        {
            //建立链接
            FileInputStream fileInputStream=new FileInputStream(file);
            int  n=0;
            byte[] b=new byte[10];
            int i=0;
            while (n!=-1)  //当n不等于-1,则代表未到末尾
            {
               n=fileInputStream.read(b);//返回实际读取到字节数组中的字节数
               System.out.println(n);
               System.out.println(Arrays.toString(b)); //读取后的字节数组内容
               i++;
               System.out.println("执行次数:"+i);
            }
            System.out.println(new String(b));
        }
        catch (FileNotFoundException e)
        {
           System.out.println("文件不存在或者文件不可读或者文件是目录");
        }
        catch (IOException e)
        {
           System.out.println("读取过程存在异常");
        }
    }
}

执行结果如下:

结合上面提到的源码我们可以发现,源码中的for循环,尽管len是10(数组长度),但是当i=5时,流中的字节已经读取完毕,指针移到文件的末尾,因此不会继续执行for循环。并且返回5,刚好符合结果中第一次实际读取5个字节到数组中。第二次读取时指针已到末尾。因此int c = read()这里返回-1。就已经结束了方法,并没有改变数组也没有再次for循环

但是这种情况存在一个问题:即数组中有5个位置被浪费了,并没有任何数据在里面

具体读取图解:

结合以上两种情况,那么发现在使用read(byte b[])方法时的数组长度至关重要,若长度小于流的字节长度,那么最后得出的内容会出现丢失。若大于流的字节长度,那么最后数组的内存就浪费了,那么就需要根据文件的字节长度来设置数组的长度

byte[] b=new byte[(int) file.length()];

3.3)从输入流中读取最多len个字节到字节数组中(从数组的off位置开始存储字节),当len为0时则返回0,如果len不为零,则该方法将阻塞,直到某些输入可用为止--此处存疑

public int read(byte[] b,int off,int len) throws IOException

源码如下

  public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

3.4)关闭此输入流并释放与该流关联的所有系统资源---即释放与实际文件的连接(查看源码可发现有同步锁锁住资源,因此关闭流释放锁

public void close() throws IOException

三、三种read方法效率比较

1、查看三种read方法源码,其本质都是利用for循环对内容进行单字节的读取

2、从代码形式看,使用read(byte[] b)较为直观和简便,因此项目中可以此方法为主进行数据读取

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java FileInputStream与FileOutputStream使用详解

    什么是InputStream和OutputStream? InputStream和OutputStream是抽象类,是所有字节输入流和输出流的父类.这里,我们首先要分清楚两个概念: InputStream(输入流):输入流是用来读入数据的.- - - > > >读入 OutputStream(输出流):输出流是用来写出数据的.- - - > > >写出 文件输入流--FileInputStream FileInputStream 从文件系统中的某个文件中获得输入字节.

  • Java使用FileInputStream流读取文件示例详解

    一.File流概念 JAVA中针对文件的读写操作设置了一系列的流,其中主要有FileInputStream,FileOutputStream,FileReader,FileWriter四种最为常用的流 二.FileInputStream 1)FileInputStream概念  FileInputStream流被称为文件字节输入流,意思指对文件数据以字节的形式进行读取操作如读取图片视频等 2)构造方法 2.1)通过打开与File类对象代表的实际文件的链接来创建FileInputStream流对象

  • Java基础之FileInputStream和FileOutputStream流详解

    一.前言 FileInputStream 用于读取本地文件中的字节数据,继承InputStream类 FileOutputStream 将字节数据写到文件,继承OutputStream类 二.创建流对象 FileInputStream fis= new FileInputStream("绝对路径"); FileOutputStream fos= new FileOutputStream("绝对路径"); 三.FileInputStream常用方法 1.构造函数,打开

  • Java中的FileInputStream 和 FileOutputStream 介绍_动力节点Java学院整理

    FileInputStream 和 FileOutputStream 介绍 FileInputStream 是文件输入流,它继承于InputStream. 通常,我们使用FileInputStream从某个文件中获得输入字节. FileOutputStream 是文件输出流,它继承于OutputStream. 通常,我们使用FileOutputStream 将数据写入 File 或 FileDescriptor 的输出流. FileInputStream 函数接口 FileInputStream

  • Java FileInputStream读中文乱码问题解决方案

    1.前提 以读取编码是GBK的文件为案例,文件内容只有中文和中文符号 2.原因 FileInputStream读中文乱码是因为一个中文对应两个字节存储(负数),也就是说,读取对应中文的字节数应该是偶数: 而英文对应一个字节存储.FileInputStream每次读取一个数组长度的字节时,读取的中文字节数可能是奇数,也就是只读到中文的一半字节,出现乱码. 3.解决方法 一次读取所有字节,此方法不靠谱,因为不确定总字节数. 在输出时进行判断,遍历数组判断负数的个数,如果是奇数,说明读取到中文的一半字

  • java基础知识之FileInputStream流的使用

    目录 一.File流概念 二.FileInputStream 1)FileInputStream概念 2)构造方法 3)FileInputStream常用API 三.三种read方法效率比较 一.File流概念 JAVA中针对文件的读写操作设置了一系列的流,其中主要有FileInputStream,FileOutputStream,FileReader,FileWriter四种最为常用的流 二.FileInputStream 1)FileInputStream概念 FileInputStream

  • Java基础知识之CharArrayWriter流的使用

    目录 Java CharArrayWriter流 一.CharArrayWriter流定义 二.CharArrayWriter流构造函数 三.CharArrayWriter流实例域 四.CharArrayWriter流方法 四.CharArrayWriter流的作用 Java基础之什么是CharArrayWriter 下面的例子阐述了CharArrayWriter Java CharArrayWriter流 一.CharArrayWriter流定义 API说明:该类实现了一个可用作字符输出流的字

  • java基础知识I/O流使用详解

    "流"概念源于UNIX中的管道(pipe)的概念.在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备.外部文件等,它屏蔽了实际的I/O设备中处理数据的细节.   一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL. 流的方向是重要的,根据流的方向,流可以分为两类:输入流和输出流.其实输入/输出是想对于内存来说的.实际上,流的源端和目的端可简单地看成是字节的生产者和消费者,对于输入流,可不必

  • Java基础知识之ByteArrayOutputStream流的使用

    目录 Java ByteArrayOutputStream流的使用 一.ByteArrayOutputStream流定义 二.ByteArrayOutputStream流实例域 三.ByteArrayOutputStream流构造函数 四.ByteArrayOutputStream流方法 五.ByteArrayOutputStream流的作用 ByteArrayOutputStream 理解 ByteArrayOutputStream的用法 ByteArrayInputStream的用法 Jav

  • Java基础知识之StringReader流的使用

    目录 Java StringReader流的使用 一.StringReader流定义 二.StringReader的实例域 三.StringReader流构造函数 四.StringReader流的API 五.StringReader流的作用 StringReader分析 IO流分类图 StringReader Java StringReader流的使用 一.StringReader流定义 API说明:字符串输入流.其本质就是字符串 二.StringReader的实例域 //流对象 private

  • Java基础知识之StringWriter流的使用

    目录 Java StringWriter流的使用 一.StringWriter流定义 二.StringWriter流实例域 三.StringWriter流构造函数 四.StringWriter流方法 五.StringWriter流的作用 使用StringWriter和StringReader的好处 Java StringWriter流的使用 一.StringWriter流定义 API说明:在字符串缓冲区中收集输出的字符流,可用于构造字符串, 关闭流无效,关闭后调用其他方法不会报异常 二.Stri

  • Java基础知识之ByteArrayInputStream流的使用

    目录 Java ByteArrayInputStream流 一.ByteArrayInputStream流定义 二.ByteArrayInputStream流实例域 三.ByteArrayInputStream流构造函数 四.ByteArrayInputStream流方法 五.ByteArrayInputStream流的作用 ByteArrayInputStream的用法解析 Java ByteArrayInputStream流 一.ByteArrayInputStream流定义 API说明:B

  • Java基础知识之CharArrayReader流的使用

    目录 Java CharArrayReader流 一.CharArrayReader流定义 二.CharArrayReader流的构造函数 三.CharArrayReader流的实例域 四.CharArrayReader流的API 四.CharArrayReader流的作用 什么是CharArrayReader Java CharArrayReader流 一.CharArrayReader流定义 API说明:该类实现了一个可用作字符输入流的字符缓冲区,即该类可利用字符缓冲区当做字符输入流进行读取

  • Java基础知识之BufferedReader流的使用

    目录 一.BufferedReader类概念 二.BufferedReader类实例域 三.BufferedReader类构造函数 四.BufferedReader类API 五.BufferedReader类与InputStreamReader类比较 一.BufferedReader类概念 API文档描述: BufferedReader类从字符输入流中读取文本并缓冲字符,以便有效地读取字符,数组和行 可以通过构造函数指定缓冲区大小也可以使用默认大小.对于大多数用途,默认值足够大 由Reader构

  • Java基础知识杂文

    1.基本概念 IO是主存和外部设备(硬盘.终端和网络等)拷贝数据的过程.IO是操作系统的底层功能实现,底层通过I/O指令进行完成. 所有语言运行时系统提供执行I/O较高级别的工具.(c的printfscanf,java的面向对象封装) 2.Java标准io回顾 Java标准IO类库是io面向对象的一种抽象.基于本地方法的底层实现,我们无须关注底层实现.InputStream\OutputStream(字节流):一次传送一个字节.Reader\Writer(字符流):一次一个字符. 3.nio简介

随机推荐