java解析配置文件的section_INI文件格式以及Java编码实现读取

苏承载
2023-12-01

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  =  value

myName  =  张三

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 object是一个Map(存放name=value对)或List(存放只有value的properties)

*/

public static Map readIniFile(String fileName){

Map> listResult = new HashMap<>();

Map result = new HashMap<>();

String globalSection = "global"; //Map中存储的global properties的key

File 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 currentProperties = new ArrayList<>();

boolean lineContinued = false;

String tempStr = null;

//一次读入一行(非空),直到读入null为文件结束

//先全部放到listResult中

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中,看各个list中的元素是否包含等号“=”,如果包含,则拆开并放到Map中

//整理后,把结果放进result中

for(String key : listResult.keySet()){

List tempList = listResult.get(key);

//空section不放到结果里面

if(tempList==null||tempList.size()==0){

continue;

}

if(tempList.get(0).contains("=")){ //name=value对,存放在MAP里面

Map 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 ini = readIniFile("D:/test.ini");

for(String k : ini.keySet()){

System.out.println(k + ini.get(k));

}

System.out.println(((Map)ini.get("myInfo")).get("myName"));

}

test.ini文件内容:

;我是

#注释

# global section

a=a_value

b = b_value

[section1] #section1注释

c=c_value

c1=c1_value

d=d_value0&d_value1

[list_section1] ;section2注释

list1

list2

list3.1&list3.2&list3.3

[section1] #重复的section

e = e_value=eee

f=f_value

[ myInfo ]

myName=老许

[list_section2]

url1

url2aldfjkjhlxmclk98u230jdslkmfsdlk2039840237509i09is0f980934285==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=老许}

老许

(原创文章,转载请注明转自Clement-Xu的博客)

和其他类型配置文件的比较:

xml, json, yaml文件:他们都支持嵌套定义properties,但属于重量级的配置文件,语法比较复杂。

版权声明:本文为原创文章,转载请注明转自Clement-Xu的csdn博客。

原文:http://blog.csdn.net/clementad/article/details/47172315

 类似资料: