Некорректная работа 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 шт):

Автор решения: Pak Uula

Вместо 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])
}
→ Ссылка