Java'da bir INI dosyasını ayrıştırmanın en kolay yolu nedir?


104

Java'daki eski bir uygulamanın yerini alacak bir eklenti yazıyorum. Gereksinimlerden biri, eski uygulamanın kullandığı ini dosyalarının yeni Java Uygulamasına olduğu gibi okunması gerektiğidir. Bu ini dosyalarının formatı, başlık bölümleri ve anahtar = değer çiftleri içeren, yorumlama için karakter olarak # kullanan ortak pencere stilidir.

Java'daki Özellikler sınıfını kullanmayı denedim, ancak farklı başlıklar arasında ad çatışmaları varsa elbette işe yaramayacak.

Öyleyse soru şu, bu INI dosyasını okumanın ve anahtarlara erişmenin en kolay yolu nedir?

Yanıtlar:


121

Kullandığım kütüphane ini4j . Hafiftir ve ini dosyalarını kolaylıkla ayrıştırır. Ayrıca, tasarım hedeflerinden biri yalnızca standart Java API'sini kullanmak olduğu için 10.000 başka jar dosyasına ezoterik bağımlılık kullanmaz.

Bu, kütüphanenin nasıl kullanıldığına dair bir örnektir:

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
çalışmıyor, hata "IniFile bir türe çözümlenemiyor" diyor
Caballero

@Caballero evet öyle görünüyor ki IniFileders çıkarıldı, deneIni ini = new Ini(new File("/path/to/file"));
Mehdi Karamosly

2
ini4j.sourceforge.net/tutorial/OneMinuteTutorial.java.html , sınıf adını tekrar değiştirseler bile muhtemelen güncel kalacaktır.
Lokathor

Bu şey artık çalışıyor mu? 0.5.4 kaynak indirildi ve oluşturulmadı bile ve eksik bir bağımlılık değildi .. daha fazla uğraşmak için zamana değmez. Ayrıca ini4j'de ihtiyacımız olmayan bir sürü şey var, Windoze kayıt defteri düzenleme ... hadi. #LinuxMasterRace ... Ama sanırım sizin için işe yararsa, kendinizi yok edin.
Kullanıcı

Yazdığım INI dosyası için Wini, "One Minute" eğitiminde gösterildiği gibi sınıfı kullanmam gerekiyordu ; prefs.node("something").get("val", null)Beklediğim şekilde çalışmadı.
Agi Hammerthief

65

Gibi söz , ini4j bunu başarmak için kullanılabilir. Başka bir örnek göstermeme izin verin.

Bunun gibi bir INI dosyamız varsa:

[header]
key = value

Aşağıdakiler valueSTDOUT göstermelidir:

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

Kontrol öğreticiler daha fazla örnek için.


2
Düzgün! Uygulamalarıma başka bir bağımlılık eklemek zorunda kalmamak için her zaman BufferedReader'ı ve biraz kopyala / yapıştır String ayrıştırma kodu kullanıyorum (en basit görevler için bile üçüncü taraf API'leri eklemeye başladığınızda orantısız bir şekilde uçabilir ). Ancak bu tür bir basitliği görmezden gelemem.
Gimby

30

80 satır kadar basit:

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 Sadece regex Kalıbı / Eşleştirici kullanımı içindir. Bir cazibe gibi çalışıyor
kalelien

Mükemmel bir çözüm değil ama iyi bir başlangıç ​​noktası, örneğin, eksik getSection () ve getString (), yalnızca tüm bölüm eksikse defaultValue döndürür.
Jack Miller

Böyle bir regx ile dize uygulamasıyla çalışmak arasındaki performans farkı nedir?
Ewoks

Küçük bir yapılandırma dosyasını okurken performans önemli değildir. Bir dosyayı açmak ve kapatmak çok daha fazla tüketici olduğuna inanıyorum.
Aerospace

Evet, bu, basit kullanım durumları için olması gerektiği kadar basit. İnsanların bunu neden karmaşıklaştırmak istediğinden emin değilim. Performans konusunda endişeleriniz varsa (veya hata raporlama gibi diğer endişeleriniz varsa), evet, muhtemelen başka bir şey kullanmak istersiniz (muhtemelen tamamen başka bir format).
Kullanıcı

