What is the best way to parse this configuration file?

I am working on a personal project that uses a custom configuration file. The main file format is as follows:

[users] name: bob attributes: hat: brown shirt: black another_section: key: value key2: value2 name: sally sex: female attributes: pants: yellow shirt: red 

There can be an arbitrary number of users, and each of them can have different key / value pairs, and in the section you can use nested keys / values ​​using tabs. I know that I can use json, yaml or even xml for this configuration file, however I would like to save it to order.

The analysis does not have to be difficult, as I already wrote the code to parse it. My question is what is the best way to parse parsing using clean and structured code, and also write in such a way as not to make difficult changes in the future (there may be many nests in the future). Right now, my code looks completely disgusting. For instance,

 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()) { // } } } 

As you can see, the code is pretty dirty and I cut it for presentation here. I feel that there are better ways to do this, perhaps not using BufferedReader. Could someone possibly provide a better way or approach that is not as confusing as mine?

+4
source share
6 answers

Everyone will recommend using XML because it is simply better.

However, if you are on a search to prove that your programmer is standing for himself ...

... there is nothing fundamentally wrong with the code that you posted, in the sense that it is clear, and for potential readers it is obvious what is happening, and if I didn’t completely get out of the loop when working with files, it should do pretty much as possible.

The only criticism I could offer is that it is not recursive. Each level requires a new level of code support. I would probably make a recursive function (a function that calls itself with sub-content as a parameter, and then again, if there is sub-sub-content, etc.), which could be called by reading all this in a hash table with hash tables or something else, and then I use this hash table as a configuration object.

And again, at that moment I will probably stop seeing the point and use XML .;)

+3
source

I would suggest not creating custom code for configuration files. What you offer is not too far from YAML (getting started ). Use this instead.

See Which java java library should I use?

+6
source

If you could use XML or JSON or other well-known data encoding as a data format, it will be much easier to parse / deserialize the text content and extract the values. For instance.

 name: bob attributes: hat: brown shirt: black another_section: key: value key2: value2 

It can be expressed as the following XML (there are other variants of expression in XML)

 <config> <User hat="brown" shirt="black" > <another_section> <key>value</key> <key2>value</key2> </another_section> </User> </config> 

Custom (very simple) As I mentioned in a comment below, you can just tell them all the name and value pairs. eg.

 name :bob attributes_hat :brown attributes_shirt :black another_section_key :value another_section_key2 :value2 

and then split the line into '\ n' (newline) and ':' to extract the key and value or build a dictionary / map object.

+2
source

It looks simple enough for a state machine.

 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. 
+2
source

I would recommend changing the format of the configuration file to JSON and using the existing library to parse JSON objects such as FlexJSON .

 { "users": [ { "name": "bob", "hat": "brown", "shirt": "black", "another_section": { "key": "value", "key2": "value2" } }, { "name": "sally", "sex": "female", "another_section": { "pants": "yellow", "shirt": "red" } } ] 

}

+1
source

A good way to clean it would be to use a table, that is, replace your legend with a map. Then you can call analysis methods using reflection (simple) or create some more classes that implement a common interface (more work, but more reliable).

+1
source

All Articles