Некорректная работа Map в GO
Пытаюсь написать, казалось бы, простую функцию, что будет по результатам прочтения текстового файла создавать такой словарь, где ключом является слово, попавшееся в тексте, а значением количество его повторений в файле.
Однако map просто отказывается по неясной мне причине обращаться к существующему ключу, создавая вместо него идентичный.
Что происходит и как это исправить?
Код:
file, _ := os.Open("text.txt")
defer file.Close()
buffer := make([]byte, 1024)
file.Read(buffer)
splited := strings.Split(string(buffer), " ")
dictionary := make(map[string]int)
for _, word := range splited {
if word != "" {
seeking := strings.Trim(strings.ToLower(word), " ")
if _, ok := dictionary[seeking]; ok {
dictionary[seeking] += 1
} else {
dictionary[seeking] = 1
}
}
}
fmt.Println(dictionary)
text.txt:
1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 9
out:
map[1:2 2:2 3:2 4:2 5:2 6:2 7:2 8:2 9:2 9:1]
Ответы (1 шт):
Вместо fmt.Println(dictionary) распечатайте отладочное представление словаря fmt.Printf("%#v\n", dictionary)
Вы увидите вот что:
map[string]int{"1":2, "2":2, "3":2, "4":2, "5":2, "6":2, "7":2, "8":2, "9":2, "9\x00\x00\x00\x00 ... 1000 раз \x00 ... \x00":1}
то есть последняя девятка в вашем словаре - это вовсе не девятка, а "9 + тысяча нулевых байтов", то есть весь хвост буфера.
Вам нужно превращать в строку не весь буфер, а только заполненную часть
n, err := file.Read(buffer)
if err != nil {
panic(err)
}
splited := strings.Split(string(buffer[:n]), " ")
Результат:
map[string]int{"1":2, "2":2, "3":2, "4":2, "5":2, "6":2, "7":2, "8":2, "9":3}
Как улучшить
ИМХО, делать свой парсер, да ещё с поблочным чтением - это верный способ где-нибудь выстрелить себе в ногу. Я бы воспользовался сканером bufio.Scaner, который из коробки умеет разбивать на строки, слова или буквы. Плюс добавил бы удаление знаков препинания в начале и конце слова. В середине нужно оставить, чтобы корректно обрабатывать слова с дефисом внутри (например, по-русски)
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"unicode"
)
func main() {
file, _ := os.Open("text.txt")
defer file.Close()
dictionary := TrimReader(file)
fmt.Printf("%#v\n", dictionary)
}
func TrimReader(input io.Reader) map[string]int {
scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanWords)
dictionary := make(map[string]int)
for scanner.Scan() {
word := scanner.Text()
seeking := TrimNonLetters(strings.ToLower(word))
if seeking != "" {
dictionary[seeking] += 1
}
}
return dictionary
}
func TrimNonLetters(word string) string {
runes := []rune(word)
start := 0
for ; start < len(runes); start++ {
if unicode.IsLetter(runes[start]) {
break
}
}
end := len(runes) - 1
for ; end >= start; end-- {
if unicode.IsLetter(runes[end]) {
break
}
}
return string(runes[start : end+1])
}