If you have ever been using Java, you may already be familiar with .properties file format. This is a simplistic configuration file format, somewhat similar to INI:
# Comment key = Value
For ones not liking that, there's also alternative syntax:
! Comment key: Value
Where leading/trailing spaces are optional and are trimmed in parsing process.
Since I've seen people ask about this kind of thing, and also have seen people invent ridiculous (and non-functional) regular expressions to parse this file format, I thought that I may as well make an implementation that works "just right".
Ideas are reasonable:
- Regular expressions are cool [citation needed], but implementations may vary, and they may perform well, not so well, or outright awful (especially when it comes to multi-line splitting).
- Splitting file into separate strings (a common thing to do) is a bit faster (in terms of lines of code taken), but unnecessary - that way you do no better than allocating a bunch of strings (input worth of strings, to be precise) just to throw them away in a moment.
Thus, what I've came up with, are some minimally tricky String.indexOf manipulations, which split the input string without any extra trouble, while keeping performance and memory healthy.
Code is as following:
static function parseProperties(text:String):Map<String, String> { var map:Map<String, String> = new Map(), ofs:Int = 0, len:Int = text.length, i:Int, j:Int, endl:Int; while (ofs < len) { // find line end offset: endl = text.indexOf("\n", ofs); if (endl < 0) endl = len; // last line // do not process comment lines: i = text.charCodeAt(ofs); if (i != "#".code && i != "!".code) { // find key-value delimiter: i = text.indexOf("=", ofs); j = text.indexOf(":", ofs); if (j != -1 && (i == -1 || j < i)) i = j; // if (i >= ofs && i < endl) { // key-value pair "key: value\n" map.set(StringTools.trim(text.substring(ofs, i)), StringTools.trim(text.substring(i + 1, endl))); } else { // value-less declaration "key\n" map.set(StringTools.trim(text.substring(ofs, endl)), ""); } } // move on to next line: ofs = endl + 1; } return map; }
You can also get the fucntion and example of usage from Github:
Few things to note:
- For the sake of not doing an extra useless operation, function assumes that linebreaks are \n. If they are not, use StringTools.replace before calling.
- For reason similar to above, escape characters are not being handled. You can still post-process values in outcoming Map structure though.
Well, that's it. Hopefully, this was useful :)
Thanks for sharing. Just what I was looking for.