解析这个配置文件的最好方法是什么?
我正在使用自定义配置文件的个人项目。该文件的基本格式看起来像这样:解析这个配置文件的最好方法是什么?
[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作为这个配置文件,但是,我想现在保留它的自定义。
解析应该不难,因为我已经编写了代码来解析它。我的问题是,使用clean和结构化解析此代码以及以不会使未来变化变得困难的方式进行解析的最佳方法是什么(未来可能会有多种巢) 。现在,我的代码看起来非常恶心。例如,
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。有人可能会提供一个更好的方法或方法,而不是像我一样错综复杂吗?
每个人都会推荐使用XML,因为它只是更好。
但是,如果你是一个追求来证明你的程序员值得自己......
...有没有什么根本性的错误,你在这个意义上发布的代码,它是明确的,它是对于潜在读者来说显而易见的是,除非我完全摆脱了文件操作的循环,否则它应该尽可能地表现出来。
我可以提供的一个批评是它不是递归的。每个级别都需要新的代码级别来支持。我可能会做一个递归函数(一个函数调用自己的子内容作为参数,然后再次如果有子子内容等),这可以被称为,阅读所有这些东西到哈希表与哈希表或什么,然后我会使用该散列表作为配置对象。
然后再次,在那一点上,我可能会停止看到这一点,并使用XML。 ;)
感谢您的回答Helgi。引用,“但是,如果你想要证明自己的程序员的价值......”在这种情况下非常适用。我会考虑你的建议,让它更加递归。到目前为止,你的文章对我来说是最有帮助的,但是在我做出这个答案之前,我会稍微等一会儿;) – trinth 2010-08-17 01:00:08
如果你可以利用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>
自定义(非常简单) 正如我在评论下面所提到的,你可以让它们成为全名和值对。 例如
name :bob
attributes_hat :brown
attributes_shirt :black
another_section_key :value
another_section_key2 :value2
,然后执行关于“\ n”(换行)和字符串分割“:”提取的键和值或构建一个字典/地图对象。
OP的数据格式看起来非常像YAML。所有其他建议的数据格式都没有意义。 – 2010-08-17 00:51:46
是的,我最初选择YAML作为这个项目,但是,我认为这个语法并不合适。配置文件需要尽可能简单,XML和JSON过于冗长,而YAML仍然没有完成。 – trinth 2010-08-17 01:02:01
在这种情况下,您可以将它们全部命名和值对。 例如 name:bob; attributes_hat:brown; attributes_shirt:black; another_section_key:value; another_section_key2:value2 然后在';'上做字符串分割。和':' – 2010-08-17 01:22:16
我建议将配置文件的格式更改为JSON并使用现有的库来解析JSON对象,如FlexJSON。
{
"users": [
{
"name": "bob",
"hat": "brown",
"shirt": "black",
"another_section": {
"key": "value",
"key2": "value2"
}
},
{
"name": "sally",
"sex": "female",
"another_section": {
"pants": "yellow",
"shirt": "red"
}
}
]
}
它对于状态机来说看起来很简单。
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.
清理它的一个好方法是使用表格,即用Map替换您的条件。然后你可以通过反射(简单)来调用你的解析方法,或者创建一些实现通用接口的类(更多的工作但更强大)。
我只是将其添加为注释,因为我没有真正回答你关于该特定配置设置的问题。由于这是一个个人项目,可以改用XML吗?然后你可以使用像JAXP这样的东西。 – JBirch 2010-08-17 00:27:33
基督与Java开发人员和XML有什么关系? Cletus对YAML的建议很有用,因为这是OP的配置看起来最像的。如果您通过Ruby on Rails或甚至PHP架构Symfony扩展自己的视野,您将会了解这一点。我也是一名Java开发人员,大多数其他Java开发人员对XML的持续依赖处于滑稽和可悲之间。我曾经展示过一个我开发给Java“架构师”的微MVC框架,并且他评论说“映射在哪里”,他指的是XML。更好的问题:为什么不能使用其他格式填充地图(ping)? – 2010-08-17 00:50:32
在一些评论中,您声明标准格式的大括号,括号,引号等都太杂乱无章。我只想指出,这种“混乱”有一个很好的理由。目前,您正在依赖选项卡,这非常脆弱。当某人打开配置文件将标签转换为空格时会发生什么?最小限度地,你应该支持那里的空间。 – 2010-08-17 01:28:25