读取 Java 属性文件而不转义值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6233532/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Reading Java Properties file without escaping values
提问by pdeva
My application needs to use a .properties file for configuration. In the properties files, users are allow to specify paths.
我的应用程序需要使用 .properties 文件进行配置。在属性文件中,允许用户指定路径。
Problem
问题
Properties files need values to be escaped, eg
属性文件需要转义值,例如
dir = c:\mydir
Needed
需要
I need some way to accept a properties file where the values are not escaped, so that the users can specify:
我需要某种方式来接受值未转义的属性文件,以便用户可以指定:
dir = c:\mydir
回答by Ian Harrigan
Why not simply extend the properties class to incorporate stripping of double forward slashes. A good feature of this will be that through the rest of your program you can still use the original Properties
class.
为什么不简单地扩展属性类以合并去除双正斜杠。这样做的一个好处是,通过程序的其余部分,您仍然可以使用原始Properties
类。
public class PropertiesEx extends Properties {
public void load(FileInputStream fis) throws IOException {
Scanner in = new Scanner(fis);
ByteArrayOutputStream out = new ByteArrayOutputStream();
while(in.hasNext()) {
out.write(in.nextLine().replace("\","\\").getBytes());
out.write("\n".getBytes());
}
InputStream is = new ByteArrayInputStream(out.toByteArray());
super.load(is);
}
}
Using the new class is a simple as:
使用新类很简单:
PropertiesEx p = new PropertiesEx();
p.load(new FileInputStream("C:\temp\demo.properties"));
p.list(System.out);
The stripping code could also be improved upon but the general principle is there.
剥离代码也可以改进,但一般原则就在那里。
回答by Michael Borgwardt
Two options:
两种选择:
- use the XML propertiesformat instead
- Writer your own parser for a modified .properties format without escapes
- 改用XML 属性格式
- 为修改后的 .properties 格式编写自己的解析器,无需转义
回答by Grekz
You can "preprocess" the file before loading the properties, for example:
您可以在加载属性之前“预处理”文件,例如:
public InputStream preprocessPropertiesFile(String myFile) throws IOException{
Scanner in = new Scanner(new FileReader(myFile));
ByteArrayOutputStream out = new ByteArrayOutputStream();
while(in.hasNext())
out.write(in.nextLine().replace("\","\\").getBytes());
return new ByteArrayInputStream(out.toByteArray());
}
And your code could look this way
你的代码可能看起来像这样
Properties properties = new Properties();
properties.load(preprocessPropertiesFile("path/myfile.properties"));
Doing this, your .properties file would look like you need, but you will have the properties values ready to use.
这样做后,您的 .properties 文件看起来就像您需要的那样,但您将拥有可供使用的属性值。
*I know there should be better ways to manipulate files, but I hope this helps.
*我知道应该有更好的方法来操作文件,但我希望这会有所帮助。
回答by Pa?lo Ebermann
The right way would be to provide your users with a property file editor (or a plugin for their favorite text editor) which allows them entering the text as pure text, and would save the file in the property file format.
正确的方法是为您的用户提供一个属性文件编辑器(或他们最喜欢的文本编辑器的插件),允许他们以纯文本形式输入文本,并将文件保存为属性文件格式。
If you don't want this, you are effectively defining a new format for the same (or a subset of the) content model as the property files have.
如果您不希望这样,您就是在为与属性文件所具有的相同(或其子集)内容模型有效地定义新格式。
Go the whole way and actually specifyyour format, and then think about a way to either
一路走下去,实际指定您的格式,然后考虑一种方法
- transform the format to the canonical one, and then use this for loading the files, or
- parse this format and populate a
Properties
object from it.
- 将格式转换为规范格式,然后使用它来加载文件,或者
- 解析此格式并
Properties
从中填充一个对象。
Both of these approaches will only work directly if you actually can control your property object's creation, otherwise you will have to store the transformed format with your application.
如果您确实可以控制属性对象的创建,这两种方法都将直接起作用,否则您将不得不将转换后的格式存储在您的应用程序中。
So, let's see how we can define this. The content model of normal property filesis simple:
那么,让我们看看我们如何定义它。普通属性文件的内容模型很简单:
- A map of string keys to string values, both allowing arbitrary Java strings.
- 字符串键到字符串值的映射,两者都允许任意 Java 字符串。
The escaping which you want to avoid serves just to allow arbitrary Java strings, and not just a subset of these.
您想要避免的转义只是为了允许任意 Java 字符串,而不仅仅是这些字符串的一个子集。
An often sufficient subset would be:
通常足够的子集是:
- A map of string keys (not containing any whitespace,
:
or=
) to string values (not containing any leading or trailing white space or line breaks).
- 字符串键(不包含任何空格
:
或=
)到字符串值(不包含任何前导或尾随空格或换行符)的映射。
In your example dir = c:\mydir
, the key would be dir
and the value c:\mydir
.
在您的示例中dir = c:\mydir
,键是dir
和值c:\mydir
。
If we want our keys and values to contain any Unicode character (other than the forbidden ones mentioned), we should use UTF-8 (or UTF-16) as the storage encoding - since we have no way to escape characters outside of the storage encoding. Otherwise, US-ASCII or ISO-8859-1 (as normal property files) or any other encoding supported by Java would be enough, but make sure to include this in your specification of the content model (and make sure to read it this way).
如果我们希望我们的键和值包含任何 Unicode 字符(除了提到的禁止字符),我们应该使用 UTF-8(或 UTF-16)作为存储编码 - 因为我们无法在存储之外转义字符编码。否则,US-ASCII 或 ISO-8859-1(作为普通属性文件)或 Java 支持的任何其他编码就足够了,但请确保将其包含在您的内容模型规范中(并确保以这种方式阅读)。
Since we restricted our content model so that all "dangerous" characters are out of the way, we can now define the file format simply as this:
由于我们限制了我们的内容模型,以便排除所有“危险”字符,我们现在可以简单地定义文件格式如下:
<simplepropertyfile> ::= (<line> <line break> )*
<line> ::= <comment> | <empty> | <key-value>
<comment> ::= <space>* "#" < any text excluding line breaks >
<key-value> ::= <space>* <key> <space>* "=" <space>* <value> <space>*
<empty> ::= <space>*
<key> ::= < any text excluding ':', '=' and whitespace >
<value> ::= < any text starting and ending not with whitespace,
not including line breaks >
<space> ::= < any whitespace, but not a line break >
<line break> ::= < one of "\n", "\r", and "\r\n" >
Every \
occurring in either key or value now is a realbackslash, not anything which escapes something else.
Thus, for transforming it into the original format, we simply need to double it, like Grekz proposed, for example in a filtering reader:
\
现在出现在键或值中的每一个都是一个真正的反斜杠,而不是任何逃脱其他东西的东西。因此,为了将其转换为原始格式,我们只需要将其翻倍,就像 Grekz 提出的那样,例如在过滤阅读器中:
public DoubleBackslashFilter extends FilterReader {
private boolean bufferedBackslash = false;
public DoubleBackslashFilter(Reader org) {
super(org);
}
public int read() {
if(bufferedBackslash) {
bufferedBackslash = false;
return '\';
}
int c = super.read();
if(c == '\')
bufferedBackslash = true;
return c;
}
public int read(char[] buf, int off, int len) {
int read = 0;
if(bufferedBackslash) {
buf[off] = '\';
read++;
off++;
len --;
bufferedBackslash = false;
}
if(len > 1) {
int step = super.read(buf, off, len/2);
for(int i = 0; i < step; i++) {
if(buf[off+i] == '\') {
// shift everything from here one one char to the right.
System.arraycopy(buf, i, buf, i+1, step - i);
// adjust parameters
step++; i++;
}
}
read += step;
}
return read;
}
}
Then we would pass this Reader to our Properties object (or save the contents to a new file).
然后我们将这个 Reader 传递给我们的 Properties 对象(或将内容保存到一个新文件)。
Instead, we could simply parse this format ourselves.
相反,我们可以自己简单地解析这种格式。
public Properties parse(Reader in) {
BufferedReader r = new BufferedReader(in);
Properties prop = new Properties();
Pattern keyValPattern = Pattern.compile("\s*=\s*");
String line;
while((line = r.readLine()) != null) {
line = line.trim(); // remove leading and trailing space
if(line.equals("") || line.startsWith("#")) {
continue; // ignore empty and comment lines
}
String[] kv = line.split(keyValPattern, 2);
// the pattern also grabs space around the separator.
if(kv.length < 2) {
// no key-value separator. TODO: Throw exception or simply ignore this line?
continue;
}
prop.setProperty(kv[0], kv[1]);
}
r.close();
return prop;
}
Again, using Properties.store()
after this, we can export it in the original format.
同样,Properties.store()
在此之后使用,我们可以将其导出为原始格式。
回答by Snicolas
Based on @Ian Harrigan, here is a complete solution to get Netbeans properties file (and other escaping properties file) right from and to ascii text-files :
基于@Ian Harrigan,这里是一个完整的解决方案,用于从 ascii 文本文件获取 Netbeans 属性文件(和其他转义属性文件):
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
/**
* This class allows to handle Netbeans properties file.
* It is based on the work of : http://stackoverflow.com/questions/6233532/reading-java-properties-file-without-escaping-values.
* It overrides both load methods in order to load a netbeans property file, taking into account the \ that
* were escaped by java properties original load methods.
* @author stephane
*/
public class NetbeansProperties extends Properties {
@Override
public synchronized void load(Reader reader) throws IOException {
BufferedReader bfr = new BufferedReader( reader );
ByteArrayOutputStream out = new ByteArrayOutputStream();
String readLine = null;
while( (readLine = bfr.readLine()) != null ) {
out.write(readLine.replace("\","\\").getBytes());
out.write("\n".getBytes());
}//while
InputStream is = new ByteArrayInputStream(out.toByteArray());
super.load(is);
}//met
@Override
public void load(InputStream is) throws IOException {
load( new InputStreamReader( is ) );
}//met
@Override
public void store(Writer writer, String comments) throws IOException {
PrintWriter out = new PrintWriter( writer );
if( comments != null ) {
out.print( '#' );
out.println( comments );
}//if
List<String> listOrderedKey = new ArrayList<String>();
listOrderedKey.addAll( this.stringPropertyNames() );
Collections.sort(listOrderedKey );
for( String key : listOrderedKey ) {
String newValue = this.getProperty(key);
out.println( key+"="+newValue );
}//for
}//met
@Override
public void store(OutputStream out, String comments) throws IOException {
store( new OutputStreamWriter(out), comments );
}//met
}//class
回答by ag112
@pdeva: one more solution
@pdeva:另一种解决方案
//Reads entire file in a String
//available in java1.5
Scanner scan = new Scanner(new File("C:/workspace/Test/src/myfile.properties"));
scan.useDelimiter("\Z");
String content = scan.next();
//Use apache StringEscapeUtils.escapeJava() method to escape java characters
ByteArrayInputStream bi=new ByteArrayInputStream(StringEscapeUtils.escapeJava(content).getBytes());
//load properties file
Properties properties = new Properties();
properties.load(bi);
回答by mindas
回答by Daniel Serodio
It's not an exact answer to your question, but a different solution that may be appropriate to your needs. In Java, you can use /
as a path separator and it'll work on both Windows, Linux, and OSX. This is specially useful for relative paths.
这不是您问题的确切答案,而是可能适合您需求的不同解决方案。在 Java 中,您可以/
用作路径分隔符,它适用于 Windows、Linux 和 OSX。这对于相对路径特别有用。
In your example, you could use:
在您的示例中,您可以使用:
dir = c:/mydir