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

参考文档:https://en.wikipedia.org/wiki/INI_file

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

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

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

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

时间: 2024-10-04 00:27:29

INI文件格式以及Java编码实现读取的相关文章

java提供的读取properties文件方法,应对编码问题

Properties prop=new Properties(); prop.load(Client.class.getClassLoader().getResourceAsStream("config.properties")); java提供的读取properties文件方法,应对编码问题

字符与编码(Java编码转换详细过程)

字符串在java内存中统一用unicode表示( 即utf-16 LE) , 不管是字符串字面量还是从文件中读取的,java使用unicode作为桥梁来处理各种编码的转换. 我们的java程序由.java的类文件组成,在这些类文件中都有可能包含中文字符串.并且我们常常需要和用户直接交互,用于输入和输出字符串.如:我们在jsp和servlet中得到客户端发送过来的字符串,这些字符串中包含中文字符.无论这些java类的作用如何,这些java程序的生命周期都是这样的: 1.编程人员在一定的操作系统上选

Java编码浅析(注意区分三个概念)(转)

Java与Unicode: Java的class文件采用utf8的编码方式,JVM运行时采用utf16. Java的字符串是unicode编码的. 总之,Java采用了unicode字符集,使之易于国际化. Java支持哪些字符集: 即Java能识别哪些字符集并对它进行正确地处理? 查看Charset 类,最新的JDK支持160种字符集.可以通过static方法availableCharsets拿到所有Java支持的字符集. Java代码   assertEquals(160, Charset.

java编码问题

工作中经常遇到java编码问题,由于缺乏研究,总是无法给出确切的答案,这个周末在网上查了一些资料,在此做些汇总. 问题一:在java中读取文件时应该采用什么编码? Java读取文件的方式总体可以分为两类:按字节读取和按字符读取.按字节读取就是采用InputStream.read()方法来读取字节,然后保存到一个byte[]数组中,最后经常用new String(byte[]);把字节数组转换成String.在最后一步隐藏了一个编码的细节,new String(byte[]);会使用操作系统默认的

Java编码问题汇总

转自 http://www.blogjava.net/zhangchao/archive/2011/05/26/351051.html Thanks Java编码问题汇总 工作中经常遇到java编码问题,由于缺乏研究,总是无法给出确切的答案,这个周末在网上查了一些资料,在此做些汇总. 问题一:在java中读取文件时应该采用什么编码? Java读取文件的方式总体可以分为两类:按字节读取和按字符读取.按字节读取就是采用InputStream.read()方法来读取字节,然后保存到一个byte[]数组

Java编码

转自:http://www.blogjava.net/zhangchao/archive/2011/05/26/351051.html Thanks Java编码问题汇总 工作中经常遇到java编码问题,由于缺乏研究,总是无法给出确切的答案,这个周末在网上查了一些资料,在此做些汇总. 问题一:在java中读取文件时应该采用什么编码? Java读取文件的方式总体可以分为两类:按字节读取和按字符读取.按字节读取就是采用InputStream.read()方法来读取字节,然后保存到一个byte[]数组

Java 编码与字符

一.字符集介绍 ANSI:American National Standards Institute.中文:美国国家标准学会 不同国家的和地区为此制定了不同标准,由此产生了 GB2312.GBK.Big5.Shift_JIS 等各自的编码标准.这些使用 1 至 4 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码.在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码:在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码. 不同 ANSI 编

java 编码 UTF-8、ISO-8859-1、GBK 【转】

Java支持UTF-8.ISO-8859-1.GBK等各种字体编码,可笔者发现Java中字体编码的问题仍难倒了不少程序员,网上虽然也有不少关于在Java中如何正确显示中文的文章,但都不够全面,笔者特意总结如下. 影响Java中字体编码正确显示的有几个因素: 1)数据库的连接方式; 2)网页中使用的字体编码; 3)数据库里存放数据的字体编码; 4)Java的缺省字体编码.如果在编程中遇到不能正确显示中文时,要先弄清楚以上几项所使用的字体编码,再分析找出原因,即可解决问题. 众所周知,JSP是Jav

【Java编码准则】の #00限制敏感数据的生命周期

当竞争对手的应用程序与我们的应用程序运行在同一个系统上时,我们的应用程序在内存中的敏感数据是很容易被竞争对手获取的.如果我们的应用程序符合下面几种情况之一,那么竞争对手可以获取到我们应用的敏感数据: 1)应用程序使用对象来存储敏感数据,而且在对象使用完后,对象的内容没有被清除或者对象没有被垃圾回收: 2)在操作系统运行内存管理任务或者执行休眠等功能时,应用程序的内存分页将被置换到磁盘上保存: 3)持有存储了操作系统缓存或者内存中敏感数据的buffer对象(例如BufferedReader): 4