Java에서 INI 파일을 구문 분석하는 가장 쉬운 방법은 무엇입니까?


104

Java에서 레거시 응용 프로그램에 대한 드롭 인 대체를 작성하고 있습니다. 요구 사항 중 하나는 이전 응용 프로그램이 사용한 ini 파일을 그대로 새 Java 응용 프로그램으로 읽어야한다는 것입니다. 이 ini 파일의 형식은 주석을위한 문자로 #을 사용하는 헤더 섹션과 키 = 값 쌍이있는 일반적인 Windows 스타일입니다.

Java의 Properties 클래스를 사용해 보았지만 물론 다른 헤더 사이에 이름 충돌이 있으면 작동하지 않습니다.

그래서 질문은이 INI 파일을 읽고 키에 액세스하는 가장 쉬운 방법은 무엇입니까?

답변:


121

내가 사용한 라이브러리는 ini4j 입니다. 가볍고 ini 파일을 쉽게 구문 분석합니다. 또한 설계 목표 중 하나가 표준 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"));
메디 Karamosly

2
ini4j.sourceforge.net/tutorial/OneMinuteTutorial.java.html 은 클래스 이름을 다시 변경하더라도 최신 상태로 유지됩니다.
Lokathor

이게 더 이상 작동합니까? 0.5.4 소스를 다운로드했고 빌드도하지 않았으며 종속성이 누락되지 않았습니다. 더 많은 시간을 할애 할 가치가 없습니다. 또한 ini4j에는 우리가 필요로하지 않는 다른 많은 쓰레기가 있습니다. Windoze 레지스트리 편집 ... #LinuxMasterRace ...하지만 그것이 당신에게 효과가 있다면 스스로를 두 드리십시오.
사용자

내가 작성한 INI 파일의 Wini경우 "1 분"튜토리얼에 설명 된대로 클래스 를 사용해야했습니다 . 는 prefs.node("something").get("val", null)내가 기대했던대로 작동하지 않았다.
Agi Hammerthief

65

마찬가지로 , ini4j는 이를 달성하기 위해 사용될 수있다. 다른 예를 하나 보여 드리겠습니다.

다음과 같은 INI 파일이있는 경우 :

[header]
key = value

다음은 valueSTDOUT에 표시되어야합니다 .

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

더 많은 예제 는 튜토리얼 을 확인하십시오 .


2
산뜻한! 저는 항상 BufferedReader와 약간의 복사 / 붙여 넣기 문자열 구문 분석 코드를 사용하여 내 애플리케이션에 아직 다른 종속성을 추가 할 필요가 없습니다 (가장 간단한 작업에도 타사 API를 추가하기 시작할 때 비율이 날아갈 수 있습니다. ). 하지만 이런 단순함을 무시할 수는 없습니다.
Gimby 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 정규식 패턴 / 매처를 사용하기위한 것입니다. 매력처럼 작동
kalelien 2013-08-22

완벽한 솔루션은 아니지만 좋은 시작점, 예를 들어 getSection () 및 getString () 누락은 전체 섹션이 누락 된 경우에만 defaultValue를 반환합니다.
Jack Miller

이러한 regx와 문자열 구현 작업의 성능 차이는 무엇입니까?
Ewoks 2015 년

작은 구성 파일을 읽을 때의 성능은 문제가되지 않습니다. 파일을 열고 닫는 것이 훨씬 더 많이 소모됩니다.
Aerospace

네, 이것은 간단한 사용 사례만큼이나 간단합니다. 사람들이 왜 그것을 복잡하게 만들고 싶어하는지 잘 모르겠습니다. 성능 (또는 오류보고와 같은 기타 문제)에 대해 염려한다면 다른 것을 사용하고 싶을 것입니다 (아마도 완전히 다른 형식).
사용자

16

다음은 아파치 클래스 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 클래스는 헤더를 처리하는 방법을 모르고 이름 충돌이있을 수 있습니다
Mario Ortegón

2
또한 Properties클래스는 \
rds

3
+1은 간단한 솔루션이지만 Mario Ortegon과 rds가 알아 차린 것처럼 단순한 구성 파일에만 적합합니다.
Benj

1
INI 파일에는 [섹션]이 포함되고 속성 파일에는 할당이 포함됩니다.
Aerospace

1
파일 형식 : 1 / 간단한 라인 지향 또는 2 / 단순 XML 형식 또는 3 / ISO 8859-1을 사용하는 단순 라인 지향 ( 유니 코드 이스케이프 + 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

개인적으로 Confucious를 선호합니다 .

외부 종속성이 필요하지 않고 매우 작기 때문에 16K에 불과하며 초기화시 자동으로 ini 파일을로드합니다. 예

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

3 년 후 Mark와 OP는 아마도 노년기로 죽었을 것입니다.하지만 이것은 정말 좋은 발견입니다.
사용자

6
나는 주위를 얻기 위해 지팡이를 사용하지만, '여전히 살아 있고 걷어차
마리오 Ortegón

@ MarioOrtegón : 그 말을 들으니 반갑습니다!
ישו אוהב אותך

0

hoat4 의 솔루션은 매우 우아하고 간단합니다. 모든 정상 ini 파일에서 작동 합니다. 그러나 에 이스케이프되지 않은 공백 문자가있는 많은 것을 보았습니다 .
이 문제를 해결하기 위해 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');
    }
}

이 메서드는 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.