怎么读文件

目录

一、简单理解文件的读和写

二、资源泄露

三、读取文件的简单写法

1.read()一次读取一个字节

2.调用read()一次调用多个字节的代码

3.从一个文件中读取中文的代码,严格来说是读取中英文混合的文件的代码


一、简单理解文件的读和写

在Java标准库中,有很多关于读和写的类,此处我们只用两个类

InputStream/FileInputStream

文件读取操作,按照字节为单位进行读文件

OutputStream/FileOutputStream

文件写入操作,按照字节为单位进行写文件

Stream,意思是“流”

上面的读写操作本质上是对字节流的操作,那么该如何理解“流”呢?

我们可以从“流”想到水,水在自然界是源源不断地,我们想象瀑布,而字节也是那种感觉进行运动的。

而字节流就类似于水流

我们举一个例子:通过水龙头接100ml水,我们采取的措施可以是

一次直接接满100ml;

两次,每次接50ml;

两次,一次接30ml,一次接70ml;

十次,每次接10ml。

而从文件中读取100个字节的数据的话,可以

一次直接读100字节;

两次,每次读50字节;

两次,一次读30字节,一次读70字节;

十次,每次读10字节。

字节流的最小单位是字节

在Java中,除了字节流之外,还有字符流,它是以字符为单位进行读写。

二、资源泄露

当读取完文件之后,调用inputStream.close()方法非常重要,它的作用是关闭文件,如果没有及时关闭,就可能会造成资源泄露。

关于资源泄露,我们都知道,系统中的很多资源都是有限的,比如内存、文件描述符。系统可分为用户态和内核态,在用户态中打开一个应用程序,在内核态中会创建一个相应的PCB,在这个PCB中有一个文件描述符表(类似于数组的一个顺序表),每次这个PCB打开一个文件,就会具体的产生一个文件描述符(是一个小的整数),然后把这个文件描述符放到这个文件描述符表中。更准确地说,这个文件描述符表类似于与一个数组,数组的元素是一个struct File,而数组的下标叫做文件描述符。每次打开一个文件,就要在文件描述符表中申请一项,每次关闭一个文件,就会把相应的表项释放掉,供后面使用,由于这个文件描述符表的长度是存在上限的,如果一直持续不断的打开文件,而且又不关闭的话,很快就会把这个文件描述符表给用完,此时再尝试打开文件,就会失败。这个就类似于一个大水缸,缸满后就不能再装水了,要不然,水就会溢出来。而泄露就是这个意思,千万别理解成不安全,被别人给偷了的意思。

三、读取文件的简单写法

以下是读取文件内容的最简单的两个代码,推荐第二种,因为第二种写法应用了try with resources语法,更简单明了。

1.read()一次读取一个字节

这是调用read()一次读取一个字节的情况

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FileDemo9 {
    public static void main(String[] args) {

        //第一种写法,比较繁琐,更推荐第二种写法
        InputStream inputStream=null;
        try{
            //这个创建实例的过程就相当于是打开文件的过程
            //要先打开文件才能读写文件
            inputStream=new FileInputStream("hello.txt");

            //通过逐个字节的方式把文件的内容读出来
            while(true){
                //调用read可以读取出一些数据出来
                //read有好几个版本,其中无参数版本是一次读取一个字节
                //对于无参数版本的read来说,返回值是这次操作读到的字节
                //返回值的范围是0~255
                //如果读到文件末尾(EOF,end of file),此时如果继续调用read,就会返回-1
                int b=inputStream.read();
                if (b == -1) {
                    break;
                }
                System.out.printf("%c",b);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();//关闭文件,防止资源泄露
                //即使这样,当代码前面出现异常时,这条代码还是不能执行,即还是会出现资源泄露
                //解决这个问题的方法是加try   catch    finally,将这条代码写在finally中
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        }


        System.out.println();


        //第二种写法
        //这种写法应用了try with resources语法
        //能更简单地完成这个任务
        try(InputStream inputStream1=new FileInputStream("hello.txt")){
            while(true){
                int b=inputStream1.read();
                if (b == -1) {
                    break;
                }
                System.out.printf("%c",b);
            }
        }catch (IOException e){
            e.printStackTrace();
        }

    }
}

2.调用read()一次调用多个字节的代码

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;


//一次读取1024个字节
public class FileDemo10 {
    public static void main(String[] args) {
        try(InputStream inputStream=new FileInputStream("hello.txt")){
            byte[] buffer=new byte[1024];
            while(true){
                int len=inputStream.read(buffer);
                if (len == -1) {
                    break;
                }
                for (int i = 0; i < len; i++) {
                    System.out.printf("%c",buffer[i]);
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

这个代码的执行过程是

代码中的read会尝试把参数中的buffer数组给填满,buffer是1024个字节

假设该文件的大小是2049字节,读写过程为:

第一次循环,读取出1024个字节,放到buffer数组中,read返回一个1024

第二次循环,读取出1024个字节,放到buffer数组中,read返回一个1024

第三次循环,读取出1个字节,放到buffer数组中,read返回一个1

第四次循环,此时已经读到文件末尾了(EOF),read返回一个-1,结束循环

当调用read()一次调用多个字节的代码的时候,假如打开的文件中有中文,那么就会报这个异常

出现这个异常的原因是中文的编码方式和英文的是不太一样的,英文直接用ASCII编码,而中文用UTF-8或者GBK编码,代码中的%c其实相当于是按照ASCII的方式来进行打印,这个时候读取的字节刚好有一部分是UTF-8或者GBK,此时读取的结果就不是一个合法的ASCII值,于是就会出现异常。

一个汉字在UTF-8中,是由三个字节构成的

解决这个问题的办法是以下代码

3.从一个文件中读取中文的代码,严格来说是读取中英文混合的文件的代码

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;


//从文件中读取中文,借助标准库中内置的处理字符集的方式
//Scanner不光能从控制台读取标准输入,也可以从文件中读取数据
public class FileDemo11 {
    public static void main(String[] args) {
        try(InputStream inputStream=new FileInputStream("hello.txt")) {

            //这里需要注意的是,Scanner里面也有一个close方法,这个close其实也是用来关闭Scanner里面包含的InputStream的
            try(Scanner scanner=new Scanner(inputStream,"UTF-8")){
                while(scanner.hasNext()){
                    String s= scanner.next();
                    System.out.print(s);
                }
            }

        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

运行的结果为

这里面需要注意的是Scanner

在上述代码中Scanner是有close()方法的,目的是关闭Scanner内的文件

当Scanner内部持有的是InputStream是System.in(标准输入)

标准输入这个文件一般不关闭,这个文件是每个进程被创建出来之后,默认打开的文件

  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值