«Go»: чтение ini-файлов
Мне тут для программы на языке «Go» понадобился простой парсер ini-файлов, который я и написал. Делюсь.
Кстати говоря, я сильно облегчил себе жизнь, когда написал небольшой bash-скрипт, компилирующий и запускающий программы на «Go», вот он:
#!/bin/bash
NAME=$1
if [ -f "$NAME" ]; then
TMPNAME=`mktemp -t go.XXXXXXX`
shift
6g -I. -o $TMPNAME.6 $NAME && 6l -L. -o $TMPNAME $TMPNAME.6 && $TMPNAME $*
rm -f $TMPNAME.6 $TMPNAME >/dev/null 2>&1
else
echo "Error: '$NAME' is not found."
fi
Сам модуль выглядит вот так (и называется «ini»):
package ini
import (
"os"
"bufio"
"strings"
)
type inisection map[string]string
type inifile map[string]inisection
func ParseFile(name string) inifile {
f, err := os.OpenFile(name, os.O_RDONLY, 0666)
if err != nil {
return nil
}
defer f.Close()
r := bufio.NewReader(f)
one := inisection{} // одна, текущая секция
ini := inifile {} // все секции
section := "" // текущее название секции
for {
line, err := r.ReadString('\n')
if err != nil {
break
}
chunks := strings.Split(strings.TrimRight(line, "\r\n"), "=", 3)
if len(chunks) == 2 {
key, value := strings.Trim(chunks[0], "\t "), strings.Trim(chunks[1], "\t ")
last := len(value) - 1
if last > 0 && value[0] == '"' && value[last] == '"' {
value = string(value[1:last])
}
one[key] = string(value)
} else {
last := len(chunks[0]) - 1
if chunks[0][0] == '[' && chunks[0][last] == ']' {
ini[section] = one
one = inisection{}
section = string(chunks[0][1:last])
}
}
}
// «поджимаем хвост», поскольку предыдущая секция попадает в map
// только когда появляется следующая, последнюю нужно перекладывать «руками»
if len(one) > 0 {
ini[section] = one
}
return ini
}
Модули пишутся очень просто — достаточно объявить как он будет называться (в директиве «package») и можно его подключать через «import». В «Go» модуль экспортирует всё, что начинается с большой буквы.
Мой модуль экспортирует единственную функцию — ParseFile, принимающую на вход имя файла, на выходе будет ассоциативный массив ассоциативных массивов (map of maps), ключами верхнего уровня является имя секции (если отсутствует, то имя пустое), ключами второго уровня — ключи ini-файла.
Не в качестве придирки, просто любопытно. Почему map — хеш? Вроде бы есть ассоциативный массив, есть прямые переводы (карта, словарь), которые зависят от языка. Ну, и вообще, map далеко не всегда внутри — основан на хеше. Или это просто привычка? А тогда откуда?
Комментарий для bealex.livejournal.com:
Привычка, ага. Да чёрт её знает откуда, как-то прилипла, теперь вторая натура.
Комментарий для bealex.livejournal.com:
Поправил.
Комментарий для Евгения Степанищева:
Выглядит хоть и правильно, но страшновато :) Кстати, чего-нить покороче «ассоциативного массива» не знаешь? Корректного, чтобы, например, на презентации можно было употреблять, но не такого длинного.
Комментарий для bealex.livejournal.com:
Вообще, я думал, что не знаю, пока не вспомнил, что в Пайтоне ассоциативный массив называется «словарь».
И я вспомнил почему меня всё тянет назвать ассоциативный массив хешем. Из-за Перла, я на нём много когда-то программировал, а там ассоциативный массив называется хеш.
Пока писал программу, обновил версию компилятора, оказалось, что сменился вызов — вместо os.Open теперь os.OpenFile, исправил в программе.
Комментарий для Евгения Степанищева:
Ну вот и Go for App Engine.
http://blog.golang.org/2011/05/go-and-google-app-engine.html
Комментарий для rodem:
Интересно как часто там будет компилятор меняться.
Комментарий для rodem:
Пока оно всё в стадии экспериментал, то думаю что часто, раз недель в шесть.
Но лучше узнать в гугл групсах.
А вообще интересно вырастет ли из Голанга конкурент Яве для хайлоад сервер-сайд.
Комментарий для rodem:
Значит программы придётся проверять достаточно часто.
На Гоу сильно сложнее писать, чем на Яве.
в разделе секций забыл проверку на пустую строку
if chunks[0][0] == ’[’ && chunks[0][last] == ’]’ {
if len(chunks[0]) — 1 > 0 && chunks[0][0] == ’[’ && chunks[0][last] == ’]’ {
Да, наверное, но спустя 10 лет вряд ли кому-то этот исходник нужен :)