112

I am writing a drop-in replacement for a legacy application in Java. One of the requirements is that the ini files that the older application used have to be read as-is into the new Java Application. The format of this ini files is the common windows style, with header sections and key=value pairs, using # as the character for commenting.

I tried using the Properties class from Java, but of course that won't work if there is name clashes between different headers.

So the question is, what would be the easiest way to read in this INI file and access the keys?

13 Answers 13

135

The library I've used is ini4j. It is lightweight and parses the ini files with ease. Also it uses no esoteric dependencies to 10,000 other jar files, as one of the design goals was to use only the standard Java API

This is an example on how the library is used:

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)); 
Sign up to request clarification or add additional context in comments.

6 Comments

doesn't work, error says "IniFile cannot be resolved to a type"
@Caballero yes it seems that IniFile class was taken out, try Ini ini = new Ini(new File("/path/to/file"));
ini4j.sourceforge.net/tutorial/OneMinuteTutorial.java.html will probably stay up to date even if they change the class name again.
Is this thing even working anymore? Downloaded 0.5.4 source and it didn't even build, and it wasn't a missing dependency.. not worth the time to bother with it more. Also ini4j has a lot of other crap in there we don't need, Windoze registry editing... come on. #LinuxMasterRace ...But I guess if it works for you, knock yourself out.
Wasted my lot of time using this solution unsuccessfully. This is buggy. This project is last updated in 2015 and lot of tickets logged 3 years before are still open. For me nothing worked . I tried WINI, prefs.node and lot of other variations I tried but failed. Better to use well maintained code.
|
69

As mentioned, ini4j can be used to achieve this. Let me show one other example.

If we have an INI file like this:

[header] key = value 

The following should display value to STDOUT:

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

Check the tutorials for more examples.

1 Comment

Neat! I've always just been using BufferedReader and a bit of copy/paste String parsing code to not have to add yet another dependency to my applications (that can blow out of proportions when you start to add in third party APIs for even the simplest tasks). But I can't ignore this kind of simplicity.
34

As simple as 80 lines:

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 )); } } 

6 Comments

+1 Simply for use of regex Pattern/Matcher. Works like a charm
Not a perfect solution but a good starting point, e.g., missing getSection() and getString() only returns defaultValue if whole section is missing.
what is performance difference between such a regx vs working with string implementation?
Yep, this is about as simple as it should be for simple use cases. Not sure why people want to complicate it. If you're concerned about performance (or other concerns like error reporting), yeah, you probably want to use something else (probably another format entirely).
Don't reinvent the wheel. This is as standard problem as it ever gets a suggesting maintaining its solution in every codebase that needs to solve it instead of building and using a public library is anti-progress.
|
19

Here's a simple, yet powerful example, using the apache class 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 has a number of runtime dependencies. At a minimum, commons-lang and commons-logging are required. Depending on what you're doing with it, you may require additional libraries (see previous link for details).

4 Comments

This would be my correct answer. Very simple to use and versatile.
commons configurations not collections.
commons-configurations2 has been released. The class is now called INIConfiguration
Looks like by iniObj you meant iniConfObj
18

Or with standard Java API you can use java.util.Properties:

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

8 Comments

Problem is that, with ini files, the structure has headers. The Property class does not know how to handle the headers, and there could be name clashes
Also, the Properties class does not properly get values that contain \
+1 for the simple solution, but fits only simple config files, as Mario Ortegon and rds noticed it.
INI file contains [section], properties file contains assignments.
file format: 1/ a simple line-oriented or 2/ a simple XML format or 3/ a simple line-oriented, using ISO 8859-1 (with Unicode escapes + use native2ascii for other encodings)
|
15

In 18 lines, extending the java.util.Properties to parse into multiple sections:

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; } 

Comments

4

Another option is Apache Commons Config also has a class for loading from INI files. It does have some runtime dependencies, but for INI files it should only require Commons collections, lang, and logging.

I've used Commons Config on projects with their properties and XML configurations. It is very easy to use and supports some pretty powerful features.

Comments

2

You could try JINIFile. Is a translation of the TIniFile from Delphi, but for java

https://github.com/SubZane/JIniFile

Comments

2

I personally prefer Confucious.

It is nice, as it doesn't require any external dependencies, it's tiny - only 16K, and automatically loads your ini file on initialization. E.g.

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

3 Comments

3 years later, Mark and the OP have probably died of old age... but this is a really good find.
I use a cane to get around, but still alive and kickin'
@MarioOrtegón: Great to hear that!
0

hoat4's solution is very elegant and simple. It works for all sane ini files. However, I have seen many that have un-escaped space characters in the key.
To solve this, I have downloaded and modified a copy of java.util.Properties. Though this is a little unorthodox, and short-term, the actual mods were but a few lines and quite simple. I will be puting forward a proposal to the JDK community to include the changes.

By adding an internal class variable:

private boolean _spaceCharOn = false; 

I control the processing related to scanning for the key/value separation point. I replaced the space characters search code with a small private method that returns a boolean depending on the state of the above variable.

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

This method is used in two places within the private method load0(...).
There is also a public method to switch it on, but it would be better to use the original version of Properties if the space separator is not an issue for your application.

If there is interest, I would be willing to post the code to my IniFile.java file. It works with either version of Properties.

Comments

0

Using answer by @Aerospace, I realized that it is legitimate for INI files to have sections without any key-values. In this case, addition to the top-level map should happen before any key-values are found, ex (minimally updated for 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); } 

Comments

0

You can use ini4j to convert INI to 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); }); }); 

Comments

-4

It is just as simple as this.....

//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 Comment

This doesn't handle INI sections

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.