浅谈Java 文件 IO

对 Java 中进行 文件 IO 的方法进行总结

Created on 2017-09-24 05:15

今天在完成 算法 一书上的练习的时候,要对文件进行读写,而书上的例子是直接通过 Linux/Unix 的重定向来实现的,而我需要把它修改成直接读取文件。此外,我一直觉得Java IO 认知都是很糢糊, 因为有太多的选择(但是这也是Java 的强大之处),现在Java8 又新增了文件的API, 所以我 就对 Java 中进行文件 IO 方法作了个小结

Read

我今天的需求是要逐行读写文本文件,我就以此为例子了;测试文件是 /tmp/test.txt

test
this is a test
this is another test

BufferedReader

虽然已经有了Java8的 Stream,但是经典的东西总是历久弥新的;例如 BufferedReader 就是JDK1.1就发布了的文件读 API (对可能出现的 IOException,使用更优雅 try-with-resource 并免去编写大量手动关闭资源的模板代码的麻烦)

	public  void testBufferedReader(){
		String filePath="/tmp/test.txt";
		try(BufferedReader bufferedReader=new BufferedReader(new FileReader(filePath))){
			String line;
			while((line=bufferedReader.readLine())!=null){
				System.out.println(line);
			}
		}catch (IOException ex){
			ex.printStackTrace();
			//do something
		}

Scanner

对发布于JDK1.5的Scanner,大部份 Java 程序员都是相当熟悉的,因为总是用它来读取标准输入的数据 现在只要把从标准输入变为从文件读取数据就可以了

	public  void testScanner(){
		String filePath="/tmp/test.txt";
		try(Scanner scanner=new Scanner(new File(filePath))){
			while(scanner.hasNextLine()){
				System.out.println(scanner.nextLine());
			}
		}catch (IOException ex){
			ex.printStackTrace();
			//do something
		}
	}

BufferedReader+Stream

Files 类作为Java NIO 的一部分在 Java 7被引入,该类提供了一系列操作文件的方法 而在Java8 又引入了另外有用的特性让 Java 开发者可以更方便地操作文件。例如 lines() 方法,可以让 BufferedReader 可以把文件内容以 Stream 的形式返回;读取文件, 并把文件内容存储到 ArrayList.

	public void testBufferedReaderAndStream(){
		String filePath="/tmp/test.txt";
		List<String> list=new ArrayList<>();
		try(BufferedReader bufferedReader= Files.newBufferedReader(Paths.get(filePath))){
			list=bufferedReader.lines().collect(Collectors.toList());
		}catch (IOException ex){
			ex.printStackTrace();
			//do something
		}
	}

得益于强大的 Stream 你可以在读取文件是进行更多的操作;例如只存储含有 this 字符的行并且删除结尾的空白符

	public  void testBufferedReaderAndStream(){
		String filePath="/tmp/test.txt";
		List<String> list=new ArrayList<>();
		try(BufferedReader bufferedReader= Files.newBufferedReader(Paths.get(filePath))){
			bufferedReader.lines().filter(line->line.contains("this")).map(String::trim)
				.forEach(System.out::println);
		}catch (IOException ex){
			ex.printStackTrace();
			//do something
		}
	}

lines+Stream

也可以直接使用 lines 方法来逐行读取文本文件,只是对比 newBufferedReader + Stream,前者颗粒度更细;

	public void testlinesAndStream(){
		String filePath="/tmp/test.txt";
		List<String> list=new ArrayList<>();
		try(Stream<String> stringStream=Files.lines(Paths.get(filePath))){
			stringStream.filter(line->line.contains("test")).forEach(System.out::println);
		}catch (IOException ex ){
			ex.printStackTrace();
			//do something
		}
	}

如果你是读取不是很大的文件的时候,你可以一次就把文件都进内存; Files 已经为你 提供这样的方法

	public static  void testReadAllLines(){
		String filePath="/tmp/test.txt";
		List<String> lists= null;
		try {
			lists = Files.readAllLines(Paths.get(filePath));
		} catch (IOException e) {
			e.printStackTrace();
		}
		for (String list : lists) {
			System.out.println(list);
		}
	}

需要注意的是 try-with-resource 是不支持 readAllLines .此外大文件请慎重使用 readAllLines,因为你可能出现 OutOfMemoryException

不得不说,新加入的API的确更加优雅


Write

我就把测试文件重新写到一个新的文件,实现复制的功能,因为我的文件很小,所以我直接 把测试独的文件加载到内存

BufferedWriter

BufferedReader 对应,对文件进行写

	public void testBufferedWriter() {
		String readFilePath = "/tmp/test.txt";
		String writeFilePath = "/tmp/test1.txt";
		try {
			List<String> lines = Files.readAllLines(Paths.get(readFilePath));
			try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(writeFilePath))) {
				for (String line : lines) {
					bufferedWriter.write(line+"\n");
				}
			} catch (IOException ex) {
				ex.printStackTrace();
				//do something
			}
		} catch (IOException ex) {
			ex.printStackTrace();
			//do something
		}
	}

你也可以将 BufferedReaderFiles 结合

	public static void testBufferedWriterAndFiles() {
		String readFilePath = "/tmp/test.txt";
		String writeFilePath = "/tmp/test1.txt";
		try {
			List<String> lines = Files.readAllLines(Paths.get(readFilePath));
			try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get(writeFilePath))) {
				for (String line : lines) {
					bufferedWriter.write(line + "\n");
				}
			} catch (IOException ex) {
				ex.printStackTrace();
				//do something
			}
		} catch (IOException ex) {
			ex.printStackTrace();
			//do something
		}
	}

Files.write

使用 Files.write() 也可以写出相当优雅的代码

	public  void testFilesWrite() {
		String readFilePath = "/tmp/test.txt";
		String writeFilePath = "/tmp/test1.txt";
		try {
			List<String> lines = Files.readAllLines(Paths.get(readFilePath));
			Files.write(Paths.get(writeFilePath), lines);
		} catch (IOException ex) {
			ex.printStackTrace();
			//do something
		}
	}

这就是各种对文本文件进行读写的方法;不知道为什么,我觉得似乎写文件的方法似乎比 读文件的方法少,例如读文件有 Scanner ,而写文件似乎没有 Printer :( 不应该是匹配的么,或许我是不知道? Enjoy Java :)

参考