使用java進行文件讀寫,由於使用的頻率不高,加上寫起來也沒那麼簡單,常常容易忘記,而後就得去翻閱之前的筆記,或者找尋以前寫的文件讀寫代碼,此次決定好好的整理下這塊的知識點,並寫幾個通用的工具類,簡化文件讀寫的操做html
本篇博文將如下面幾個點做爲研究對象java
文件類型mysql
讀取方式git
編碼spring
java讀寫文件的IO流分兩大類,字節流和字符流,基類分別是字符:Reader和Writer;字節:InputStream和OutPutStreamsql
字符流分爲FileReader和FileWrtier,這兩個的父類是InputStreamReader和OutStreamWrtier數據庫
字節流分爲FileInputStream和FileOutPutStreamapache
繼承關係表json
Reader->InputStreamReader->FileReader Reader->BufferedReader Writer->OutputStreamWriter->FileWriter Writer->BufferedWriter InputStream->FileInputStream。FileInputStream 用於讀取諸如圖像數據之類的原始字節流。要讀取字符流,請考慮使用 FileReader。 InputStream->FilterInputStream->BufferedInputStream OutputStream->FileOutputStream。FileOutputStream 用於寫入諸如圖像數據之類的原始字節的流。要寫入字符流,請考慮使用 FileWriter OutputStream->FilterOutputStream->BufferedOutputStream
通常使用流程數組
File file = new File("xxx.txt");
FileReader fr = new FileReader(file);
BufferReader br = new BufferReader(fr);
String s = null; StringBuffer sb = new StringBuffer(); while((s=br.readLine()!=null) { sb.append(s); }
InputStreamReader isr = new InpuStreamReader(InputStream in);
InputStreamReader isr = new InpuStreamReader(InputStream in,Charset cs);
按字節讀取文件, 按字符讀取文件, 按行讀取文件, 隨機讀取文件
/** * 以字節爲單位讀取文件,經常使用於讀二進制文件,如圖片、聲音、影像等文件。 * * @param fileName * 文件的名 */ public static InputStream createByteRead(String fileName) throws IOException { File file = new File(fileName); return new FileInputStream(file); } /** * 以字符爲單位讀取文件,經常使用於讀文本,數字等類型的文件 * * @param fileName * 文件名 */ public static Reader createCharRead(String fileName) throws FileNotFoundException { File file = new File(fileName); return new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")); } /** * 以行爲單位讀取文件,經常使用於讀面向行的格式化文件 * * @param fileName * 文件名 */ public static BufferedReader createLineRead(String fileName) throws FileNotFoundException { File file = new File(fileName); // return new BufferedReader(new FileReader(file)); return new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"))); }
上面的方法,主要對讀字節,讀字符,讀行進行簡單包裝,隨機讀直接用RandomAccessFile
直接測試用例中給出
/** * 字節方式讀取文件 * * @throws IOException */ @Test public void testByteRead() throws IOException { String fileName = "/tmp/test.d"; InputStream in = FileUtil.createByteRead(fileName); int temp; while ((temp = in.read()) != -1) { logger.info("read bytes: {}", temp); } in.close(); // 關閉輸入流 in = FileUtil.createByteRead(fileName); byte[] tmpAry = new byte[100]; while ((temp = in.read(tmpAry)) != -1) { logger.info("read 100 bytes: {}, return: {}", tmpAry, temp); } in.close(); } @Test public void testCharRead() throws IOException { String fileName = "/tmp/test.d"; Reader reader = FileUtil.createCharRead(fileName); int temp; while ((temp = reader.read()) != -1) { logger.info("read char: {}", (char) temp); } reader.close(); reader = FileUtil.createCharRead(fileName); char[] tmpAry = new char[100]; while ((temp = reader.read(tmpAry)) != -1) { logger.info("read 100 chars: {}, return: {}", tmpAry, temp); } reader.close(); } @Test public void testLineRead() throws IOException { String fileName = "/tmp/test.d"; BufferedReader bufferedReader = FileUtil.createLineRead(fileName); String temp; while ((temp = bufferedReader.readLine()) != null) { // 會過濾掉換行符 logger.info("read line: >>>{}<<<", temp); } bufferedReader.close(); } /** * 隨機讀取文件內容 * * 文件名 */ @Test public void testRandomRead() { String fileName = "/tmp/test.d"; RandomAccessFile randomFile = null; try { System.out.println("隨機讀取一段文件內容:"); // 打開一個隨機訪問文件流,按只讀方式 randomFile = new RandomAccessFile(fileName, "r"); // 文件長度,字節數 long fileLength = randomFile.length(); // 讀文件的起始位置 int beginIndex = (fileLength > 4) ? 4 : 0; // 將讀文件的開始位置移到beginIndex位置。 randomFile.seek(beginIndex); byte[] bytes = new byte[10]; int byteread = 0; // 一次讀10個字節,若是文件內容不足10個字節,則讀剩下的字節。 // 將一次讀取的字節數賦給byteread while ((byteread = randomFile.read(bytes)) != -1) { System.out.write(bytes, 0, byteread); } } catch (IOException e) { e.printStackTrace(); } finally { if (randomFile != null) { try { randomFile.close(); } catch (IOException e1) { } } } }
test.d 文件內容以下
he你好 world
上面的輸出以下
// 按字節讀取 10:47:56.754 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 104 10:47:56.773 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 101 10:47:56.775 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 228 10:47:56.781 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 189 10:47:56.783 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 160 10:47:56.783 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 229 10:47:56.785 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 165 10:47:56.786 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 189 10:47:56.787 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 10 10:47:56.787 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 119 10:47:56.789 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 111 10:47:56.790 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 108 10:47:56.792 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 114 10:47:56.794 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 100 10:47:56.794 [main] INFO c.h.h.q.file.test.FileUtilTest - read bytes: 10 10:47:56.794 [main] INFO c.h.h.q.file.test.FileUtilTest - read 100 bytes: [104, 101, -28, -67, -96, -27, -91, -67, 10, 119, 111, 108, 114, 100, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], return: 15 // 按字符讀取 10:47:56.721 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: h 10:47:56.736 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: e 10:47:56.736 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: 你 10:47:56.737 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: 好 10:47:56.737 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: 10:47:56.737 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: w 10:47:56.738 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: o 10:47:56.739 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: l 10:47:56.743 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: r 10:47:56.747 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: d 10:47:56.747 [main] INFO c.h.h.q.file.test.FileUtilTest - read char: 10:47:56.748 [main] INFO c.h.h.q.file.test.FileUtilTest - read 100 chars: [h, e, 你, 好, , w, o, l, r, d, , , , , , , , , , , , , , , , , , , , , , , ], return: 11 // 按行讀取 10:47:56.801 [main] INFO c.h.h.q.file.test.FileUtilTest - read line: >>>he你好<<< 10:47:56.803 [main] INFO c.h.h.q.file.test.FileUtilTest - read line: >>>wolrd<<< // 隨機讀取 隨機讀取一段文件內容: �好 wolrd
小結:
從上面的三中方式能夠較明顯的感知到,使用不一樣的讀取類,獲取的方式也將不一樣
FileInputStream
二進制的讀寫InputStreamReader
字符的讀寫BufferedReader
字符讀寫,支持按行讀取FileReader 和 InputStreamReader
FileReader
繼承了InputStreamReader
,但並無實現父類中帶字符集參數的構造函數,因此使用FileReader
時,可能形成中文亂碼,而使用InputStreamReader
則能夠根據 new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"));
來強制指定編碼FileReader
是字符流,用於從文件中讀取數據,InputStreamReader
是轉換流,能夠把字節轉換成字符流的方式讀入相對路徑獲取文件, 絕對路徑獲取文件, 從網絡上獲取文件
絕對路徑讀取
File file = new File(fileName);
相對路徑讀取
InputStream stream = XXX.class.getClassLoader().getResourceAsStream(fileName);
網絡讀取
URL url = new URL(fileName); InputStream stream = url.openStream();
簡單的封裝類
public static InputStream getStreamByFileName(String fileName) throws IOException { if (fileName == null) { throw new IllegalArgumentException("fileName should not be null!"); } if (fileName.startsWith("http")) { // 網絡地址 URL url = new URL(fileName); return url.openStream(); } else if (fileName.startsWith("/")) { // 絕對路徑 Path path = Paths.get(fileName); return Files.newInputStream(path); } else { // 相對路徑 return FileUtil.class.getClassLoader().getResourceAsStream(fileName); } }
測試用例, 測試以前在相應的地方準備好圖片
@Test public void testReadFile() { try { // 絕對路徑讀取 String img = "/tmp/img.jpg"; BufferedImage bf1 = ImageIO.read(FileUtil.getStreamByFileName(img)); logger.info("read success!"); } catch (IOException e) { logger.error("read absolute img error!"); } try { // 相對路徑 String img2 = "avatar.jpg"; BufferedImage bf2 = ImageIO.read(FileUtil.getStreamByFileName(img2)); logger.info("read success!"); } catch (Exception e) { logger.error("read relative img error!"); } try { String img3 = "http://fanyi.baidu.com/static/translation/img/header/logo_cbfea26.png"; URL url = new URL(img3); BufferedImage bf3 = ImageIO.read(FileUtil.getStreamByFileName(img3)); logger.info("read success!"); } catch (Exception e) { logger.error("read net img error!"); } }
在 JS 語言中,一切都是對象。所以,任何支持的類型均可以經過 JSON 來表示,例如字符串、數字、對象、數組等。可是對象和數組是比較特殊且經常使用的兩種類型:
隨便寫了個json串,基本上包含了json中用到的各類數據格式
{ "user": { "name": [ "yihui", "一灰" ], "info": { "sex": "男", "age": 26 } }, "address": { "phone": null, "qq": true, "email": {} } }
如今有較多的json輔助工具,實現對象和JSON字符串的互轉,這裏咱們選擇fastjson做爲測試用例
依賴
<!--json--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.29</version> </dependency>
工具類
/** * 讀取json文件, 並轉爲特定對象 * @param filename * @param typeReference * @param <T> * @return * @throws IOException */ public static <T> T read(String filename, TypeReference<T> typeReference) throws IOException { BufferedReader bf = FileUtil.createLineRead(filename); try { StringBuilder stringBuffer = new StringBuilder(); String line; while ((line = bf.readLine()) != null) { stringBuffer.append(line); } return JSON.parseObject(stringBuffer.toString(), typeReference); } finally { bf.close(); } }
測試用例
@Getter @Setter @ToString private static class JsonDO { private User user; private Address address; @Getter @Setter @ToString static class User { private List<String> name; private Info info; } @Getter @Setter @ToString static class Info { private Integer age; private String sex; } @Getter @Setter @ToString static class Address { private Long phone; private Boolean qq; private Map<String, String> email; } } @Test public void testJsonRead() throws IOException { String fileName = "info.json"; TypeReference<JsonDO> typeReference = new TypeReference<JsonDO>() {}; JsonDO jsonDO = JsonUtil.read(fileName, typeReference); logger.info("the csv words: {}", jsonDO); }
輸出結果
xml文件讀寫,更常見的使用場景是按照本身的意願去選擇的獲取某些節點的值, 沒想到什麼好的方法來返回這種xml文件的數據對象,這裏就給一個簡單的使用測試case, 參考來源 : http://www.cnblogs.com/yjmyzz/archive/2012/11/11/2765312.html
主要利用 dom4j 來讀寫xml文件, 首先添加pom依賴
<dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency>
測試case以下, 下面爲待讀取的 demo.xml
<?xml version="1.0" encoding="UTF-8" ?> <address-list> <card name="yangjm" id="1"> <sex>男</sex> <address><![CDATA[中國上海外灘No.01]]> </address> <telephone>13916732212</telephone> </card> <card name="zhangsan" id="2"> <sex>女</sex> <address> <item type="家庭地址"><![CDATA[中國.北京.東直門外大街]]> </item> <item type="單位地址"><![CDATA[中國.上海.田林路888號]]> </item> </address> <telephone>010-123123</telephone> </card> </address-list>
測試代碼
@Test public void test() throws IOException { String fileName = "demo.xml"; InputStream inputStream = FileUtil.getStreamByFileName(fileName); try { SAXReader reader = new SAXReader(); Document doc = reader.read(inputStream); //加載xml文件 List peoples = doc.selectNodes("//*[@name]"); //選擇全部具備name屬性的節點(即demo.xml中的全部card節點) for (Iterator iter = peoples.iterator(); iter.hasNext(); ) { Element card = (Element) iter.next(); List attrList = card.attributes(); //輸出每一個card的全部屬性 for (Iterator attr = attrList.iterator(); attr.hasNext(); ) { Attribute a = (Attribute) attr.next(); System.out.println(a.getName() + "=" + a.getValue()); } System.out.println( "----------------------------------------------------"); } Element zhangsan = (Element) doc.selectSingleNode("//card[@id='2']"); //查找「id屬性」=2的card元素 System.out.println("張三的名稱:" + zhangsan.attribute("name").getValue()); //輸出zhangsan的name屬性 Node addrFamily = zhangsan.selectSingleNode("./address/item[2]"); //選擇zhangsan元素下的address節點下的第2個item子節點 System.out.println("張三的單位地址:" + addrFamily.getStringValue()); //輸出cdata內容 System.out.println( "----------------------------------------------------"); //爲zhangsan下增長二個節點 zhangsan.addElement("email").addAttribute("type", "工做").addText("work@some-domain.com"); zhangsan.addElement("email").addAttribute("type", "私人").addCDATA("private@some-domain.com"); //設置CDATA內容 System.out.println(zhangsan.asXML()); //打印zhangsan節點的xml內容(調試用) System.out.println( "----------------------------------------------------"); } catch (Exception e) { e.printStackTrace(); } inputStream.close(); }
多行縮進 數據結構能夠用相似大綱的縮排方式呈現,結構經過縮進來表示,連續的項目經過減號「-」來表示,map結構裏面的key/value對用冒號「:」來分隔 注意: 字串不必定要用雙引號標識; 在縮排中空白字符的數目並非很是重要,只要相同階層的元素左側對齊就能夠了(不過不能使用TAB字符); 容許在文件中加入選擇性的空行,以增長可讀性; 在一個檔案中,可同時包含多個文件,並用「——」分隔; 選擇性的符號「...」能夠用來表示檔案結尾(在利用串流的通信中,這很是有用,能夠在不關閉串流的狀況下,發送結束訊號)。
這是一種以縮進表示數據結構的表現方式,詳細說明能夠參考百科,解析方法依然使用開源的工具snakeyaml
<dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.17</version> </dependency>
測試文件 test.yaml
house: family: name: Doe parents: - John - Jane children: - Paul - Mark - Simone address: number: 34 street: Main Street city: Nowheretown zipcode: 12345
封裝實現類
/** * yaml文件讀取 * * @param fileName 文件名 * @param clz 格式化的對象實例 * @param <T> * @return * @throws IOException */ public static <T> T read(String fileName, Class<T> clz) throws IOException { try (InputStream inputStream = FileUtil.getStreamByFileName(fileName)) { Yaml yaml = new Yaml(); return yaml.loadAs(inputStream, clz); } }
測試case
@Getter @Setter @ToString public static class Me { private Integer age; private String name; private Map<String, Object> params; private List<String> favoriteBooks; public Me() { } } @Test public void testYamlRead() throws IOException { String fileName = "test.yaml"; Me me = YamlUtil.read(fileName, Me.class); logger.info("me: {}", me); }
輸出結果以下
NI文件由節、鍵、值組成。 節 [section] 參數(鍵=值) name=value 註解 註解使用分號表示(;)。在分號後面的文字,直到該行結尾都所有爲註解。
ini
文件的格式相似 properties
文件,根據這個規則進行讀寫也比較簡單,即使本身開發一個讀寫工具類,貌似也不會特別 複雜,固然也徹底沒有必要本身造輪子, 直接找個開源工具使用便可
<!--ini--> <dependency> <groupId>org.ini4j</groupId> <artifactId>ini4j</artifactId> <version>0.5.2</version> </dependency>
測試文件 test.ini
[system] program_name=ini4jExample version=1.0 [person_1] name=kanpiaoxue_1 age=30 sex=1 [person_2] name=kanpiaoxue_2 age=31 sex=1 [company] name=company1 address=beijing [company] name=company1 address=beijing
封裝代碼
/** * ini文件讀取 * @param fileName 文件名 * @return * @throws IOException */ public static Ini read(String fileName) throws IOException { InputStream inputStream = FileUtil.getStreamByFileName(fileName); try { Config cfg = new Config(); // 設置Section容許出現重複 cfg.setMultiSection(true); Ini ini = new Ini(); ini.setConfig(cfg); ini.load(inputStream); return ini; } finally { inputStream.close(); } }
測試用例
@Test public void testIniUtilRead() throws IOException { String fileName = "test.ini"; Ini ini = IniUtil.read(fileName); Profile.Section section = ini.get("system"); logger.info("section: {}", section); }
輸出結果以下

properties 文件,一般用作配置文件來使用,在spring的工程中有較普遍的應用。定義也比較簡單,每一行由
propertyName = propertyValue
這種格式組成, 其中等號前面的爲name, 等號後面的value值; # 號後面爲註釋 properties文件更適用於kv格式的配置項
一個簡單的測試case, 下面記錄的是數據庫的連接信息
driver=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/wolf?useUnicode=true&characterEncoding=UTF-8 username=root password=
這個讀取比較簡單, 能夠直接使用jdk的 Properties
類來作, 這個類繼承自 Hashtable
, 簡單的封裝代碼以下
/** * 讀取properties文件的內容 * * @param fileName 文件名 * @return * @throws IOException */ public static Properties read(String fileName) throws IOException { InputStream inputStream = FileUtil.getStreamByFileName(fileName); try { Properties pro = new Properties(); pro.load(inputStream); return pro; } finally { inputStream.close(); } }
測試用例
@Test public void testReadProperties() throws IOException { String fileName = "jdbc.properties"; Properties properties = PropertiesUtil.read(fileName); logger.info("properties: {}", properties); }
輸出結果以下

逗號分隔值(Comma-Separated Values,CSV,有時也稱爲字符分隔值,由於分隔字符也能夠不是逗號),其文件以純文本形式存儲表格數據(數字和文本)
一個簡單的實例, 一個存儲詞典的csv文件,共有2列,第一列爲單詞的id, 第二列爲單詞內; 第一行表示的頭
dicId,"name" 1,"質量" 2,"服務" 3,"發貨" 4,"性價比" 5,"尺碼"
對於csv文件的讀取, 主要借用 apache的開源工具
<!--csv 文件讀寫--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> <version>1.3</version> </dependency>
簡單的封裝類以下, 須要注意的是返回值是一個 CSVRecord
對象, 須要對其進行轉換; 第一列返回的是頭, 須要過濾掉
/** * 讀取csv文件, 返回結構話的對象 * @param filename csv 路徑 + 文件名, 支持絕對路徑 + 相對路徑 + 網絡文件 * @param headers csv 每列的數據 * @return * @throws IOException */ public static List<CSVRecord> read(String filename, String[] headers) throws IOException { Reader reader = FileUtil.createCharRead(filename); try { CSVParser csvParser = new CSVParser(reader, CSVFormat.INFORMIX_UNLOAD_CSV.withHeader(headers) ); return csvParser.getRecords(); } finally { reader.close(); } }
測試用例以下
@Getter @Setter @ToString private static class WordDO { Integer dicId; String name; } @Test public void testCsvRead() throws IOException { String fileName = "word.csv"; List<CSVRecord> list = CsvUtil.read(fileName, new String[]{"dicId", "name"}); Assert.assertTrue(list != null && list.size() > 0); List<WordDO> words = list.stream() .filter(csvRecord -> !"dicId".equals(csvRecord.get("dicId"))) .map(this::parseDO).collect(Collectors.toList()); logger.info("the csv words: {}", words); } private WordDO parseDO(CSVRecord csvRecord) { WordDO wordDO = new WordDO(); wordDO.dicId = Integer.parseInt(csvRecord.get("dicId")); wordDO.name = csvRecord.get("name"); return wordDO; }
輸出結果以下

源碼直通車: https://git.oschina.net/liuyueyi/quicksilver/tree/master/silver-file?dir=1&filepath=silver-file