Golang, есть ли лучший способ прочитать файл целых чисел в массив? - PullRequest
15 голосов
/ 25 марта 2012

Мне нужно прочитать файл целых чисел в массив. У меня это работает с этим:

package main

import (
    "fmt"
    "io"
    "os"
)

func readFile(filePath string) (numbers []int) {
    fd, err := os.Open(filePath)
    if err != nil {
        panic(fmt.Sprintf("open %s: %v", filePath, err))
    }
    var line int
    for {

        _, err := fmt.Fscanf(fd, "%d\n", &line)

        if err != nil {
            fmt.Println(err)
            if err == io.EOF {
                return
            }
            panic(fmt.Sprintf("Scan Failed %s: %v", filePath, err))

        }
        numbers = append(numbers, line)
    }
    return
}

func main() {
    numbers := readFile("numbers.txt")
    fmt.Println(len(numbers))
}

Файл numbers.txt просто:

1
2
3
...

ReadFile() кажется слишком длинным (возможно, из-за ошибки).

Существует ли более короткий / более идиоматический способ загрузки файла?

Ответы [ 3 ]

17 голосов
/ 21 сентября 2013

Использование bufio.Scanner делает вещи приятными.Я также использовал io.Reader вместо того, чтобы брать имя файла.Часто это хорошая техника, так как она позволяет использовать код на любом файловом объекте, а не только на диске.Вот это «чтение» из строки.

package main

import (
    "bufio"
    "fmt"
    "io"
    "strconv"
    "strings"
)

// ReadInts reads whitespace-separated ints from r. If there's an error, it
// returns the ints successfully read so far as well as the error value.
func ReadInts(r io.Reader) ([]int, error) {
    scanner := bufio.NewScanner(r)
    scanner.Split(bufio.ScanWords)
    var result []int
    for scanner.Scan() {
        x, err := strconv.Atoi(scanner.Text())
        if err != nil {
            return result, err
        }
        result = append(result, x)
    }
    return result, scanner.Err()
}

func main() {
    tf := "1\n2\n3\n4\n5\n6"
    ints, err := ReadInts(strings.NewReader(tf))
    fmt.Println(ints, err)
}
5 голосов
/ 25 марта 2012

Я бы сделал это так:

package main

import (
"fmt"
    "io/ioutil"
    "strconv"
    "strings"
)

// It would be better for such a function to return error, instead of handling
// it on their own.
func readFile(fname string) (nums []int, err error) {
    b, err := ioutil.ReadFile(fname)
    if err != nil { return nil, err }

    lines := strings.Split(string(b), "\n")
    // Assign cap to avoid resize on every append.
    nums = make([]int, 0, len(lines))

    for i, l := range lines {
        // Empty line occurs at the end of the file when we use Split.
        if len(l) == 0 { continue }
        // Atoi better suits the job when we know exactly what we're dealing
        // with. Scanf is the more general option.
        n, err := strconv.Atoi(l)
        if err != nil { return nil, err }
        nums = append(nums, n)
    }

    return nums, nil
}

func main() {
    nums, err := readFile("numbers.txt")
    if err != nil { panic(err) }
    fmt.Println(len(nums))
}
0 голосов
/ 25 марта 2012

Ваше решение с fmt.Fscanf в порядке. Есть, конечно, несколько других способов сделать это, в зависимости от вашей ситуации. Технику Мостафы я часто использую (хотя я мог бы выделить результат сразу с помощью make. Упс! Вычеркните это. Он это сделал), но для полного контроля вы должны изучить bufio.ReadLine. См. go readline -> string для примера кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...