108

我正在为 Java 中的遗留应用程序编写一个替代品。要求之一是旧应用程序使用的 ini 文件必须按原样读取到新的 Java 应用程序中。这个ini文件的格式是常见的windows风格,有标题部分和key=value对,使用#作为注释字符。

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

所以问题是,读取这个 INI 文件并访问密钥的最简单方法是什么?

4

13 回答 13

129

我使用的库是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));
于 2008-10-10T09:22:12.433 回答
66

如前所述,ini4j可用于实现此目的让我再举一个例子。

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

[header]
key = value

以下应显示value到 STDOUT:

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

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

于 2011-01-31T14:17:02.020 回答
31

简单到 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 ));
   }
}
于 2013-03-26T13:34:21.747 回答
17

这是一个简单但功能强大的示例,使用 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。根据您使用它的目的,您可能需要其他库(有关详细信息,请参阅上一个链接)。

于 2009-01-28T07:57:43.150 回答
15

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

Properties props = new Properties();
try (FileInputStream in = new FileInputStream(path)) {
    props.load(in);
}
于 2008-10-13T08:41:40.253 回答
11

在 18 行中,将java.util.Propertiesto parse 扩展为多个部分:

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;
}
于 2016-12-11T08:59:52.607 回答
2

另一种选择是Apache Commons Config也有一个用于从INI 文件加载的类。它确实有一些运行时依赖项,但对于 INI 文件,它应该只需要 Commons 集合、语言和日志记录。

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

于 2008-10-10T23:05:09.140 回答
2

你可以试试 JINIFile。是来自 Delphi 的 TINiFile 的翻译,但对于 java

https://github.com/SubZane/JNiFile

于 2013-01-08T10:35:02.010 回答
2

我个人更喜欢孔子

很好,因为它不需要任何外部依赖,它很小——只有 16K,并且在初始化时自动加载你的 ini 文件。例如

Configurable config = Configuration.getInstance();  
String host = config.getStringValue("host");   
int port = config.getIntValue("port"); 
new Connection(host, port);
于 2013-09-19T14:43:42.690 回答
0

hoat4的解决方案非常优雅和简单。它适用于所有健全的ini 文件。但是,我看到很多在key中有未转义的空格字符。
为了解决这个问题,我下载并修改了java.util.Properties. 虽然这有点不正统,而且是短期的,但实际的模组只有几行而且非常简单。我将向 JDK 社区提出一项提案以包含这些更改。

通过添加一个内部类变量:

private boolean _spaceCharOn = false;

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

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

此方法在私有方法中的两个地方使用load0(...)
还有一个公共方法可以打开它,但Properties如果空格分隔符对您的应用程序没有问题,最好使用原始版本。

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

于 2019-12-14T04:37:22.887 回答
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);
            }

于 2020-07-19T21:39:33.827 回答
0

您可以使用ini4j将 INI 转换为 Properties

    Properties properties = new Properties();
    Ini ini = new Ini(new File("path/to/file"));
    ini.forEach((header, map) -> {
      map.forEach((subKey, value) -> {
        StringBuilder key = new StringBuilder(header);
        key.append("." + subKey);
        properties.put(key.toString(), value);
      });
    });
于 2021-07-02T03:39:14.210 回答
-4

就是这么简单......

//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");
于 2019-02-12T11:30:57.077 回答