Task: Read a configuration file


C++: #include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string/case_conv.hpp>
using namespace std;
using namespace boost;

typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
static const char_separator<char> sep(" ","#;,");


struct configs{
	string fullname;
	string favoritefruit;
	bool needspelling;
	bool seedsremoved;
	vector<string> otherfamily;
} conf;

void parseLine(const string &line, configs &conf)
{
	if (line[0] == '#' || line.empty())
		return;
	Tokenizer tokenizer(line, sep);
	vector<string> tokens;
	for (Tokenizer::iterator iter = tokenizer.begin(); iter != tokenizer.end(); iter++)
		tokens.push_back(*iter);
	if (tokens[0] == ";"){
		algorithm::to_lower(tokens[1]);
		if (tokens[1] == "needspeeling")
			conf.needspelling = false;
		if (tokens[1] == "seedsremoved")
			conf.seedsremoved = false;
	}
	algorithm::to_lower(tokens[0]);
	if (tokens[0] == "needspeeling")
		conf.needspelling = true;
	if (tokens[0] == "seedsremoved")
		conf.seedsremoved = true;
	if (tokens[0] == "fullname"){
		for (unsigned int i=1; i<tokens.size(); i++)
			conf.fullname += tokens[i] + " ";
		conf.fullname.erase(conf.fullname.size() -1, 1);
	}
	if (tokens[0] == "favouritefruit") 
		for (unsigned int i=1; i<tokens.size(); i++)
			conf.favoritefruit += tokens[i];
	if (tokens[0] == "otherfamily"){
		unsigned int i=1;
		string tmp;
		while (i<=tokens.size()){		
			if ( i == tokens.size() || tokens[i] ==","){
				tmp.erase(tmp.size()-1, 1);
				conf.otherfamily.push_back(tmp);
				tmp = "";
				i++;
			}
			else{
				tmp += tokens[i];
				tmp += " ";
				i++;
			}
		}
	}
}

int _tmain(int argc, TCHAR* argv[])
{
	if (argc != 2)
	{
		wstring tmp = argv[0];
		wcout << L"Usage: " << tmp << L" <configfile.ini>" << endl;
		return -1;
	}
	ifstream file (argv[1]);
	
	if (file.is_open())
		while(file.good())
		{
			char line[255];
			file.getline(line, 255);
			string linestring(line);
			parseLine(linestring, conf);
		}
	else
	{
		cout << "Unable to open the file" << endl;
		return -2;
	}

	cout << "Fullname= " << conf.fullname << endl;
	cout << "Favorite Fruit= " << conf.favoritefruit << endl;
	cout << "Need Spelling= " << (conf.needspelling?"True":"False") << endl;
	cout << "Seed Removed= " << (conf.seedsremoved?"True":"False") << endl;
	string otherFamily;
	for (unsigned int i = 0; i < conf.otherfamily.size(); i++)
		otherFamily += conf.otherfamily[i] + ", ";
	otherFamily.erase(otherFamily.size()-2, 2);
	cout << "Other Family= " << otherFamily << endl;

	return 0;
}



Go: package config

import (
	"errors"
	"io"
	"fmt"
	"bytes"
	"strings"
	"io/ioutil"
)

var (
	ENONE    = errors.New("Requested value does not exist")
	EBADTYPE = errors.New("Requested type and actual type do not match")
	EBADVAL  = errors.New("Value and type do not match")
)

type varError struct {
	err error
	n   string
	t   VarType
}

func (err *varError) Error() string {
	return fmt.Sprintf("%v: (%q, %v)", err.err, err.n, err.t)
}

type VarType int

const (
	Bool VarType = 1 + iota
	Array
	String
)

func (t VarType) String() string {
	switch t {
	case Bool:
		return "Bool"
	case Array:
		return "Array"
	case String:
		return "String"
	}

	panic("Unknown VarType")
}

type confvar struct {
	Type VarType
	Val  interface{}
}

type Config struct {
	m map[string]confvar
}

func Parse(r io.Reader) (c *Config, err error) {
	c = new(Config)
	c.m = make(map[string]confvar)

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return
	}

	lines := bytes.Split(buf, []byte{'\n'})

	for _, line := range lines {
		line = bytes.TrimSpace(line)
		if len(line) == 0 {
			continue
		}
		switch line[0] {
		case '#', ';':
			continue
		}

		parts := bytes.SplitN(line, []byte{' '}, 2)
		nam := string(bytes.ToLower(parts[0]))

		if len(parts) == 1 {
			c.m[nam] = confvar{Bool, true}
			continue
		}

		if strings.Contains(string(parts[1]), ",") {
			tmpB := bytes.Split(parts[1], []byte{','})
			for i := range tmpB {
				tmpB[i] = bytes.TrimSpace(tmpB[i])
			}
			tmpS := make([]string, 0, len(tmpB))
			for i := range tmpB {
				tmpS = append(tmpS, string(tmpB[i]))
			}

			c.m[nam] = confvar{Array, tmpS}
			continue
		}

		c.m[nam] = confvar{String, string(bytes.TrimSpace(parts[1]))}
	}

	return
}

func (c *Config) Bool(name string) (bool, error) {
	name = strings.ToLower(name)

	if _, ok := c.m[name]; !ok {
		return false, nil
	}

	if c.m[name].Type != Bool {
		return false, &varError{EBADTYPE, name, Bool}
	}

	v, ok := c.m[name].Val.(bool)
	if !ok {
		return false, &varError{EBADVAL, name, Bool}
	}
	return v, nil
}

func (c *Config) Array(name string) ([]string, error) {
	name = strings.ToLower(name)

	if _, ok := c.m[name]; !ok {
		return nil, &varError{ENONE, name, Array}
	}

	if c.m[name].Type != Array {
		return nil, &varError{EBADTYPE, name, Array}
	}

	v, ok := c.m[name].Val.([]string)
	if !ok {
		return nil, &varError{EBADVAL, name, Array}
	}
	return v, nil
}

func (c *Config) String(name string) (string, error) {
	name = strings.ToLower(name)

	if _, ok := c.m[name]; !ok {
		return "", &varError{ENONE, name, String}
	}

	if c.m[name].Type != String {
		return "", &varError{EBADTYPE, name, String}
	}

	v, ok := c.m[name].Val.(string)
	if !ok {
		return "", &varError{EBADVAL, name, String}
	}

	return v, nil
}