2008-10-10 14 views
85

Estoy escribiendo un reemplazo directo para una aplicación heredada en Java. Uno de los requisitos es que los archivos ini que utilizó la aplicación anterior se deben leer tal cual en la nueva aplicación Java. El formato de estos archivos ini es el estilo de ventana común, con secciones de encabezado y pares clave = valor, usando # como el carácter para comentar.¿Cuál es la forma más fácil de analizar un archivo INI en Java?

He intentado utilizar la clase de propiedades de Java, pero por supuesto que no funcionará si hay conflictos de nombres entre diferentes encabezados.

Entonces la pregunta es, ¿cuál sería la forma más fácil de leer en este archivo INI y acceder a las claves?

Respuesta

104

La biblioteca que he usado es ini4j. Es liviano y analiza los archivos ini con facilidad. También se utiliza sin dependencias esotéricos a otras 10.000 archivos jar, como uno de los objetivos de diseño era utilizar sólo el API estándar de Java

Este es un ejemplo de cómo se usa la biblioteca:

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)); 
+15

Enlace: http://ini4j.sourceforge.net/ – alastairs

+1

no funciona, dice error "no puede ser INIFILE resuelto a un tipo " – Caballero

+0

@Caballero sí parece que se tomó la clase' IniFile', intente 'Ini ini = new Ini (nuevo archivo ("/ruta/a/archivo "))' –

2

Otra opción es Apache Commons Config también tiene una clase para cargar desde INI files. Tiene algunos runtime dependencies, pero para los archivos INI solo debería requerir colecciones Commons, lang y logging.

He usado Commons Config en proyectos con sus propiedades y configuraciones XML. Es muy fácil de usar y admite algunas funciones bastante potentes.

11

O con la API estándar de Java puede utilizar java.util.Properties:

new Properties() props = rowProperties.load(new FileInputStream(path)); 
+9

El problema es que, con archivos ini, la estructura tiene encabezados. La clase Propiedad no sabe cómo manejar los encabezados, y podría haber conflictos de nombres –

+2

Además, la clase 'Propiedades' no obtiene correctamente los valores que contienen \ – rds

+3

+1 para la solución simple, pero solo ajusta archivos de configuración simples, como Mario Ortegon y Rds lo notaron. – Benj

15

Aquí es un simple pero potente ejemplo, mediante la clase 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 configuración tiene un número de runtime dependencies. Como mínimo, se requieren commons-lang y commons-logging. Dependiendo de lo que esté haciendo con él, es posible que necesite bibliotecas adicionales (consulte el enlace anterior para obtener más información).

+1

Esta sería mi respuesta correcta. Muy simple de usar y versátil. – marcolopes

+0

configuraciones comunes, no colecciones. – jantox

59

Como mentioned, ini4j se puede utilizar para lograr esto. Déjame mostrar otro ejemplo.

Si tenemos un archivo INI así:

[header] 
key = value 

Lo siguiente debe mostrar value a STDOUT:

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

Comprobar the tutorials para más ejemplos.

+2

¡Aseado! Siempre he estado usando BufferedReader y un poco de copiar/pegar el código de análisis de cadenas para no tener que agregar otra dependencia a mis aplicaciones (que puede perder proporciones cuando comienzas a agregar API de terceros incluso para las tareas más simples)) Pero no puedo ignorar este tipo de simplicidad. – Gimby

25

tan simple como 80 líneas:

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

+1 Simplemente para el uso del Patrón/Matcher de expresiones regulares. Funciona como un amuleto –

+0

No es una solución perfecta, pero es un buen punto de partida, por ejemplo, la falta de getSection() y getString() solo devuelve defaultValue si falta toda la sección. –

+0

¿cuál es la diferencia de rendimiento entre un regx semejante vs trabajar con la implementación de cadenas? – Ewoks

1

yo personalmente prefiero Confucious.

Es bueno, ya que no requiere ninguna dependencia externa, es pequeño, solo 16K, y carga automáticamente su archivo ini en la inicialización. P.ej.

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

3 años después, Mark y el OP probablemente hayan muerto de viejos ... pero este es un hallazgo realmente bueno. – User

+3

Uso un bastón para desplazarme, pero vivo y pateando –

3

En 19 líneas, extendiendo el java.util.Properties a analizar en varias secciones:

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("]")) 
       result.put(header.substring(1, header.length() - 1), 
         section = new Properties()); 
      else 
       section.put(key, value); 
      return null; 
     } 

    }.load(reader); 
    return result; 
} 
Cuestiones relacionadas