Golang, есть ли лучший способ прочитать файл целых чисел в массив?
Мне нужно прочитать файл целых чисел в массиве. Я работаю с этим:
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))
}
Файл number.txt справедлив:
1
2
3
...
ReadFile()
кажется слишком длинным (возможно, из-за ошибки).
Есть ли более короткий/более Go идиоматический способ загрузки файла?
Ответы
Ответ 1
Использование bufio.Scanner
делает все хорошо. Я также использовал io.Reader
вместо того, чтобы брать имя файла. Часто это хорошая техника, поскольку она позволяет использовать код на любом файлоподобном объекте, а не только на диске. Здесь он "читает" из строки.
package main
import (
"bufio"
"fmt"
"io"
"strconv"
"strings"
)
// ReadInts reads whitespace-separated ints from r. If there 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)
}
Ответ 2
Я бы сделал это следующим образом:
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))
}
Ответ 3
Ваше решение с fmt.Fscanf в порядке. Конечно, есть ряд других способов сделать это, в зависимости от вашей ситуации. Техника Mostafa - это то, что я использую много (хотя я мог бы распределить результат сразу с make. Oops! Scratch this. Он сделал это.), Но для окончательного контроля вы должны изучить bufio.ReadLine. См. go readline → string для некоторого примера кода.