天天看點

Guava翻譯系列之File使用guava處理檔案

讀寫檔案是一個程式員的核心能力! 令人意外的事,雖然java有非常豐富的并且強壯的i/o接口,但是卻不怎麼好用。 雖然在java7中已經有了一些改善。 但是我們還是要學一下guava的i/o相關的工具。 這一章我們要學習一下内容:

-- 使用files類處理檔案的移動和複制,或者從檔案中讀取内容到字元串中

-- closer 類 給我們提供非常簡潔幹淨的方式去確定檔案被正确關閉

-- bytesource 和 charsource 類,是inputstream和readers的不可變實作類

-- bytesink 和 charsink 類,是 outputstreams 和 writers的不可變實作類

-- charstreams 和 bytestreams 類 提供了靜态方法去分别處理 readers,writers,inputstreams,和outputstreams

-- baseencoding 類,提供方法處理 byte序列和ascii碼

files 類提供了很多非常有用的方法處理檔案對象,對于任何一個java開發者,copy檔案也算是一個比較有挑戰性的過程,但是我們看一下在guava的幫助下,怎樣友善的額copy一個檔案:

移動檔案和複制檔案一樣在java中也是非常笨重,但是使用guava就會變的比較簡單:

上面的例子中,我們将copy.txt檔案重命名為newfile.txt檔案

files類可以讀取将檔案的内容讀取到字元串數組中,并傳回檔案的第一行, 下面的例子中我們将看到怎樣将檔案讀取到字元串數組中:

在這個例子中,我們使用單元測試的方式從檔案中讀取内容并和期望讀取到的内容進行比較。 數組中字元串的分行符都已經被去除,但是其他的空白字元串都被保留。 還有另外一個版本的files.readlines方法,接受一個lineprocessor執行個體作為額外的參數,每一行都會經過lineprocessor.processline方法處理,這個方法傳回一個boolean值,當processline方法傳回false,或者檔案讀取完成,檔案處理會終止。 下面我們來看一下下面的一個csv檔案, 檔案包含的内容如下:

為了提取書的名稱,我們可以實作下面的一個lineprocessor執行個體:

這裡我們以逗号分隔讀取到的字元串,并将title放到list 中,這裡我們都是傳回true,因為我們想獲得所有的書的标題。 下面是一個單元測試確定我們上面的lineprocessor的邏輯處理是正确的:

給一個檔案産生hashcode,使用原有的java代碼的情況下,會産生很多标準化的代碼。 但是使用guava就可以很容易的給檔案産生一個hash碼.

上面的例子中,我們使用的files的hash方法,傳入file對象,和 hashfunction執行個體,這裡的hashfunction我們使用的是hashfunction的md5實作。

處理input/output streams時,我們一般會有下面的幾個步驟:

打開檔案的輸入、輸出流

從檔案中讀取位元組流

操作完成後,在finally塊中保證所有的資源關閉

當我們在代碼中一遍遍重複這樣的過程後,代碼将變得不好維護, files類提供了非常友善的方法去寫或者追加内容到檔案中。 一般情況隻需要一行代碼就可以搞定。

下面是一個追加檔案内容的例子:

上面的例子中,我們使用了一個單元測試做了如下的事情:

建立一個檔案,并且保證這個檔案如果存在的話,就将其删除

使用file.write方法寫檔案,并且保證寫入是成功的

使用file.append方法追加内容到字元串中,并且同樣保證追加的内容是成功的

使用file.write方法覆寫之前的内容,并保證之前的内容被覆寫了

雖然這是一個比較簡單的例子,但是注意到我們這裡并沒有任何打開或關閉檔案的操作,這些基本的操作已經由guava幫助我們完成了。

guava 有inputsupplier 和 outputsupplier 接口作為inputstreams/readers 和 outputstream/writers的門面。 下面章節中,我們将看到我們是怎樣從inputsuppliers和outputsuppliers中受益,使用這些接口guava會自動幫助我們open,flush,close用到的資源。

guava i/o中有source和sink分别對應為reading,writing檔案,sources,sinks不是streams readers writers 但是提供了相同的功能。 source 和 sink對象可以按照下面兩個方式使用:

--

bytesource 代表了可讀的bytes類型的資料源,典型的是我們可以從檔案中讀取byte類型的資料,下面我們從檔案中讀取一個bytesource。

這裡我們使用files.asbytesource方法建立一個bytesource。 接着我們調用read方法讀取位元組數組。 最後我們假設讀取到的bytes和調用的files.tobytearray方法得到的值是一緻的.

bytesink類代表了一個可以寫的byte source。 我們可以寫入bytes到檔案或者byte數組。 從檔案中建立一個bytesink.我們可以按照如下的方式:

和上面的讀取類似這裡就不再描述.

下面我們将學習怎樣将bytesource和bytesink整合起來使用,這樣就可以屏蔽具體的細節,隻要關注 bytesource和bytesink。

上面的例子中我們通過使用files.asbytesource 和 files.asbytesink方法 建立了 bytesource和bytesink執行個體。 然後我們調用bytesource.copyto方法 将bytes寫入到bytesink對象中。 然後驗證一下寫入的是否正确。 bytesink 也有copyto方法,接受一個outputstream将位元組寫入到目标檔案中。

bytestreams 是與inputstream和outputstream的工具類,charstreams是reader和writer的工具類, 這兩個工具類中有很多方法,我們這裡隻關注一些比較有趣的方法.

bytestreams,limit方法接受一個inputstream和一個長整形的參數,傳回一個包裝了好了的 inputstream 僅僅包含指定長度的的位元組數。下面我們看一個例子:

使用charstreams.join方法可以将多個檔案的内容一起寫入到一個檔案中去.

我們看一下上面一大段代碼的意思:

建立了4個檔案對象,其中3個位輸入檔案對象,1個為輸出檔案對象

使用 files.newreadersupplier的靜态方法建立inputsupplier執行個體

将3個inputsupplier邏輯上變成一個inputsupplier

調用files.newwritersupplier方法建立outputsupplier

最後調用files.tostring方法擷取要比較的資料

調用charstreams.copy方法将inputsupplier資料寫入到outputsupplier

最後驗證我們寫入的資料是否和想象的一樣

closer類在guava中的作用是保證所有實作了closeable接口的對象都能夠調用closer.close方法合理的關閉。 這個功能在java7中也有類似的實作 try-with-resources. 但是使用closer的方式更加直覺,具體的例子如下:

當我們處理二進制資料時,我們有時候需要把二進制資料轉換成可列印的ascii碼,我們當然也需要将已經編碼的資料轉換成原來的編碼方式,baseencoding是一個抽象類包含一些靜态工廠方法來建立不同編碼方式的執行個體,下面是一個簡單的例子:

上面的例子中,我們擷取了一個pdf檔案,并且将其用base64編碼, 我們假設所有的位元組都被編輯成了ascii碼, 然後又将獲得到的base64編碼的資料decode,baseencoding 類除了給我們簡單的encode和decode,我們還可以包裝outputsupplier bytesink,writer執行個體,這樣在寫入時,就可以使用我們指定的編碼。 一樣的我們也可以包裝inputstream,bytesource,reader執行個體,再讀取檔案時進行decode. 下面我們看一個具體的例子:

我們學習了怎樣使用inputsupplier和outputsupplier處理檔案的打開和關閉,然後我們還學習了怎樣是用bytesource,bytesink,charsource,charsink 類,最後我們學習了使用baseencoding類将二進制資料轉換為文本資料,