java io流

一、File流概念

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

1.1 FileWriter 写文本文件:
import java.io.*;

/*
 * 写入到C盘hui目录下的一个文本文件,并往里写入若干行文字。
 *如果该次磁盘下有重复的内容,则删除重复的内容后再写入一个新的内容。
 */
public class TestWriteText{

	public static void main(String[] args){
        // 创建一个File的实例对象
        File file=new File("C:\\Users\\huiqiang\\Desktop\\hui");
        //判断file是否存在,不存在就创建出一个文件目录
        if(!file.exists()){
            file.mkdirs();
            System.out.println("该目录不存在,已创建");
        }
        File file1=new File(file,"lian.txt");
        // 判断file1是否存在,不存在就创建出一个文件
        if(!file1.exists()){
            file.mkdirs();
            System.out.println("该文件不存在,已创建");
        }
        // 在main方法中声明抛出IO异常。
        // 定义写入路径的字符串。
        String fileName ="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
        try
        {
            // 使用面向字符流的FileWriter类。
            // 直接调用构造方法,传递写入路径。FileWriter类的构造方法有五个。
            FileWriter writer1 = new FileWriter(fileName);
            // OutputStreamWriter writer1 = new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8"); // 转换成utf-8格式
            // 相当于在FileWriter类上再套上一层管道。如果要写入的内容更多,就应该用高效的缓冲流类:BufferedWriter.与FileWriter不同是多个了newLine()方法用于换行。
            BufferedWriter writer = new BufferedWriter(writer1);
            //FileWriter类的Write()方法向文件中写入字符。
            writer.write("a1.sources = r1\n");
            writer.write("a1.sinks = k1\n");
            writer.write("a1.channels = c1\n");
            writer.write("\n");
            writer.write("a1.sources.r1.type = exec\n");
            writer.write("a1.sources.r1.command = tail -F /home/hadoop/messages\n");
            writer.write("a1.sources.r1.port = 44444\n");
            writer.write("a1.sources.r1.host = 192.168.8.71\n");
            writer.write("a1.sources.r1.channels = c1\n");
            writer.newLine();
            writer.write("a1.sinks.k1.type = logger\n");
            writer.write("a1.sinks.k1.type = hbase\n");
            writer.write("a1.sinks.k1.table = messages\n");
            writer.write("a1.sinks.k1.columnFamily = cf\n");
            writer.write("a1.sinks.k1.serializer = com.tcloud.flume.AsyncHbaseLogEventSerializer\n");
            writer.write("a1.sinks.k1.channel = memoryChannel\n");
            writer.newLine();
            writer.write("a1.channels.c1.type = memory\n");
            writer.write("a1.channels.c1.capacity = 1000\n");
            writer.write("a1.channels.c1.transactionCapacity = 100\n");
            writer.write("\n");
            writer.write("a1.sources.r1.channels = c1\n");
            writer.write("a1.sinks.k1.channel = c1");
            // 清空流里的内容并关闭它,如果不调用该方法还没有完成所有数据的写操作,程序就结束了。
            writer.close();
        }
        catch(IOException iox)
        {
        	System.out.println("Problemwriting" + fileName);
        }
    }
}
1.2 FileReader 读文本文件:
import java.io.*;

/*
 * 从C盘的lian.txt中读出文件中的数据到命令行中。
 *
 */
public class TestBufferedReader{
	
    public static void main(String[] args){
        //在main方法中声明抛出IO异常。
        //定义写入路径的字符串。
		String fileName ="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
		String line;
		try
		{
            // 利用BufferedReader套在FileReader,处理流套在节点流上。
			BufferedReader in = new BufferedReader(new FileReader(fileName));
            //首先读取一行的内容。
			line = in.readLine();
            //判断读写的内容是否为空。
			while(line!=null)
			{
                //输出一行。
				System.out.println(line);
                //继续读出下一行的内容。
				line = in.readLine();
			}
            //关闭这个“管道”。
			in.close();
		} 
		catch(IOException iox)
		{
			System.out.println("Problemreading" + fileName);
		}
	}
}

注意:在执行过程中遇到了乱码的问题。

1.执行 javac hehe.java 时报错误: 编码 GBK 的不可映射字符
解决:javac -encoding UTF-8 hehe.java
2.在控制台打印出来的内容有乱码
解决:将上面的相关代码改为

        	InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
        	BufferedReader in = new BufferedReader(isr);

  优化:

import java.util.zip.ZipInputStream;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.charset.Charset;
import java.io.*;

public class Test {

    /**
     * 多种文件编码方式
     */
    private static final String[] charsetNames = new String[]{"GB2312", "UTF-8", "Unicode", "GBK"};

