按顺序从 Java 属性文件中提取值?
Posted
技术标签:
【中文标题】按顺序从 Java 属性文件中提取值?【英文标题】:Pulling values from a Java Properties file in order? 【发布时间】:2010-11-21 16:39:21 【问题描述】:我有一个属性文件,其中值的顺序很重要。我希望能够遍历属性文件并根据原始文件的顺序输出值。
但是,由于属性文件是由不维护插入顺序的 Map 支持的,如果我错了,请纠正我,迭代器以错误的顺序返回值。
这是我正在使用的代码
Enumeration names = propfile.propertyNames();
while (names.hasMoreElements())
String name = (String) names.nextElement();
//do stuff
除了编写我自己的自定义文件解析器之外,有没有办法恢复属性?
【问题讨论】:
【参考方案1】:不 - 地图本质上是“无序的”。
您可以可能创建自己的 Properties
子类,它会覆盖 setProperty
和可能的 put
,但它可能会变得非常特定于实现...... Properties
是一个主要的封装不良的例子。当我上次写一个扩展版本时(大约 10 年前!)它最终变得很糟糕,并且对 Properties
的实现细节绝对敏感。
【讨论】:
我害怕那个。我现在正在查看属性代码,我明白你的意思。支持的实现应该是一个可设置的委托。你能推荐任何替代品吗? Apache Commons 配置是否可以帮助我? 快速更正一下,Java 确实有一个 Map 的实现 LinkedHashMap,它确实保持插入顺序。 @nemo:是的,但这是专门为此设计的地图。地图一般没有顺序。我相信 Spring 有自己的属性文件阅读器,您可能会觉得它很有用。 扩展属性,覆盖 put() 并将键存储在内部列表中。使用所述列表按顺序迭代属性。 [DataMember(Name = "attribute ID", Order = 0)] private int _attributeID;我们不能在 Java 中有这样的东西吗【参考方案2】:如果您可以更改属性名称,您可以在它们前面加上数字或其他可排序的前缀,然后对 Properties KeySet 进行排序。
【讨论】:
是的,我想到了。这可能是最简单的解决方法。 可能不会给出你想要的,因为字符串可排序与混合字符串的整数可排序不匹配。如果您按字符串的自然排序值对字符串进行排序,则 11-SomePropName 将在 2-OtherPropName 之前排序。 这实际上是我最终做的。我只处理四个值,而公共配置需要太多的依赖项,这会使我的构建变得复杂。 但是此方法按字母顺序对条目进行排序并且不保证插入顺序(原始文件的顺序)【参考方案3】: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();
【讨论】:
这看起来很有趣,但是使用一个公地需要加载其他几个。我最终选择了一个快速而肮脏的解决方案。 大部分依赖是可选的。对于简单的 PropertiesConfiguration,您只需要 Commons Lang 和 Commons Collections。【参考方案4】:扩展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);
【讨论】:
将您的类创建为属性的包装器比扩展它更安全。只有重写这些方法才能假设底层类的工作方式(假设 putAll() 使用 put()),并且您可能会遇到键设置不正确的情况。 你真的应该重写 remove() 和 clear() - 否则你会在 save() 上得到 NullPointerExceptions!此外,除非用于键的集合是线程安全的,否则您应该在父方法中添加同步。 类型可以在return Collections.<Object>enumeration(keys);
中推断出来,这样就足够了:return Collections.enumeration(keys);
解释代码:诀窍是使用Java的Properties
类,重写它的put方法并将密钥放入保存插入顺序的数据结构中。这是有效的,因为Properties
将键/值按照从文件中读取它们的顺序放置 - 顶部->底部。
-1 - 您假设 put 方法是根据源文件中属性的顺序调用的。我认为你不能假设这是有保证的。如果文件是从下到上读取的,您将以相反的顺序获得属性。【参考方案5】:
如果要将属性导出为 XML,还必须覆盖 keySet():
public Set<Object> keySet()
return keys;
【讨论】:
【参考方案6】:Dominique Laurent 的上述解决方案非常适合我。我还添加了以下方法覆盖:
public Set<String> stringPropertyNames()
Set<String> set = new LinkedHashSet<String>();
for (Object key : this.keys)
set.add((String)key);
return set;
可能不是最有效的,但它在我的 servlet 生命周期中只执行一次。
谢谢多米尼克!
【讨论】:
【参考方案7】:为了完整性...
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();
我不认为返回集合的方法应该被覆盖,因为定义的集合不维护插入顺序
【讨论】:
【参考方案8】:另一种方法是使用 LinkedHashMap 编写自己的属性文件,这是我使用的:
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
public class OrderedProperties
private static Map<String, String> properties = new LinkedHashMap<String, String>();
private static OrderedProperties instance = null;
private OrderedProperties()
//The propertyFileName is read from the classpath and should be of format : key=value
public static synchronized OrderedProperties getInstance(String propertyFileName)
if (instance == null)
instance = new OrderedProperties();
readPropertiesFile(propertyFileName);
return instance;
private static void readPropertiesFile(String propertyFileName)
LineIterator lineIterator = null;
try
//read file from classpath
URL url = instance.getClass().getResource(propertyFileName);
lineIterator = FileUtils.lineIterator(new File(url.getFile()), "UTF-8");
while (lineIterator.hasNext())
String line = lineIterator.nextLine();
//Continue to parse if there are blank lines (prevents IndesOutOfBoundsException)
if (!line.trim().isEmpty())
List<String> keyValuesPairs = Arrays.asList(line.split("="));
properties.put(keyValuesPairs.get(0) , keyValuesPairs.get(1));
catch (IOException e)
e.printStackTrace();
finally
lineIterator.close();
public Map<String, String> getProperties()
return OrderedProperties.properties;
public String getProperty(String key)
return OrderedProperties.properties.get(key);
使用方法:
OrderedProperties o = OrderedProperties.getInstance("/project.properties");
System.out.println(o.getProperty("test"));
示例属性文件(在本例中为 project.properties):
test=test2
【讨论】:
这种方法的问题是,原来的Properties类在加载时支持的不仅仅是简单的例子“test=test2”。例如,数据可以有“=”,您可以对特殊字符使用转义符等。编写自己的类意味着您必须实现更多。【参考方案9】:请参阅https://github.com/etiennestuder/java-ordered-properties 了解允许以明确定义的顺序读取/写入属性文件的完整实现。
OrderedProperties properties = new OrderedProperties();
properties.load(new FileInputStream(new File("~/some.properties")));
【讨论】:
【参考方案10】:我将添加一个更著名的 YAEOOJP(有序 Java 属性的另一个示例),因为似乎没有人会关心你的 默认 属性可以喂给你的属性。
@见http://docs.oracle.com/javase/tutorial/essential/environment/properties.html
那是我的课:肯定不是 1016% 符合任何可能的情况,但这对于我现在有限的愚蠢目的来说很好。感谢任何进一步的纠正意见,以便更大的利益可以受益。
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;
【讨论】:
谢谢!!!!它解决了我的问题,以与文件中存在的顺序相同的顺序保存。【参考方案11】: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));
这会保持属性文件的顺序
【讨论】:
【参考方案12】:在我看来,Properties
与Hashtable
有很大关系。我建议阅读它以获取LinkedHashMap
。为此,您只需要重写一个方法 Object put(Object key, Object value)
,忽略 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);
【讨论】:
【参考方案13】:在 some 的答案中,假设从文件中读取的属性被放入 Properties
的实例中(通过调用 put
),以便它们出现在文件中。虽然这通常是它的行为方式,但我看不到这种顺序的任何保证。
恕我直言:最好是逐行读取文件(以保证顺序),而不是将 Properties 类用作单个属性的解析器
行,最后将其存储在一些有序的集合中,如LinkedHashMap
。
可以这样实现:
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
,之后应该关闭它(当然,将它重写为只接受一个文件作为参数是没有问题的)。
【讨论】:
【参考方案14】:工作示例:
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;
【讨论】:
【参考方案15】:对于最近阅读此主题的人: 只需使用 org.apache.commons:commons-configuration2 中的类 PropertiesConfiguration。 我已经测试过它保持属性排序(因为它在内部使用 LinkedHashMap)。 正在做:
`
PropertiesConfiguration properties = new PropertiesConfiguration();
properties.read(new FileReader("/some/path));
properties.write(new FileWriter("/some/other/path"));
`
只删除尾随空格和不必要的转义。
【讨论】:
以上是关于按顺序从 Java 属性文件中提取值?的主要内容,如果未能解决你的问题,请参考以下文章