web-dev-qa-db-ja.com

Javaプロパティファイルから順番に値を取得しますか?

値の順序が重要なプロパティファイルがあります。プロパティファイルを繰り返し処理し、元のファイルの順序に基づいて値を出力できるようにしたいと考えています。

ただし、Propertiesファイルはサポートされているため、間違っている場合は修正してください。挿入順序が維持されないマップイテレータは誤った順序で値を返します

これが私が使っているコードです

Enumeration names = propfile.propertyNames();
while (names.hasMoreElements()) {
    String name = (String) names.nextElement();
    //do stuff
}

独自のカスタムファイルパーサーを作成する前に、プロパティを元に戻す方法はありますか?

30
James McMahon

プロパティ名を変更できる場合は、数値またはその他の並べ替え可能なプレフィックスをプレフィックスとして付けてから、プロパティキーセットを並べ替えることができます。

6
Clint

_Java.util.Properties_を拡張し、put()keys()の両方をオーバーライドします。

_import Java.util.Collections;
import Java.util.Enumeration;
import Java.util.HashSet;
import Java.util.LinkedHashSet;
import Java.util.Properties;
import Java.util.HashMap;

public class LinkedProperties extends Properties {
    private final HashSet<Object> keys = new LinkedHashSet<Object>();

    public LinkedProperties() {
    }

    public Iterable<Object> orderedKeys() {
        return Collections.list(keys());
    }

    public Enumeration<Object> keys() {
        return Collections.<Object>enumeration(keys);
    }

    public Object put(Object key, Object value) {
        keys.add(key);
        return super.put(key, value);
    }
}
_
66

いいえ、マップは本質的に「順不同」です。

おそらくPropertiessetPropertyをオーバーライドするputの独自のサブクラスを作成できますが、おそらく実装固有のものになるでしょう... Propertiesは、不適切なカプセル化の代表的な例です。私が最後に拡張バージョンを書いたとき(約10年前!)、それは恐ろしく、Propertiesの実装の詳細に確実に敏感でした。

11
Jon Skeet

上記のDominique Laurentのソリューションは、私にとって非常に効果的です。次のメソッドオーバーライドも追加しました。

public Set<String> stringPropertyNames() {
    Set<String> set = new LinkedHashSet<String>();

    for (Object key : this.keys) {
        set.add((String)key);
    }

    return set;
}

おそらく最も効率的ではありませんが、それは私のサーブレットのライフサイクルで一度だけ実行されます。

ドミニク、ありがとう!

6
Noah

作業例:

Map<String,String> properties = getOrderedProperties(new FileInputStream(new File("./a.properties")));
properties.entrySet().forEach(System.out::println);

それのためのコード

public Map<String, String> getOrderedProperties(InputStream in) throws IOException{
    Map<String, String> mp = new LinkedHashMap<>();
    (new Properties(){
        public synchronized Object put(Object key, Object value) {
            return mp.put((String) key, (String) value);
        }
    }).load(in);
    return mp;
}

Apache Commons Configuration でうまくいくかもしれません。私はこれを自分でテストしていませんが、ソースを確認したところ、プロパティキーがAbstractFileConfigurationクラスのLinkedListによってサポートされているようです。

public Iterator getKeys()
{
    reload();
    List keyList = new LinkedList();
    enterNoReload();
    try
    {
        for (Iterator it = super.getKeys(); it.hasNext();)
        {
            keyList.add(it.next());
        }

        return keyList.iterator();
    }
    finally
    {
        exitNoReload();
    }
}
4
serg

完全を期して...

public class LinkedProperties extends Properties {

    private final LinkedHashSet<Object> keys = new LinkedHashSet<Object>();

    @Override
    public Enumeration<?> propertyNames() {
        return Collections.enumeration(keys);
    }

    @Override
    public synchronized Enumeration<Object> elements() {
        return Collections.enumeration(keys);
    }

    public Enumeration<Object> keys() {
        return Collections.enumeration(keys);
    }

    public Object put(Object key, Object value) {
        keys.add(key);
        return super.put(key, value);
    }

    @Override
    public synchronized Object remove(Object key) {
        keys.remove(key);
        return super.remove(key);
    }

    @Override
    public synchronized void clear() {
        keys.clear();
        super.clear();
    }
}

セットを定義するメソッドは挿入順序を維持しないため、セットを返すメソッドをオーバーライドする必要があるとは思わない

3
qwerty
Map<String, String> mapFile = new LinkedHashMap<String, String>();
ResourceBundle bundle = ResourceBundle.getBundle(fileName);
TreeSet<String> keySet = new TreeSet<String>(bundle.keySet());
for(String key : keySet){
    System.out.println(key+" "+bundle.getString(key));
    mapFile.put(key, bundle.getString(key));
}

これはプロパティファイルの順序を維持します

3
Oshin Talreja

私が見ているように、PropertiesHashtableにかなり縛られています。 LinkedHashMapの順に読むことをお勧めします。そのためには、単一のメソッドObject put(Object key, Object value)disregarding the Propertiesをキー/値コンテナーとしてオーバーライドするだけで済みます。

public class InOrderPropertiesLoader<T extends Map<String, String>> {

    private final T map;

    private final Properties properties = new Properties() {
        public Object put(Object key, Object value) {
            map.put((String) key, (String) value);
            return null;
        }

    };

