標籤:ini java 格式 讀取
INI是 initialization的縮寫。INI檔案是一種輕量級的設定檔,廣泛地用於各種作業系統和軟體中。INI檔案是一種簡單的文字檔,基本結構很簡單、可讀性高,必要的元素只有兩種:section、property(包括name/key和value)。
曆史:在MS-DOS和16位Windows系統中,直到Windows ME為止,都是使用INI檔案作為作業系統設定檔(比如:win.ini, system.ini),用來配置驅動、字型、啟動項、等等等等。各種應用軟體也廣泛地採用INI檔案來儲存自己的配置資訊。
Windows NT之後,微軟開始採用和推廣註冊表來儲存配置資訊,並引導開發人員盡量使用註冊表。然而,由於註冊表不是跨作業系統可用的,所有很多應用程式還是喜歡並繼續使用INI檔案,就算有些不是以ini作為副檔名(比如conf、txt等),也是使用了類似的section、property兩種元素。
格式/元素:
Property:一般是由“=”號分隔的key(或叫name)/value對。一個property佔用一行。例子:name = valuemyName = 張三
Section:就是由若干個property的歸類和分組,一個section佔用一行,名字放在中括弧“[]”裡面。section定義後面的所有property都屬於這個section,直到下一個section出現為止。
大小寫:在windows中,大小寫是不敏感的。
注釋:windows中的注釋是以分號“;”開始的文字(Linux用井號“#”)
除了以上的標準定義之外,一些應用程式還支援和補充了其他擴充的格式:
空行:某些程式不允許有空行;
注釋:有些程式支援使用井號“#”做注釋的開頭;有些程式不允許注釋和section、property混在一行中;
重名:如有重名的property,有些程式取第一個,有些取最後一個,(section重名的話無所謂,一般就是合并他們的properties);
轉義符:有些程式支援轉義符,特別是反斜線“\”在行末作為兩行的串連符;
Global properties:有些程式支援在第一個section標籤之前可以有properties,並把它們歸類為“global” section;
空格:大多數程式支援處理name/value前後的空格,以便文字對齊增強可讀性;
順序:絕大多數程式是不管section和property出現的順序的;
和其他類型設定檔的比較:xml, json, yaml檔案:他們都支援嵌套定義properties,但屬於重量級的設定檔,文法比較複雜。
Java編碼實現讀取:實現的功能:* 讀取 INI 檔案,存放到Map中
*
* 支援以‘#’或‘;’開頭的注釋;
* 支援行串連符(行末的‘\‘標記);
* 支援預設的global properties;
* 支援list格式(非name=value格式處理為list格式);
* 支援空行、name/value前後的空格;
* 如果有重名,取最後一個;
代碼詳情:
/** * 去除ini檔案中的注釋,以";"或"#"開頭,順便去除UTF-8等檔案的BOM頭 * @param source * @return */private static String removeIniComments(String source){String result = source;if(result.contains(";")){result = result.substring(0, result.indexOf(";"));}if(result.contains("#")){result = result.substring(0, result.indexOf("#"));}//去除UTF-8的BOM!!!用Windows中的編輯器儲存UTF-8檔案,在檔案的第一個字元就是這個!!!if(result.startsWith("\uFEFF")){ //result = result.substring(1);result = result.replace("\uFEFF", "");}return result.trim();}/** * 讀取 INI 檔案,存放到Map中 * * 支援以‘#’或‘;’開頭的注釋; * 支援行串連符(行末的'\'標記); * 支援預設的global properties; * 支援list格式(非name=value格式處理為list格式); * 支援空行、name/value前後的空格; * 如果有重名,取最後一個; * * 格式(例子)如下 * * # 我是注釋 * ; 我也是注釋 * * name0=value0 # 我是global properties * name10=value10 * * [normal section] # 我是普通的section * name1=value1 # 我是name和value * * [list section] # 我是只有value的section,以第一個是否包含'='為判斷標準 * value1 * value2 * * @param fileName * @return Map<sectionName, object> object是一個Map(存放name=value對)或List(存放只有value的properties) */public static Map<String, Object> readIniFile(String fileName){Map<String, List<String>> listResult = new HashMap<>();Map<String, Object> result = new HashMap<>();String globalSection = "global"; //Map中儲存的global properties的keyFile file = new File(fileName); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(file)); //reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),"windows-1256")); String str = null; String currentSection = globalSection; //處理預設的section List<String> currentProperties = new ArrayList<>(); boolean lineContinued = false; String tempStr = null; //一次讀入一行(非空),直到讀入null為檔案結束 //先全部放到listResult<String, List>中 while ((str = reader.readLine()) != null) { str = removeIniComments(str).trim(); //去掉尾部的注釋、去掉首尾空格 if("".equals(str)||str==null){ continue; } //如果前一行包括了串連符'\' if(lineContinued == true){ str = tempStr + str; } //處理行串連符'\' if(str.endsWith("\\")){ lineContinued = true; tempStr = str.substring(0,str.length()-1); continue; }else { lineContinued = false;} //是否一個新section開始了 if(str.startsWith("[") && str.endsWith("]")){ String newSection = str.substring(1, str.length()-1).trim(); //如果新section不是現在的section,則把當前section存進listResult中 if(!currentSection.equals(newSection)){ listResult.put(currentSection, currentProperties); currentSection = newSection; //新section是否重複的section //如果是,則使用原來的list來存放properties //如果不是,則new一個List來存放properties currentProperties=listResult.get(currentSection); if(currentProperties==null){ currentProperties = new ArrayList<>(); } } }else{ currentProperties.add(str); } } //把最後一個section存進listResult中 listResult.put(currentSection, currentProperties); reader.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e1) { } } } //整理拆開name=value對,並存放到MAP中: //從listResult<String, List>中,看各個list中的元素是否包含等號“=”,如果包含,則拆開並放到Map中 //整理後,把結果放進result<String, Object>中 for(String key : listResult.keySet()){ List<String> tempList = listResult.get(key); //空section不放到結果裡面 if(tempList==null||tempList.size()==0){ continue; } if(tempList.get(0).contains("=")){ //name=value對,存放在MAP裡面 Map<String, String> properties = new HashMap<>(); for(String s : tempList){ int delimiterPos = s.indexOf("="); //處理等號前後的空格 properties.put(s.substring(0,delimiterPos).trim(), s.substring(delimiterPos+1, s.length()).trim()); } result.put(key, properties); }else{ //只有value,則擷取原來的list result.put(key, listResult.get(key)); } } return result;}@SuppressWarnings("unchecked")public static void main(String[] args) {Map<String, Object> ini = readIniFile("D:/test.ini");for(String k : ini.keySet()){System.out.println(k + ini.get(k));}System.out.println(((Map<String, String>)ini.get("myInfo")).get("myName"));}
test.ini檔案內容:
;我是#注釋# global sectiona=a_valueb = b_value[section1] #section1注釋c=c_valuec1=c1_valued=d_value0&d_value1[list_section1] ;section2注釋list1list2list3.1&list3.2&list3.3[section1] #重複的sectione = e_value=eeef=f_value[ myInfo ]myName=老許[list_section2]url1url2aldfjkjhlxmclk98u230jdslkmfsdlk2039840237509i09is0f980934285==234u093
測試結果:
list_section2[url1, url2aldfjkjhlxmclk98u230jdslkmfsdlk2039840237509i09is0f980934285==234u093]global{b=b_value, a=a_value}list_section1[list1, list2, list3.1&list3.2&list3.3]section1{f=f_value, d=d_value0&d_value1, e=e_value=eee, c1=c1_value, c=c_value}myInfo{myName=老許}老許
參考文檔:https://en.wikipedia.org/wiki/INI_file
(原創文章,轉載請註明轉自Clement-Xu的部落格)
和其他類型設定檔的比較:xml, json, yaml檔案:他們都支援嵌套定義properties,但屬於重量級的設定檔,文法比較複雜。
著作權聲明:本文為原創文章,轉載請註明轉自Clement-Xu的csdn部落格。
INI檔案格式以及Java編碼實現讀取