對程序開發人員來講,文件的讀寫是很重要的一項技能。可是使人驚訝的是,儘管Java提供了一個豐富而健壯的I/O庫,進行一些基本的文件操做卻顯得很繁 瑣。不過在Java 7中已經發生了一些改變,但那些使用Java 6的就不那麼好運了。幸運的是,Guava作了一些咱們指望I/O庫作的事情,提供了一系列的工具,讓咱們可以更方便的進行I/O操做。本篇,咱們就開始來學習如何使用Guava Files進行一些I/O操做。算法
儘管Java 7作了一些改進,並解決了一些Guava的小的問題,但咱們發現Guava提供的工具在進行I/O操做時仍然很是有用。本Guava Files系列中,咱們將要學習一下內容:數組
使用Files類來執行那些基本的任務,好比:移動或複製文件,或讀取文件內容到一個字符串集合app
Closer類,提供了一種很是乾淨的方式,確保Closeable實例被正確的關閉ide
ByteSource 和 CharSource類,提供了不可變的輸入流(Input)和讀(Reader)函數
ByteSink 和 CharSink類,提供了不可變的輸出流(Output)和寫(Writer)工具
CharStreams和ByteStreams類,爲讀Readers、寫Writers、輸入流InputStreams、輸出流OutputStreams 提供了一些靜態的實用方法單元測試
BaseEncoding類,提供了編碼和解碼字節序列和ASCII字符的方法學習
文件的複製測試
Files類提供了一些有用的方法來操做File對象,對Java開發人員來講,複製一個文件到另外一個文件是件有挑戰的工做。可是在Guava裏,咱們來看怎樣經過Files類完成一樣的工做:this
@Test public void testCopyFile() throws IOException { File original = new File("D:\\test.txt"); File copy = new File("D:\\test2.txt"); Files.copy(original, copy); }
文件的移動/重命名
一樣,Java中移動文件也和複製同樣繁瑣。在Guava裏,則很是的簡單,代碼以下:
@Test public void testMoveFile() throws IOException { File original = new File("D:\\test.txt"); File newFile = new File("D:\\test2.txt"); Files.move(original, newFile); }
像字符串同樣處理文件
有些時候咱們須要操做或使用文件的字符串表示。Files類提供了一些方法,可以將文件讀取到一個字符串集合,返回文件的第一行字符串,將一個完整的文件 的內容讀入一個字符串。下面的例子,會介紹經過調用Files.readLines方法將文件讀取到一個string集合中:
@Test public void readFileIntoListOfStringsTest() throws IOException { File file = new File("D:\\test2.txt"); List<String> expectedLines = Lists.newArrayList("hello world", "this is realfighter", "www.xx566.com"); List<String> readLines = Files.readLines(file, Charsets.UTF_8); assertThat(expectedLines, is(readLines)); }
上面的例子中,咱們使用了一個單元測試來確認從簡單文件中讀取的三行內容與咱們的指望相同。每行內容中的換行符被刪除,但其餘空白的字符則保留。Files.readLines還能夠接收LineProcessor實例做爲額外的附加參數。每一行內容都參數LineProcessor.processLine方法,該方法返回一個布爾值。LineProcessor實例會持續讀取文件中的行,直到文件讀取完畢或LineProcessor.processLine方法返回false。假設,咱們有包含以下信息的一個文件,是一些書本的信息:
"Savage Tom",Being A Great Cook,Acme Publishers,ISBN- 123456,29.99,1 "Smith Jeff",Art is Fun,Acme Publishers,ISBN-456789,19.99,2 "Vandeley Art",Be an Architect,Acme Publishers,ISBN- 234567,49.99,3 "Jones Fred",History of Football,Acme Publishers,ISBN- 345678,24.99,4 "Timpton Patty",Gardening My Way,Acme Publishers,ISBN- 4567891,34.99,5
咱們想要抽取出每行數據中的書本的標題。爲了完成這項任務,咱們須要對LineProcessor接口作以下的實現:
class ToListLineProcessor implements LineProcessor<List<String>> { private static final Splitter splitter = Splitter.on(","); private List<String> bookTitles = Lists.newArrayList(); private static final int TITLE_INDEX = 1; @Override public boolean processLine(String line) throws IOException { bookTitles.add(Iterables.get(splitter.split(line), TITLE_INDEX)); return true; } @Override public List<String> getResult() { return bookTitles; } }
在這裏咱們將使用逗號分隔每行,獲取這本書的標題,是每行中的第二項,並將標題添加到一個字符串集合中。注意,咱們使用了Iterables類,使用了靜態的Iterables.get方法,來獲取書本的標題。processLine方法老是返回true,由於咱們須要獲取全部文件中的書本名,下面是對LineProcessor實例的單元測試:
@Test public void readLinesWithProcessor() throws Exception { File file = new File("D:\\test2.txt"); List<String> expectedLines = Lists.newArrayList("Being A Great Cook", "Art is Fun", "Be an Architect", "History of Football", "Gardening My Way"); List<String> readLines = Files.readLines(file, Charsets.UTF_8, new ToListLineProcessor()); assertThat(expectedLines, is(readLines)); }
在這個例子中,咱們簡單的獲取了讀取了全部的輸入,可是咱們能夠很容易的經過一些條件只獲取n行或過濾一些數據。
文件的哈希值
在Java中生成文件的哈希值彷佛須要不少的代碼操做,但在Guava中,它變得很是簡單。Files類擁有一個hash方法,使用代碼以下:
@Test public void testFilesHashing() throws Exception{ File file = new File("D:\\test2.txt"); HashCode hashCode = Files.hash(file, Hashing.md5()); System.out.println(hashCode); }
上面的例子中,爲了使用Files.hash方法,咱們提供了File對象和HashFuction實例,咱們使用了一個實現MD5算法的hash函數,而且方法返回一個HashCode對象。Hash函數將在下一個系列中介紹,敬請期待。
文件寫
當咱們使用輸入/輸出流時,咱們常常須要編寫如下幾步的代碼:
打開輸入/輸出流。
將字節讀入/讀出。
讀取完畢,確保全部的資源都在finally代碼塊中關閉。
當咱們不得不一遍遍的重複這個過程,就很容易出錯,並會使得代碼愈來愈不清晰和難以維護。Files類爲咱們提供了方便,可以很容易的 在文件的寫/追加數據或讀取文件內容到字節數組。大部分那些咱們須要特別關注的打開或關閉資源的代碼,只須要簡單的一行代碼。
文件寫和追加數據
一個簡單的文件的寫和追加數據例子,代碼以下:
@Test public void appendingWritingToFileTest() throws IOException { File file = new File("D:\\test2.txt"); file.deleteOnExit(); String hamletQuoteStart = "To be, or not to be"; Files.write(hamletQuoteStart, file, Charsets.UTF_8); assertThat(Files.toString(file, Charsets.UTF_8), is(hamletQuoteStart)); String hamletQuoteEnd = ",that is the question"; Files.append(hamletQuoteEnd, file, Charsets.UTF_8); assertThat(Files.toString(file, Charsets.UTF_8), is(hamletQuoteStart + hamletQuoteEnd)); String overwrite = "Overwriting the file"; Files.write(overwrite, file, Charsets.UTF_8); assertThat(Files.toString(file, Charsets.UTF_8), is(overwrite)); }
在這個例子中,咱們使用單元測試作了以下幾件事情:
建立一個測試的文件,並確保JVM中不存在同名的文件
咱們使用Files.write方法向文件寫入一個字符,並確保寫入成功
以後咱們使用了Files.append方法追加了另外一個字符到文件,並一樣確認文件中已經存在追加的內容
最後,咱們再次使用Files.write方法去覆蓋文件,並確保文件已被覆蓋
雖然這是一個簡單的例子,但請注意,咱們三次對文件進行寫,咱們未曾編寫任何打開或關閉資源的代碼。所以,咱們的代碼變得簡單易讀,更重要的是,不容易出現錯誤。