Правильным способом было бы предоставить вашим пользователям редактор файлов свойств (или плагин для их любимого текстового редактора), который позволяет им вводить текст в виде чистого текста и сохранять файл в формате файлов свойств.
Если вы не хотите этого, вы фактически определяете новый формат для той же (или подмножества) модели содержимого, что и у файлов свойств.
Пройдите весь путь и фактически укажите ваш формат, а затем подумайте о способе либо
- преобразовать формат в канонический, а затем использовать его для загрузки файлов, либо
- проанализируйте этот формат и заполните из него объект
Properties
.
Оба эти подхода будут работать напрямую, только если вы действительно можете управлять созданием объекта свойства, в противном случае вам придется сохранять преобразованный формат с помощьюваше приложение.
Итак, давайте посмотрим, как мы можем определить это.Модель содержимого обычных файлов свойств проста:
- Карта строковых ключей и строковых значений, допускающих произвольные строки Java.
экранирование, которого вы хотите избежать, служит только для разрешения произвольных строк Java, а не только их подмножества.
Часто достаточным подмножеством будет:
- Карта ключей строк (не содержит пробелов,
:
или =
) для строковых значений (не содержит начальных или конечных пробелов или разрывов строк).
В вашем примере dir = c:\mydir
ключ будетdir
и значение c:\mydir
.
Если мы хотим, чтобы наши ключи и значения содержали любой символ Unicode (кроме упомянутых запрещенных), мы должны использовать UTF-8 (или UTF-16) каккодировка хранилища - поскольку у нас нет возможности экранировать символы вне кодировки хранилища.В противном случае было бы достаточно US-ASCII или ISO-8859-1 (как обычные файлы свойств) или любая другая кодировка, поддерживаемая Java, но обязательно включите это в вашу спецификацию модели содержимого (и обязательно прочитайте ее таким образом).
Поскольку мы ограничили нашу модель содержимого, чтобы исключить все «опасные» символы, теперь мы можем определить формат файла просто так:
<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 \
встречающийся в ключе или значении теперь является реальным обратным слешем, а не чем-то, что ускользает от чего-то еще.Таким образом, чтобы преобразовать его в исходный формат, нам просто нужно удвоить его, как предложил Грекз, например, в считывающем устройстве для фильтрации:
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;
}
}
Затем мы передадим этот Reader нашему объекту Properties (или сохранимсодержимое в новый файл).
Вместо этого мы могли бы просто проанализировать этот формат сами.
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;
}
Опять же, используя Properties.store()
после этого, мы можем экспортировать его в исходный формат.