JavaでINIファイルを解析する最も簡単な方法は何ですか?


104

Javaでレガシーアプリケーションのドロップイン置換を書いています。要件の1つは、古いアプリケーションが使用していたiniファイルをそのまま新しいJavaアプリケーションに読み込む必要があることです。このiniファイルの形式は、コメント用の文字として#を使用して、ヘッダーセクションとキー=値のペアを持つ一般的なWindowsスタイルです。

JavaのPropertiesクラスを使用してみましたが、もちろん、異なるヘッダー間で名前の衝突がある場合は機能しません。

したがって、問題は、このINIファイルを読み取ってキーにアクセスする最も簡単な方法は何でしょうか。

回答:


121

私が使用したライブラリはini4jです。軽量で、iniファイルを簡単に解析します。また、設計目標の1つは標準のJava APIのみを使用することであったため、他の10,000個のjarファイルへの難解な依存関係は使用していません。

これは、ライブラリの使用例です。

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 2013年

2
ini4j.sourceforge.net/tutorial/OneMinuteTutorial.java.htmlは、クラス名が再度変更されても、おそらく最新の状態に保たれます。
ロカソール14

これはもう機能していますか?0.5.4のソースをダウンロードしましたが、ビルドもできませんでした。依存関係が欠けていることもありませんでした。また、ini4jには必要のない他の多くのがらくたがあります。Windozeレジストリ編集...さあ。#LinuxMasterRace ...でもうまくいくかどうかはわかりません。
ユーザー

私が書いたINIファイルについてはWini、「One Minute」チュートリアルに示されているように、クラスを使用する必要がありました。prefs.node("something").get("val", null)私が予想ように動作しませんでした。
Agi Hammerthief 2018年

65

以下のように述べたini4jは、これを達成するために使用することができます。もう1つの例を示します。

次のようなINIファイルがある場合:

[header]
key = value

以下がvalueSTDOUTに表示されます。

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

その他の例については、チュートリアルを確認してください


2
きちんと!私は常にBufferedReaderと少しのコピー/貼り付け文字列解析コードを使用して、アプリケーションにさらに別の依存関係を追加する必要がないようにしています(これは、最も単純なタスクでさえサードパーティのAPIを追加し始めると、比率を吹き飛ばす可能性があります) )。しかし、このような単純さは無視できません。
ジンビー2013年

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単に正規表現のパターン/マッチングを使用するため。魅力的な作品
カリリーン2013

完全なソリューションではありませんが、出発点としては適切です。たとえば、getSection()とgetString()が欠落している場合、セクション全体が欠落している場合にのみdefaultValueが返されます。
ジャックミラー

そのようなregxと文字列実装の操作のパフォーマンスの違いは何ですか?
Ewoks

小さな構成ファイルを読み取るときのパフォーマンスは問題ではありません。ファイルを開いたり閉じたりする方がはるかに負荷がかかると思います。
Aerospace

はい、これは単純なユースケースの場合と同じくらい簡単です。なぜ人々がそれを複雑にしたいのかわからない。パフォーマンス(またはエラー報告などのその他の懸念)が気になる場合は、ええ、おそらく何か他のもの(おそらく別のフォーマット)を使用したいと思うでしょう。
ユーザー

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 2012

13

または、標準のJava APIでは、java.util.Propertiesを使用できます。

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

12
問題は、iniファイルの場合、構造にヘッダーがあることです。Propertyクラスはヘッダーの処理方法を認識していないため、名前の衝突が発生する可能性があります
MarioOrtegón2008年

2
また、Propertiesクラスは\を含む値を適切に取得しません
rds

3
+1はシンプルなソリューションですが、Mario Ortegonとrdsが気づいたように、シンプルな設定ファイルのみに適合します。
ベンジ

1
INIファイルには[セクション]が含まれ、プロパティファイルには割り当てが含まれます。
航空宇宙

1
ファイル形式:1 / シンプルなライン指向または2 / シンプルなXMLフォーマットまたは3 / ISO 8859-1を使用したシンプルなライン指向Unicodeエスケープ + native2ascii他のエンコーディングに使用)
n611x007

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 ConfigINIファイルからロードするためのクラスも持っていることです実行時の依存関係はありますが、INIファイルの場合、Commonsコレクション、lang、およびロギングのみが必要です。

プロパティとXML構成を持つプロジェクトでCommons Configを使用しました。それは非常に使いやすく、いくつかのかなり強力な機能をサポートしています。



2

私は個人的にはConfuciousを好みます

外部依存関係を必要とせず、わずか16Kであり、初期化時にiniファイルを自動的にロードするので、これはすばらしいことです。例えば

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

3年後、マークとOPはおそらく古い年齢で亡くなりました...しかし、これは本当に良い発見です。
ユーザー

6
私は
歩き回るのに

@MarioOrtegón:それを聞いてよかった!
ישואוהבאותך

0

hoat4のソリューションは非常にエレガントでシンプルです。それはすべての正気な 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');
    }
}

このメソッドは、プライベートメソッド内の2つの場所で使用されload0(...)ます。
スイッチをオンにするパブリックメソッドもありますがProperties、スペースセパレーターがアプリケーションの問題でない場合は、の元のバージョンを使用することをお勧めします。

興味があれば、私は自分のIniFile.javaファイルにコードを投稿してもかまいません。どちらのバージョンでも動作しますProperties


0

@Aerospaceの回答を使用して、INIファイルにKey-Valueのないセクションがあることは正当であることを理解しました。この場合、トップレベルのマップへの追加は、キー値が見つかる前に行う必要があります。

            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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.