2012-03-25 16 views
15

Bir tamsayı dosyasını bir diziye okumam gerekiyor. Ben bu çalışmayı vardır:Golang, bir dizi içine tamsayı bir dosya okumak daha iyi bir yolu var mı?

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

dosya numbers.txt adildir:

1 
2 
3 
... 

ReadFile() (belki hata teslim etme) çok uzun görünüyor.

Dosya yüklemek için daha kısa/daha fazla Go idiomatic yolu var mı?

+4

'fd.Close()' eksiksiniz. 'ReadFile' satır iki olarak bir defer fd.Close() ekleyin. – Mostafa

+1

Hata denetlemesinden sonra 'defer fd.Close()' yerleştirin. Dosya okunamadığı zaman çalışma zamanı paniklerini bu satırdan alacaksınız çünkü fd nil. Önce hatayı kontrol et, sonra kapat. Açamayacak olursanız yine de kapatmak zorunda kalmazsınız. – burfl

+1

Açıklığa kavuşturmak için, bunun sebebi, er ya da geç derhal değerlendirilmekte ve daha sonra icra edilmektedir. Yani fd.Close() bir nil fd (hangi yöntemleri vardır) üzerinde ertelemeye çalıştığınızda bir panik alacaksınız. 'x: = 2; erteleme fmt.Print (x); x = 3 ',' 2 'yazdıracak, değil 3. – burfl

cevap

5

böyle yapacağını: fmt.Fscanf ile

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

Bence, "Her ekte yeniden boyutlandırmaktan kaçınmak için kapak atama", yeniden boyutlandırmadan kaçınmıyor çünkü "[] dizgisinin yeniden boyutlandırılması, dizgelerde bir yerde gizli .Split'. –

+0

Hayır, "dizeleri" boşluklu "önce" sep 'olaylarının sayısını bulur ve bu sayıyı ayırma için kullanır. ['GenSplit'] konusuna bakın (http://code.google.com/p/go/source/browse/src/pkg/strings/strings.go#186). – Mostafa

+0

Gerçekten, ama 'strings.Split 'dize iki kez geçme pahasına yapıyor - bunu beklemiyordum. Her durumda, her ekte "ekleme" nin yeniden boyutlandırılması doğru değildir. –

0

Çözümünüz gayet iyi. Durumunuza bağlı olarak, kesinlikle yapmak için başka yollar vardır. Mostafa'nın tekniği çok kullanıyorum (sonuçta hepsini bir kerede yapamamış olmama rağmen. Oops! O çizdi. Yaptı.) Ama nihai kontrol için bufio.ReadLine'ı öğrenmelisiniz. Bazı örnek kodlar için bkz. go readline -> string.

15

bufio.Scanner'u kullanmak güzel şeyler yapar. Ayrıca bir dosya adı almak yerine io.Reader kullanıyorum. Çoğu zaman bu iyi bir tekniktir, çünkü kodun sadece diskteki bir dosya için değil herhangi bir dosya benzeri nesne üzerinde kullanılmasına izin verir. İşte bir dizeden "okuma".

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) 
} 
İlgili konular