    public InOrderPropertiesLoader(T map) {
        this.map = map;
    }

    public synchronized T load(InputStream inStream) throws IOException {
        properties.load(inStream);

        return map;
    }
}

使用法:

LinkedHashMap<String, String> props = new LinkedHashMap<>();
try (InputStream inputStream = new FileInputStream(file)) {
    new InOrderPropertiesLoader<>(props).load(inputStream);
}
2

someの回答では、ファイルから読み取られたプロパティは、ファイルに表示される順にPropertiesのインスタンスに(putの呼び出しによって)配置されると想定されています。これは一般的な動作方法ですが、そのような順序の保証はありません。

私見:プロパティクラスを単一のプロパティ行のパーサーとして使用し、最後にLinkedHashMapのようないくつかの順序付けられたコレクションに格納するよりも、ファイルを1行ずつ読み取ることをお勧めします(順序が保証されるため)。

これは次のようにして実現できます。

private LinkedHashMap<String, String> readPropertiesInOrderFrom(InputStream propertiesFileInputStream)
                                                           throws IOException {
    if (propertiesFileInputStream == null) {
      return new LinkedHashMap(0);
    }

    LinkedHashMap<String, String> orderedProperties = new LinkedHashMap<String, String>();

    final Properties properties = new Properties(); // use only as a parser
    final BufferedReader reader = new BufferedReader(new InputStreamReader(propertiesFileInputStream));

    String rawLine = reader.readLine();

    while (rawLine != null) {
      final ByteArrayInputStream lineStream = new ByteArrayInputStream(rawLine.getBytes("ISO-8859-1"));
      properties.load(lineStream); // load only one line, so there is no problem with mixing the order in which "put" method is called


      final Enumeration<?> propertyNames = properties.<String>propertyNames();

      if (propertyNames.hasMoreElements()) { // need to check because there can be empty or not parsable line for example

        final String parsedKey = (String) propertyNames.nextElement();
        final String parsedValue = properties.getProperty(parsedKey);

        orderedProperties.put(parsedKey, parsedValue);
        properties.clear(); // make sure next iteration of while loop does not access current property
      }

      rawLine = reader.readLine();
    }

    return orderedProperties;

  }

上記のメソッドは、後で閉じる必要があるInputStreamを取ることに注意してください(もちろん、引数としてファイルのみを取るように書き換えるのに問題はありません)。

2
walkeros

明確に定義された順序でプロパティファイルを読み書きできる完全な実装については、 https://github.com/etiennestuder/Java-ordered-properties を参照してください。

OrderedProperties properties = new OrderedProperties();
properties.load(new FileInputStream(new File("~/some.properties")));
2
Etienne Studer

プロパティをXMLとしてエクスポートする場合は、keySet()もオーバーライドする必要があります。

public Set<Object> keySet() { return keys; }

2

もう1つ有名なYAEOOJP(Yet Another Example Of Ordered Java Properties)をこのスレッドに追加します。 defaultプロパティをプロパティにフィードできます。

@see http://docs.Oracle.com/javase/tutorial/essential/environment/properties.html

それは私のクラスです。確かに10ではありません16すべての可能な状況に準拠していますが、今の私の限定されたばかげた目的には問題ありません。 Greater Goodが恩恵を受けることができるように、訂正のための追加のコメントはありがたいです。

import Java.util.Collections;
import Java.util.Enumeration;
import Java.util.LinkedHashSet;
import Java.util.Map;
import Java.util.Properties;
import Java.util.Set;

/**
 * Remember javadocs  >:o
 */
public class LinkedProperties extends Properties {

    protected LinkedProperties linkedDefaults;
    protected Set<Object> linkedKeys = new LinkedHashSet<>();

    public LinkedProperties() { super(); }

    public LinkedProperties(LinkedProperties defaultProps) {
        super(defaultProps); // super.defaults = defaultProps;
        this.linkedDefaults = defaultProps;
    }

    @Override
    public synchronized Enumeration<?> propertyNames() {
        return keys();
    }

    @Override
    public Enumeration<Object> keys() {
        Set<Object> allKeys = new LinkedHashSet<>();
        if (null != defaults) {
            allKeys.addAll(linkedDefaults.linkedKeys);
        }
        allKeys.addAll(this.linkedKeys);
        return Collections.enumeration(allKeys);
    }

    @Override
    public synchronized Object put(Object key, Object value) {
        linkedKeys.add(key);
        return super.put(key, value);
    }

    @Override
    public synchronized Object remove(Object key) {
        linkedKeys.remove(key);
        return super.remove(key);
    }

    @Override
    public synchronized void putAll(Map<?, ?> values) {
        for (Object key : values.keySet()) {
            linkedKeys.add(key);
        }
        super.putAll(values);
    }

    @Override
    public synchronized void clear() {
        super.clear();
        linkedKeys.clear();
    }

    private static final long serialVersionUID = 0xC00L;
}
2
Campa

最近このトピックを読んだ人のために:org.Apache.commons:commons-configuration2のクラスPropertiesConfigurationを使用してください。プロパティの順序を維持することをテストしました(内部でLinkedHashMapを使用しているため)。実行:

`
    PropertiesConfiguration properties = new PropertiesConfiguration();
    properties.read(new FileReader("/some/path));
    properties.write(new FileWriter("/some/other/path"));
`

末尾の空白と不要なエスケープのみを削除します。

0
pootek