用Java解析INI文件最简单的方法是什么?


104

我正在用Java替代传统应用程序。要求之一是必须将旧应用程序使用的ini文件原样读取到新的Java应用程序中。此ini文件的格式是常见的Windows样式,带有标题部分和键=值对,使用#作为注释字符。

我尝试使用Java中的Properties类,但是如果不同的标头之间存在名称冲突,那当然是行不通的。

因此,问题是,读取此INI文件和访问密钥的最简单方法是什么?

Answers:


121

我使用的库是ini4j。它轻巧,可以轻松解析ini文件。此外,它不使用对10,000个其他jar文件的神秘依赖,因为设计目标之一是仅使用标准Java API

这是有关如何使用库的示例:

Ini ini = new Ini(new File(filename));
java.util.prefs.Preferences prefs = new IniPreferences(ini);
System.out.println("grumpy/homePage: " + prefs.node("grumpy").get("homePage", null));

2
不起作用,错误提示“ IniFile无法解析为类型”
Caballero

@Caballero是的,看来IniFile上课了,请尝试Ini ini = new Ini(new File("/path/to/file"));
Mehdi Karamosly,

2
ini4j.sourceforge.net/tutorial/OneMinuteTutorial.java.html可能会保持最新状态,即使它们再次更改了类名。
Lokathor 2014年

这东西还能工作吗?下载了0.5.4源代码,它甚至没有构建,也不是缺少依赖项。另外,ini4j还有很多其他不需要的东西,Windoze注册表编辑...快来。#LinuxMasterRace ...但是我想如果它对您有用,请把自己踢出去。
用户

对于我编写的INI文件,我必须使用Wini该类,如“一分钟”教程中所说明;在prefs.node("something").get("val", null)没有工作我预期的方式。
Agi Hammerthief,

65

正如提到的ini4j可以用来实现这一目标。我再举一个例子。

如果我们有这样的INI文件:

[header]
key = value

以下应显示value到STDOUT:

Ini ini = new Ini(new File("/path/to/file"));
System.out.println(ini.get("header", "key"));

查看教程以获取更多示例。


2
整齐!我一直只是使用BufferedReader和一些复制/粘贴字符串解析代码而不必向我的应用程序添加另一个依赖项(当您开始为最简单的任务添加第三方API时,这种依赖关系可能会超出比例) )。但是我不能忽略这种简单性。
Gimby

30

只需80行:

package windows.prefs;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class IniFile {

   private Pattern  _section  = Pattern.compile( "\\s*\\[([^]]*)\\]\\s*" );
   private Pattern  _keyValue = Pattern.compile( "\\s*([^=]*)=(.*)" );
   private Map< String,
      Map< String,
         String >>  _entries  = new HashMap<>();

   public IniFile( String path ) throws IOException {
      load( path );
   }

   public void load( String path ) throws IOException {
      try( BufferedReader br = new BufferedReader( new FileReader( path ))) {
         String line;
         String section = null;
         while(( line = br.readLine()) != null ) {
            Matcher m = _section.matcher( line );
            if( m.matches()) {
               section = m.group( 1 ).trim();
            }
            else if( section != null ) {
               m = _keyValue.matcher( line );
               if( m.matches()) {
                  String key   = m.group( 1 ).trim();
                  String value = m.group( 2 ).trim();
                  Map< String, String > kv = _entries.get( section );
                  if( kv == null ) {
                     _entries.put( section, kv = new HashMap<>());   
                  }
                  kv.put( key, value );
               }
            }
         }
      }
   }

   public String getString( String section, String key, String defaultvalue ) {
      Map< String, String > kv = _entries.get( section );
      if( kv == null ) {
         return defaultvalue;
      }
      return kv.get( key );
   }

   public int getInt( String section, String key, int defaultvalue ) {
      Map< String, String > kv = _entries.get( section );
      if( kv == null ) {
         return defaultvalue;
      }
      return Integer.parseInt( kv.get( key ));
   }

   public float getFloat( String section, String key, float defaultvalue ) {
      Map< String, String > kv = _entries.get( section );
      if( kv == null ) {
         return defaultvalue;
      }
      return Float.parseFloat( kv.get( key ));
   }

   public double getDouble( String section, String key, double defaultvalue ) {
      Map< String, String > kv = _entries.get( section );
      if( kv == null ) {
         return defaultvalue;
      }
      return Double.parseDouble( kv.get( key ));
   }
}

+1仅用于正则表达式模式/匹配器。像魅力一样运作
kalelien

不是一个完美的解决方案,而是一个很好的起点,例如,缺少getSection()和getString()只会在缺少整个节的情况下返回defaultValue。
Jack Miller

这种regx与使用字符串实现之间的性能差异是什么?
Ewoks 2015年

读取小型配置文件时的性能无关紧要。我相信,打开和关闭文件会更加耗费精力。
航空航天

是的,这与简单用例一样简单。不知道为什么人们要使其复杂化。是的,如果您担心性能(或其他问题,例如错误报告),是的,您可能希望使用其他内容(可能完全使用另一种格式)。
用户

16

这是一个使用apache类HierarchicalINIConfiguration的简单但功能强大的示例:

HierarchicalINIConfiguration iniConfObj = new HierarchicalINIConfiguration(iniFile); 

// Get Section names in ini file     
Set setOfSections = iniConfObj.getSections();
Iterator sectionNames = setOfSections.iterator();

