我正在开发一个使用自定义配置文件的个人项目。该文件的基本格式如下所示:
[users]
name: bob
attributes:
hat: brown
shirt: black
another_section:
key: value
key2: value2
name: sally
sex: female
attributes:
pants: yellow
shirt: red
可以有任意数量的用户,每个用户可以拥有不同的键/值对,并且使用制表位可以在一个节下面嵌套键/值。我知道我可以为这个配置文件使用json,yaml甚至xml,但是,我现在想保持自定义。
解析应该不难,因为我已经编写了解析它的代码。我的问题是,使用干净和结构化代码解析此问题的最佳方法是什么,以及以不会在未来进行更改的方式编写(将来可能会有多个巢穴)。现在,我的代码看起来非常恶心。例如,
private void parseDocument() {
String current;
while((current = reader.readLine()) != null) {
if(current.equals("") || current.startsWith("#")) {
continue; //comment
}
else if(current.startsWith("[users]")) {
parseUsers();
}
else if(current.startsWith("[backgrounds]")) {
parseBackgrounds();
}
}
}
private void parseUsers() {
String current;
while((current = reader.readLine()) != null) {
if(current.startsWith("attributes:")) {
while((current = reader.readLine()) != null) {
if(current.startsWith("\t")) {
//add user key/values to User object
}
else if(current.startsWith("another_section:")) {
while((current = reader.readLine()) != null) {
if(current.startsWith("\t")) {
//add user key/values to new User object
}
else if (current.equals("")) {
//newline means that a new user is up to parse next
}
}
}
}
}
else if(!current.isEmpty()) {
//
}
}
}
正如您所看到的,代码非常混乱,我在此处简化了演示文稿。我觉得有更好的方法可以做到这一点,也许不使用BufferedReader。有人可以提供一种更好的方式或方法,而不是像我的那样复杂吗?
答案 0 :(得分:6)
我建议不要为配置文件创建自定义代码。您提出的建议与YAML(getting started)相距甚远。请改用它。
答案 1 :(得分:3)
每个人都会建议使用XML,因为它更好。
但是,如果您正在寻求证明您的程序员对自己的价值......
...你所发布的代码没有任何根本性的错误,因为它很清楚,对于潜在的读者而言,显而易见的是,除非我完全脱离文件操作的循环,否则它应该执行几乎可以。
我能提出的一个批评是它不是递归的。每个级别都需要支持新级别的代码。我可能会做一个递归函数(一个用子内容作为参数调用自身的函数,然后再调用子子内容等),可以调用,将所有这些东西读入带有哈希表的哈希表中,然后我将该哈希表用作配置对象。
然后,在那一点上,我可能会停止看到这一点并使用XML。 ;)
答案 2 :(得分:2)
如果您可以使用XML或JSON或其他众所周知的数据编码作为数据格式,则解析/反序列化文本内容并提取值将更加容易。 例如。
name: bob
attributes:
hat: brown
shirt: black
another_section:
key: value
key2: value2
可以表示为以下XML(还有其他选项可以用XML表示)
<config>
<User hat="brown" shirt="black" >
<another_section>
<key>value</key>
<key2>value</key2>
</another_section>
</User>
</config>
自定义(非常简单) 正如我在下面的评论中提到的,您可以将它们全部设为名称和值对。 e.g。
name :bob
attributes_hat :brown
attributes_shirt :black
another_section_key :value
another_section_key2 :value2
然后在'\ n'(换行符)和':'上进行字符串拆分以提取键和值或构建字典/地图对象。
答案 3 :(得分:2)
状态机看起来很简单。
while((current = reader.readLine()) != null) {
if(current.startsWith("[users]"))
state = PARSE_USER;
else if(current.startsWith("[backgrounds]"))
state = PARSE_BACKGROUND;
else if (current.equals("")) {
// Store the user or background that you've been building up if you have one.
switch(state) {
case PARSE_USER:
case USER_ATTRIBUTES:
case USER_OTHER_ATTRIBUTES:
state = PARSE_USER;
break;
case PARSE_BACKGROUND:
case BACKGROUND_ATTRIBUTES:
case BACKGROUND_OTHER_ATTRIBUTES:
state = PARSE_BACKGROUND;
break;
}
} else switch(state) {
case PARSE_USER:
case USER_ATTRIBUTES:
case USER_OTHER_ATTRIBUTES:
if(current.startsWith("attributes:"))
state = USER_ATTRIBUTES;
else if(current.startsWith("another_section:"))
state = USER_OTHER_ATTRIBUTES;
else {
// Split the line into key/value and store into user
// object being built up as appropriate based on state.
}
break;
case PARSE_BACKGROUND:
case BACKGROUND_ATTRIBUTES:
case BACKGROUND_OTHER_ATTRIBUTES:
if(current.startsWith("attributes:"))
state = BACKGROUND_ATTRIBUTES;
else if(current.startsWith("another_section:"))
state = BACKGROUND_OTHER_ATTRIBUTES;
else {
// Split the line into key/value and store into background
// object being built up as appropriate based on state.
}
break;
}
}
// If you have an unstored object, store it.
答案 4 :(得分:1)
我建议将配置文件的格式更改为JSON,并使用现有的库来解析FlexJSON等JSON对象。
{
"users": [
{
"name": "bob",
"hat": "brown",
"shirt": "black",
"another_section": {
"key": "value",
"key2": "value2"
}
},
{
"name": "sally",
"sex": "female",
"another_section": {
"pants": "yellow",
"shirt": "red"
}
}
]
}
答案 5 :(得分:1)
清理它的一个好方法是使用一个表,即用Map替换你的条件。然后,您可以通过反射(简单)调用解析方法,或者创建一些实现公共接口的类(更多工作但更强大)。