目录

  • 1读取文件
  • 1.1 JDK1.5的Scanner类读取
  • 1.2 JDK8的Files.lines+Stream流按行读取(推荐)
  • 1.3 JDK11提供的Files.readString()
  • 1.4 FileInputStream+InputStreamReader+BufferedReader按行读取
  • 1.5 经典管道流方式(推荐)
  • 2 JAVA快速统计大文本文件行数
  • 2.1BufferedReader类的readLine()方法递归遍历文件
  • 2.2 改进的方法使用了LineNumberReader类
  • 2.3 JAVA8的出现也为我们提供了一些新的思路
  • 3 参考资料



使用Java读取一个txt文件,包含48行3列的数据集,前5行如下。总结了5种读取文件的方式。

1 6734 1453
2 2233 10
3 5530 1424
4 401 841
5 3082 1644

1读取文件

1.1 JDK1.5的Scanner类读取

第一种方式是Scanner,从JDK1.5开始提供的API,特点是可以按行读取、按分割符去读取文件数据,既可以读取String类型,也可以读取Int类型、Long类型等基础数据类型的数据。

public static Node[] readFile_Scanner(String filepath) throws IOException {
        int lineNum = (int) Files.lines(Paths.get(new File(filepath).getPath())).count();
        Node[] nodes = new Node[lineNum];

        try (Scanner sc = new Scanner(new FileReader(filepath))) {
            while (sc.hasNextLine()) {  //按行读取字符串
                String line = sc.nextLine();
                String[] data = line.split(" ");
                int id = Integer.parseInt(data[0]);
                int x = Integer.parseInt(data[1]);
                int y = Integer.parseInt(data[2]);
                Node node = new Node(id, x, y);
                nodes[id - 1] = node;
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        return nodes;
    }

1.2 JDK8的Files.lines+Stream流按行读取(推荐)

如果你是需要按行去处理数据文件的内容,这种方式是我推荐大家去使用的一种方式,代码简洁,使用java 8的Stream流将文件读取与文件处理有机融合。

public static Node[] readFile_Java8(String filepath) throws IOException {
        int lineNum = (int) Files.lines(Paths.get(new File(filepath).getPath())).count();
        Node[] nodes = new Node[lineNum];

        // 读取文件内容到Stream流中,按行读取
        Stream<String> lines = Files.lines(Paths.get(filepath));

        // 随机行顺序进行数据处理
        lines.forEach(line -> {
            String[] data = line.split(" ");
            int id = Integer.parseInt(data[0]);
            int x = Integer.parseInt(data[1]);
            int y = Integer.parseInt(data[2]);
            Node node = new Node(id, x, y);
            nodes[id - 1] = node;
        });
        return nodes;
    }

forEach获取Stream流中的行数据不能保证顺序,但速度快。如果你想按顺序去处理文件中的行数据,可以使用forEachOrdered,但处理效率会下降。

// 按文件行顺序进行处理
lines.forEachOrdered(System.out::println);

1.3 JDK11提供的Files.readString()

从 java11开始,为我们提供了一次性读取一个文件的方法。文件不能超过2G,同时要注意你的服务器及JVM内存。这种方法适合快速读取小文本文件

public static Node[] readFile_JDK11(String filepath) throws IOException {
        int lineNum = (int) Files.lines(Paths.get(new File(filepath).getPath())).count();
        Node[] nodes = new Node[lineNum];

        Path path = Paths.get(filepath);
        String txt = Files.readString(path);
        
        String[] lines = txt.split("\n");

        for (String line : lines) {

            String[] id_x_y = line.split(" ", 3);
            int id = Integer.parseInt(id_x_y[0]);
            int x = Integer.parseInt(id_x_y[1]);
            int y = Integer.parseInt(id_x_y[2].trim());//去除字符串两端的特殊字符
            Node node = new Node(id, x, y);
            nodes[id - 1] = node;
        }
        return nodes;

    }

1.4 FileInputStream+InputStreamReader+BufferedReader按行读取

  1. java提供了一个FileInputStream,我们可以直接以文件路径构造这个流,也可以以文件对象构造他,如:FileInputStream fin = new FileInputStream("d:/aa.txt");
  2. 然后使用这个流就可以直接读取到文件了,但是这个时候读取到的内容是int类型的数值,所以需要进一步的处理,我们把fin外面包上一个InputStreamReader,就变成了这样:InputStreamReader reader = new InputStreamReader(fin);
  3. 接下来,使用这个reader构造BufferedReaderBufferedReader拥有一个名为readLine的方法,可以读取一整行的文本,作为字符串返回,因此用起来会比较方便。
public static Node[] readFile(String filepath) throws IOException {
        int lineNum = (int) Files.lines(Paths.get(new File(filepath).getPath())).count();
        System.out.println(lineNum);
        Node[] nodes = new Node[lineNum];
        FileInputStream fin = new FileInputStream(filepath);
        InputStreamReader reader = new InputStreamReader(fin);
        BufferedReader buffReader = new BufferedReader(reader);
        String line = "";
        while ((line = buffReader.readLine()) != null) {
            String[] data = line.split(" ");
            int id = Integer.parseInt(data[0]);
            int x = Integer.parseInt(data[1]);
            int y = Integer.parseInt(data[2]);
            Node node = new Node(id, x, y);
            nodes[id - 1] = node;
        }
        buffReader.close();
        return nodes;
    }

1.5 经典管道流方式(推荐)

这种方式可以通过管道流嵌套的方式,组合使用,比较灵活。

public static Node[] readFile_(String filepath) throws IOException {

        int lineNum = (int) Files.lines(Paths.get(new File(filepath).getPath())).count();
        System.out.println(lineNum);
        Node[] nodes = new Node[lineNum];

        // 带缓冲的流读取,默认缓冲区8k
        try (BufferedReader br = new BufferedReader(new FileReader(filepath))) {
            String line;
            while ((line = br.readLine()) != null) {
                String[] data = line.split(" ");
                int id = Integer.parseInt(data[0]);
                int x = Integer.parseInt(data[1]);
                int y = Integer.parseInt(data[2]);
                Node node = new Node(id, x, y);
                nodes[id - 1] = node;
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return nodes;

    }

2 JAVA快速统计大文本文件行数

2.1BufferedReader类的readLine()方法递归遍历文件

统计某文件的文本行数,常用的方法是通过BufferedReader类的readLine()方法递归遍历文件,从而间接地统计行数。然而对于大的文本文件,尤其是一些生信的测序文件,readLine()的方法显然不能让人满意,所以,通过查阅了一些资料,找到了一些更为高效的方法。测试文件选择了一个4985014行的文件,文件大小为242MB。测试耗时以毫秒为单位。

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
 
public class LineNumberReaderExample
{
    public static void main(String[] args)
    {
    	try{
    		File file =new File("c:\\test.txt");
    		if(file.exists()){
    		    FileReader fr = new FileReader(file);
    		    LineNumberReader lnr = new LineNumberReader(fr);
    		    int linenumber = 0;
    	            while (lnr.readLine() != null){
    	        	linenumber++;
    	            }
    	            System.out.println("Total number of lines : " + linenumber);
    	            lnr.close();
    		}else{
    			 System.out.println("File does not exists!");
    		}
    	}catch(IOException e){
    		e.printStackTrace();
    	}
    }
}

2.2 改进的方法使用了LineNumberReader类

使用LineNumberReader类使用默认的输入缓冲区大小来创建新的行号读取器。skip()方法用于跳过n个数字字符,所以在这里我们跳过 文件长度大小的字符,跳到文件的末尾。getLineNumber()方法返回最后一行的当前行号。具体的代码如下:

public static void main(String[] args) {
    	try {
    		File file = new File("e://test.fa");
    		if(file.exists()){
    			long fileLength = file.length();
    			LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(file));
    			lineNumberReader.skip(fileLength);
    	                int lines = lineNumberReader.getLineNumber();
    	                System.out.println("Total number of lines : " + lines);
	                lineNumberReader.close();
    		}else {
    			System.out.println("File does not exists!");
    		}
    	}catch(IOException e) {
    		e.printStackTrace();
    	}
    }

2.3 JAVA8的出现也为我们提供了一些新的思路

public class CountOfLines {
  public static void main(String[] args) {
    // for total number of lines in the File with Files.lines
    try {
    	long startTime=System.currentTimeMillis();
        long lines = Files.lines(Paths.get(new File("e://test.fa").getPath())).count();
        System.out.println("Total number of lines : " + lines);
        long endTime=System.currentTimeMillis();
        System.out.println("Total time is:"+ (endTime-startTime) );
    } catch (IOException e) {
      System.out.println("No File Found");
    }
  }
}

3 参考资料

  1. Java IO 流对象详解+使用方法
  2. Java读取文件内容的六种方法
  3. att48.txt