while(sectionNames.hasNext()){

 String sectionName = sectionNames.next().toString();

 SubnodeConfiguration sObj = iniObj.getSection(sectionName);
 Iterator it1 =   sObj.getKeys();

    while (it1.hasNext()) {
    // Get element
    Object key = it1.next();
    System.out.print("Key " + key.toString() +  " Value " +  
                     sObj.getString(key.toString()) + "\n");
}

Commons Configuration具有许多运行时依赖项。至少需要commons-langcommons-logging。根据您所使用的功能,可能需要其他库(有关详细信息,请参见前面的链接)。


1
这是我的正确答案。使用非常简单,用途广泛。
marcolopes

公共配置而不是集合。
jantox

13

或者使用标准Java API,您可以使用java.util.Properties

Properties props = new Properties();
try (FileInputStream in = new FileInputStream(path)) {
    props.load(in);
}

12
问题是,对于ini文件,该结构具有标头。Property类不知道如何处理标头,并且可能会发生名称冲突
MarioOrtegón08年

2
另外,Properties该类无法正确获取包含\的值
rds

3
+1是简单的解决方案,但仅适合简单的配置文件,正如Mario Ortegon和rds注意到的那样。
Benj

1
INI文件包含[section],属性文件包含分配。
航天

1
文件格式:使用ISO 8859-1(使用Unicode转义 + 用于其他编码)的1 / 简单的行格式或2 / 简单的XML格式或3 / 简单的行native2ascii
n611x007 2013年

10

在18行中,将java.util.Properties解析扩展为多个部分:

public static Map<String, Properties> parseINI(Reader reader) throws IOException {
    Map<String, Properties> result = new HashMap();
    new Properties() {

        private Properties section;

        @Override
        public Object put(Object key, Object value) {
            String header = (((String) key) + " " + value).trim();
            if (header.startsWith("[") && header.endsWith("]"))
                return result.put(header.substring(1, header.length() - 1), 
                        section = new Properties());
            else
                return section.put(key, value);
        }

    }.load(reader);
    return result;
}

2

另一个选择是Apache Commons Config还具有一个用于从INI文件加载的类。它确实具有一些运行时依赖性,但是对于INI文件,它仅需要Commons集合,lang和日志记录。

我在项目上使用了Commons Config及其属性和XML配置。它非常易于使用,并支持一些非常强大的功能。



2

我个人更喜欢孔夫子

很好,因为它不需要任何外部依赖关系,它很小-仅16K,并在初始化时自动加载您的ini文件。例如

Configurable config = Configuration.getInstance();  
String host = config.getStringValue("host");   
int port = config.getIntValue("port"); 
new Connection(host, port);

3年后,Mark和OP可能已经去世了……但这是一个非常不错的发现。
用户

6
我用拐杖四处走动,但仍然活着而且
踢着脚

@MarioOrtegón:很高兴听到这个消息!
ישואוהבאותך

0

at4的解决方案非常优雅和简单。它适用于所有合理的 ini文件。但是,我见过很多有在非转义空格
为了解决这个问题,我下载并修改了的副本java.util.Properties。尽管这有点不合常规,而且是短期的,但实际的mod只是几行而已,非常简单。我将向JDK社区提出一个建议,以包括这些更改。

通过添加内部类变量:

private boolean _spaceCharOn = false;

我控制与扫描键/值分离点有关的处理。我用一个小的私有方法替换了空格字符搜索代码,该方法根据上述变量的状态返回一个布尔值。

private boolean isSpaceSeparator(char c) {
    if (_spaceCharOn) {
        return (c == ' ' || c == '\t' || c == '\f');
    } else {
        return (c == '\t' || c == '\f');
    }
}

此方法在private方法内的两个地方使用load0(...)
还有一种公共方法可以将其打开,但是Properties如果空间分隔符对您的应用程序来说不是问题,则最好使用原始版本的。

如果有兴趣,我愿意将代码发布到我的 IniFile.java文件中。适用于的任何版本Properties


0

使用@Aerospace的答案,我意识到INI文件包含没有任何键值的段是合法的。在这种情况下,应该在找到任何键值之前(例如,Java 8的最小更新)对顶级映射进行添加:

            Path location = ...;
            try (BufferedReader br = new BufferedReader(new FileReader(location.toFile()))) {
                String line;
                String section = null;
                while ((line = br.readLine()) != null) {
                    Matcher m = this.section.matcher(line);
                    if (m.matches()) {
                        section = m.group(1).trim();
                        entries.computeIfAbsent(section, k -> new HashMap<>());
                    } else if (section != null) {
                        m = keyValue.matcher(line);
                        if (m.matches()) {
                            String key = m.group(1).trim();
                            String value = m.group(2).trim();
                            entries.get(section).put(key, value);
                        }
                    }
                }
            } catch (IOException ex) {
                System.err.println("Failed to read and parse INI file '" + location + "', " + ex.getMessage());
                ex.printStackTrace(System.err);
            }

-2

就这么简单.....

//import java.io.FileInputStream;
//import java.io.FileInputStream;

Properties prop = new Properties();
//c:\\myapp\\config.ini is the location of the ini file
//ini file should look like host=localhost
prop.load(new FileInputStream("c:\\myapp\\config.ini"));
String host = prop.getProperty("host");

1
这不能处理INI部分
Igor Melnichenko,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.