16

Apache sınıfı HierarchicalINIConfiguration kullanılarak basit ama güçlü bir örnek :

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'ın bir dizi çalışma zamanı bağımlılığı vardır . En azından, ortak dil ve ortak günlük kaydı gereklidir. Onunla ne yaptığınıza bağlı olarak, ek kitaplıklara ihtiyacınız olabilir (ayrıntılar için önceki bağlantıya bakın).


1
Bu benim doğru cevabım olacaktır. Kullanımı çok basit ve çok yönlü.
marcolopes

koleksiyonlar değil ortak konfigürasyonlar.
jantox

13

Veya standart Java API ile java.util.Properties'i kullanabilirsiniz :

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

12
Sorun şu ki, ini dosyalarında yapının başlıkları var. Property sınıfı, başlıkları nasıl kullanacağını bilmiyor ve isim çatışmaları olabilir
Mario Ortegón

2
Ayrıca, Propertiessınıf düzgün \ içeren değerler almaz
RDS

3
Basit çözüm için +1, ancak Mario Ortegon ve rds'nin fark ettiği gibi yalnızca basit yapılandırma dosyalarına uyar.
Benj

1
INI dosyası [bölüm] içerir, özellikler dosyası atamaları içerir.
Uzay


10

18 satırda, java.util.Propertiesbirden çok bölüme ayrıştırmak için genişletilir :

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

Şahsen Confucious'u tercih ederim .

Herhangi bir harici bağımlılık gerektirmediği için güzel, çok küçük - yalnızca 16K ve başlatıldığında ini dosyanızı otomatik olarak yükler. Örneğin

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

3 yıl sonra, Mark ve OP muhtemelen yaşlılıktan öldüler ... ama bu gerçekten iyi bir keşif.
Kullanıcı

6
Etrafa almak için bir baston kullanır, ancak hala hayatta ve diri
Mario Ortegon

@ MarioOrtegón: Bunu duymak harika!
ישו אוהב אותך

0

hoat4'ün çözümü çok şık ve basit. Tüm aklı başında ini dosyaları için çalışır . Bununla birlikte, anahtarda kaçışı olmayan boşluk karakterleri olan birçok kişi gördüm .
Bunu çözmek için bir kopyasını indirdim ve değiştirdim java.util.Properties. Bu biraz alışılmışın dışında ve kısa vadeli olsa da, gerçek modlar sadece birkaç satırdı ve oldukça basitti. JDK topluluğuna değişiklikleri dahil etmek için bir teklif sunacağım.

Dahili bir sınıf değişkeni ekleyerek:

private boolean _spaceCharOn = false;

Anahtar / değer ayırma noktası için tarama ile ilgili işlemleri kontrol ediyorum. Boşluk karakterleri arama kodunu, yukarıdaki değişkenin durumuna bağlı olarak bir boole döndüren küçük bir özel yöntemle değiştirdim.

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

Bu yöntem özel yöntem içinde iki yerde kullanılmaktadır load0(...).
Bunu açmak için genel bir yöntem de vardır, ancak Propertiesalan ayırıcı uygulamanız için bir sorun değilse orijinal sürümünü kullanmak daha iyi olacaktır .

İlgi varsa, kodu dosyama göndermeye istekli olurum IniFile.java. Her iki sürümüyle de çalışır Properties.


0

@Aerospace tarafından verilen yanıtı kullanarak, INI dosyalarının herhangi bir anahtar-değer içermeyen bölümlere sahip olmasının meşru olduğunu anladım. Bu durumda, herhangi bir anahtar / değer çifti bulunmadan önce üst düzey haritaya ekleme yapılmalıdır (ör. Java 8 için minimum düzeyde güncellenmiştir):

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

-1

Bu kadar basit .....

//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
Bu INI bölümlerini işlemez
Igor Melnichenko
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.