    public List<String> getZipDataMap(String filePath) throws IOException {
        InputStream inputStream = Files.newInputStream(Paths.get(filePath));
        ZipInputStream zis = new ZipInputStream(inputStream);
        InputStreamReader streamReader = null;
        BufferedReader reader = null;
        List<String> lineList = new ArrayList<>();

        for (String charsetName : charsetNames) {
            streamReader = new InputStreamReader(zis, Charset.forName(charsetName));
            reader = new BufferedReader(streamReader);
            String line;
            while ((line = reader.readLine()) != null) {
                lineList.add(line);
            }
        }
        return lineList;
    }
}
1.3 FileInputStream:

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

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @author: huiq
 * @createTime: 2021/9/1 8:57
 * @description:
 */
public class Demo {

    public static void main(String[] args) {

        // 得到一个文件对象
        File f = new File("C:\\Users\\9\\Desktop\\临时文件夹\\kafka.yml");
        FileInputStream fis = null;

        try {
            // 因为File没有读写能力,所以需要有个InpuntStream
            fis = new FileInputStream(f);

            // 定义一个字节数组,相当于缓存
            byte []bytes= new byte[1024];
            int n = 0;

            // 循环读取
            while ((n=fis.read(bytes)) != -1) {
                // 把字节转成String
                String s = new String(bytes, 0, n);
                System.out.println(s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 这个流最后必须关闭,而且应该在finally方法中关闭
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  InputStream 是字节输入流的所有类的超类,一般我们使用它的子类,如 FileInputStream 等。

  OutputStream 是字节输出流的所有类的超类,一般我们使用它的子类,如 FileOutputStream 等。

  InputStreamReader 是字节流通向字符流的桥梁,它将字节流转换为字符流。

  OutputStreamWriter 是字符流通向字节流的桥梁,它将字符流转换为字节流。

  BufferedReader 由 Reader 类扩展而来,提供通用的缓冲方式文本读取,readLine 读取一个文本行,从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。

  BufferedWriter 由 Writer 类扩展而来,提供通用的缓冲方式文本写入,newLine 使用平台自己的行分隔符,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

  InputStream 能从來源处读取一個一個 byte,所以它是最低级的; InputStreamReader 封裝了 InputStream 在里头,它以较高级的方式,一次读取一个一个字符,下例是假设有一个编码为 utf8 的文档, 其內容只有一个中文字 “陳”。

import java.io.*;

public class Main {     
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {     
                     
            FileInputStream fis=new FileInputStream("d://desktop//test.txt");     
            try {     
                InputStreamReader isr=new InputStreamReader(fis,"utf8");     
                int i;     
                while((i=isr.read()) != -1){     
                    System.out.println((char)i);  //輸出  陳     
                }     
            } catch (Exception e) {     
                // TODO Auto-generated catch block     
                e.printStackTrace();     
            }                                            
                     
    }     
}

  BufferedReader 则是比 InputStreamReader 更高级,它封裝了 StreamReader 类,一次读取取一行的字符。

import java.io.*;

public class Main {     
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {     
                     
            FileInputStream fis=new FileInputStream("d://desktop//test.txt");     
                 
            try {     
                InputStreamReader isr=new InputStreamReader(fis,"utf8");                     
                BufferedReader br=new BufferedReader(isr);     
                     
                String line;     
                while((line=br.readLine()) != null){     
                    System.out.println(line);     
                }     
            } catch (Exception e) {     
                // TODO Auto-generated catch block     
                e.printStackTrace();     
            }                                            
                     
    }     
}

参考:
FileInputStream、InputStreamReader和BufferedReader几种读取文件的区别
几种Java读写数据的流性能对比

二、二进制文件

  包含在ASCII及扩展ASCII字符中编写的数据或程序指令的文件。

  计算机文件基本上分为二种:二进制文件和ASCII(也称纯文本文件),图形文件及文字处理程序等计算机程序都属于二进制文件。这些文件含有特殊的格式及计算机代码。ASCII则是可以用任何文字处理程序阅读的简单文本文件。

2.1 写二进制文件:
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;

/*
 * 用字节流写二进制文件。将三个int类型255 0 -1写入数据文件lian.txt中。
 */
public class TestDataOutputStream{

    /**
     * java.io包中的OutputStream及其子类专门用于写二进制数据。
     * FileOutputStream是其子类,可用于将二进制数据写入文件。
     * DataOutputStream是OutputStream的另一个子类,它可以
     * 连接到一个FileOutputStream上,便于写各种基本数据类型的数据。
     */
    public static void writeMethod1()
    {
        String fileName="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
        int value0=255;
        int value1=0;
        int value2=-1;
        try
        {
            // 将DataOutputStream与FileOutputStream连接可输出不同类型的数据
            // FileOutputStream类的构造函数负责打开文件kuka.dat,如果文件不存在,
            // 则创建一个新的文件,如果文件已存在则用新创建的文件代替。然后FileOutputStream
            // 类的对象与一个DataOutputStream对象连接,DataOutputStream类具有写
            // 各种数据类型的方法。
            DataOutputStream out=new DataOutputStream(new FileOutputStream(fileName));
            out.writeInt(value0);
            out.writeInt(value1);
            out.writeInt(value2);
            out.close();
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    //对于大量数据的写入,使用缓冲流BufferedOutputStream类可以提高效率
    public static void writeMethod2()
    {
        String fileName="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
        try
        {
            DataOutputStream out=new DataOutputStream(
                    new BufferedOutputStream(
                            new FileOutputStream(fileName)));
            out.writeInt(10);
            System.out.println(out.size()+" bytes have been written.");
            out.writeDouble(31.2);
            System.out.println(out.size()+" bytes have been written.");
            out.writeBytes("JAVA");
            System.out.println(out.size()+" bytes have been written.");
            out.close();
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        writeMethod1();
//        writeMethod2();
    }
}

  这里列出在 UltraEdit(UE)里看到的东西:

在这里插入图片描述

2.2 读二进制文件:
import java.io.*;

public class TestDataInputStream{

    /**
     * 对二进制文件比较常见的类有FileInputStream,DataInputStream
     * BufferedInputStream等。类似于DataOutputStream,DataInputStream
     * 也提供了很多方法用于读入布尔型、字节、字符、整形、长整形、短整形、
     * 单精度、双精度等数据。
     */
    public static void readMethod1()
    {
        //定义写入路径的字符串。
        String fileName ="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
        int sum = 0;
        try {
            DataInputStream instr= new DataInputStream(
                    new BufferedInputStream(new FileInputStream(fileName)));
            sum += instr.readInt();
            sum += instr.readInt();
            sum += instr.readInt();
            System.out.println( "The sum is: " + sum );
            instr.close();
        }
        catch ( IOException iox ) {
            System.out.println("Problem reading " + fileName);
        }
    }

    public static void readMethod2()
    {
        try
        {
            FileInputStream stream=new FileInputStream("C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt");
            int c;
            while((c=stream.read())!=-1)
            {
                System.out.println(c);
            }
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        readMethod1();
//        readMethod2();
    }
}

运行结果:
The sum is: 254

  readMethod1 方法中当我们不知道是三个数据的时候,多读的话就会抛出 EOFException 异常,所以我们将读操作放在 try中。我们可以使用嵌套的try catch块来对异常进行进一步的处理:

    public static void readMethod1()
    {
        String fileName ="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
        int sum = 0;
        try {
            DataInputStream instr= new DataInputStream(new BufferedInputStream(new FileInputStream(fileName)));
            try{
                while(true) {
                    sum += instr.readInt();
                }
            } catch(EOFException eof){
                System.out.println( "The sum is: " + sum );
                instr.close();
            }
        }
        catch ( IOException iox ) {
            System.out.println("Problem reading " + fileName);
        }
    }

注意:路径的写法:1. 如果你是在 Windows 的 myeclipse 中运行程序的话要这写 C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt 2. 如果是在 Linux 中运行程序的话要这样写 /home/hadoop/hui/lian.txt

参考:
java IO 流(二)写文件和读文件
IO流中的文件创建并且写入读取
Java.过滤流(包装流)
Java读写二进制文件操作

三、逐行读取文本文件的几种方式以及效率对比

参考:
java 读取文件 效率_Java 逐行读取文本文件的几种方式以及效率对比
Apache Commons-io工具类的使用

  1000000 行文本读取结果比对:

BufferedReader 耗时: 49ms

Scanner 耗时: 653ms

Apache Commons IO 耗时: 44ms

InputStreamReader 耗时: 191ms

FileInputStream 耗时: 3171ms

BufferedInputStream 耗时: 70ms

FileUtils 耗时: 46ms

Files 耗时: 99ms

  24488656 行文本读取结果比对:

BufferedReader 耗时: 989ms

Scanner 耗时: 11899ms

Apache Commons IO 耗时: 568ms

InputStreamReader 耗时: 3377ms

FileInputStream 耗时: 78903ms

BufferedInputStream 耗时: 1480ms

FileUtils 耗时: 16569ms

Files 耗时: 25162ms

  可见,当文件较小时:

ApacheCommonsIO 流表现最佳;

FileUtils, BufferedReader 居其二;

BufferedInputStream, Files 随其后;

InputStreamReader, Scanner, FileInputStream 略慢.

当文件较大时, Apache Commons IO 流, BufferedReader 依然出色, Files, FileUtils 速度开始变慢.

  简要分析,使用到的工具类包括:

java.io.BufferedReader

java.util.Scanner

org.apache.commons.io.FileUtils

java.io.InputStreamReader

java.io.FileInputStream

java.io.BufferedInputStream

com.google.common.io.Files

  其中:Apache Commons IO 流和 BufferedReader 使用到了缓冲区,所以在不消耗大量内存的情况下提高了处理速度;FileUtils 和 Files 是先把文件内容全部读入内存,然后在进行操作,是典型的空间换时间案例,这种方法可能会大量消耗内存,建议酌情使用;其他几个工具类本来就不擅长逐行读取,效率底下也是情理之中。

  建议:在逐行读取文本内容的需求下,建议使用 Apache Commons IO 流,或者 BufferedReader,既不会过多地占用内存,也保证了优异的处理速度。

  疑惑:我看 Apache Commons IO 底层源码就是使用了 BufferedReader,那为什么 Apache Commons IO 要比 BufferedReader 快呢,我目前实测也是 BufferedReaderApache Commons IO 稍快一些,不知道是我的测试方法是否有问题。

// 源码 commons-io 2.11.0
public class IOUtils {
	
	// IOUtils.readLines 方法
    public static List<String> readLines(InputStream input, Charset charset) throws IOException {
        InputStreamReader reader = new InputStreamReader(input, Charsets.toCharset(charset));
        return readLines((Reader)reader);
    }

    public static List<String> readLines(Reader reader) throws IOException {
        BufferedReader bufReader = toBufferedReader(reader);
        ArrayList list = new ArrayList();

        String line;
        while((line = bufReader.readLine()) != null) {
            list.add(line);
        }

        return list;
    }
    
    // IOUtils.lineIterator 方法
    public static LineIterator lineIterator(InputStream input, Charset charset) {
        return new LineIterator(new InputStreamReader(input, Charsets.toCharset(charset)));
    }
    
    public LineIterator(Reader reader) throws IllegalArgumentException {
        if (reader == null) {
            throw new IllegalArgumentException("Reader must not be null");
        } else {
            if (reader instanceof BufferedReader) {
                this.bufferedReader = (BufferedReader)reader;
            } else {
                this.bufferedReader = new BufferedReader(reader);
            }

        }
    }
}

在这里插入图片描述

3.1 Apache Commons Io

参考:Apache Commons IO: 简化文件和IO操作

  commons-io 是 Java 文件 IO『第一库』是公认的,它的功能和代码质量都是极佳的,它好到没有人想到再写一个竞品与之竞争,这么干完全是费力不讨好的做法。

  在做 Java 编程的时候,经常会遇到各种文件操作和输入输出(IO)的问题。不论是读取一个配置文件,还是把数据写入日志,这些看似简单的任务有时候会让人头疼。传统的 Java IO 操作,虽然功能强大,但往往代码冗长,而且容易出错。这时候,Apache Commons IO 库就像一股清泉,为咱们简化这些操作提供了极大的便利。在 Java 世界里,Apache Commons IO 是一个非常受欢迎的库,它提供了一系列工具类,帮助咱们以更高效、更简洁的方式处理文件和 IO。

  Apache Commons IO 库。这个库是 Apache Commons 项目的一部分,目的是为 Java 开发者提供一系列通用的 IO 操作工具。不知道咱们有没有注意到,Java 标准库中的 IO 操作有时候显得有点复杂和笨重。Apache Commons IO 就是为了解决这个问题而诞生的。

  问题描述:在读取非常大的文件时,可能会遇到内存溢出的问题。

  解决方案:使用流式处理方式,而不是一次性将整个文件加载到内存中。比如,可以使用 LineIterator 来逐行处理文件,这样可以有效地控制内存使用。

  代码示例:高效处理大文件。在处理大型文件时,咱们需要特别注意资源管理和效率。下面是一个示例,展示如何高效地读取大文件。

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class LargeFileReadExample {
    public static void main(String[] args) throws IOException {
        // 方式一:FileUtils.lineIterator
        File largeFile = new File("大型文件.txt");
        try (LineIterator it = FileUtils.lineIterator(largeFile, "UTF-8")) {
            List<String> lineList = new ArrayList<>();
            while (it.hasNext()) {
                lineList.add(it.nextLine());
            }
            LineIterator.closeQuietly(it);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 方式二:FileUtils.readLines
        try {
            List<String> lines = FileUtils.readLines(largeFile, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 方式三:IOUtils.lineIterator
        InputStream inputStream = Files.newInputStream(Paths.get("大型文件.txt"));
        LineIterator it = IOUtils.lineIterator(inputStream, "UTF-8");
        List<String> lineList = new ArrayList<>();
        while (it.hasNext()) {
            lineList.add(it.nextLine());
        }
        LineIterator.closeQuietly(it);

        // 方式四:IOUtils.readLines
        List<String> lines = IOUtils.readLines(inputStream, "UTF-8");
    }
}

  在这个例子中,LineIterator 类被用于逐行读取文件,这样可以防止将整个文件内容一次性加载到内存中,从而提高了处理大文件的效率。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小强签名设计

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值