2012-03-25 28 views
15

Necesito leer un archivo de enteros en una matriz. Tengo que trabajar con esto:Golang, ¿hay alguna manera mejor de leer un archivo de enteros en una matriz?

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)) 
} 

El archivo numbers.txt es simplemente:

1 
2 
3 
... 

ReadFile() parece demasiado largo (tal vez debido a la entrega de error).

¿Hay una manera más corta/más idiomática de cargar un archivo?

+4

Te estás perdiendo 'fd.Close()'. Agregue un 'diferir fd.Close()' como la línea dos de 'readFile'. – Mostafa

+1

Coloque 'diferir fd.Close()' después de la comprobación de errores. Obtendrá pánico de tiempo de ejecución desde esta línea cuando la lectura del archivo falle porque fd es nulo. Verifique primero el error y luego difiera su cierre. No será necesario cerrar de todos modos si no se abre. – burfl

+1

Para aclarar, esto se debe a que los difers se evalúan inmediatamente y se ejecutan más tarde. Entonces, cuando intentes diferir fd.Close() en un fd nulo (que no tiene métodos), te entrará pánico. 'x: = 2; aplazar fmt.Print (x); x = 3 'imprimirá' 2 ', no 3. – burfl

Respuesta

5

lo haría así:

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)) 
} 
+1

En mi opinión, el "Asignar límite para evitar el cambio de tamaño en cada apéndice" no evita el cambio de tamaño porque el tamaño de '[] cadena' está escondido en alguna parte de' cadenas .Split'. –

+0

No, 'cadenas.Split' primero encuentra el número de apariciones de' sep' y usa ese número para la asignación. Ver ['genSplit'] (http://code.google.com/p/go/source/browse/src/pkg/strings/strings.go#186). – Mostafa

+0

De hecho, pero 'strings.Split' lo hace a costa de pasar por la cadena dos veces, no esperaba eso. En cualquier caso, no es cierto que 'append' cambia de tamaño en cada apéndice. –

0

su solución con fmt.Fscanf está muy bien. Sin embargo, hay una serie de otras formas de hacerlo, dependiendo de su situación. La técnica de Mostafa es una que yo uso mucho (aunque podría asignar el resultado de una sola vez con make. ¡Vaya! ¡Raspe eso!). Pero para el control final, debería aprender bufio.ReadLine. Consulte go readline -> string para obtener un código de ejemplo.

15

El uso de bufio.Scanner hace las cosas bien. También utilicé io.Reader en lugar de tomar un nombre de archivo. A menudo esa es una buena técnica, ya que permite que el código se use en cualquier objeto similar a un archivo y no solo un archivo en el disco. Aquí está "leyendo" de una cadena.

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) 
} 
Cuestiones relacionadas