2016-09-19 53 views
0

我開始學習Go,當使用ioutil.ReadFile函數時,我對包含EOF的事實感到有些困惑。例如,我想要讀取文件並解析字段分隔符上的所有行。防止讀取文件或ReadAll讀取EOF

示例輸入文件:

CZG;KCZG;some text 
EKY;KEKY;some text 
A50;KA50;some text 
UKY;UCFL;some text 
MIC;KMIC;some text 
K2M;K23M;some text 

這是我做的讀取和解析該文件的內容:

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

func main() { 
    /* Read file */ 
    airportsFile := "/path/to/file/ad_iata" 
    content, err := ioutil.ReadFile(airportsFile) 
    if err != nil { 
     log.Fatal(err) 
    } 

    /* split content on EOL */ 
    lines := strings.Split(string(content), "\n") 

    /* split line on field separator ; */ 
    for _, line := range lines { 
     lineSplit := strings.Split(line, ";") 
     fmt.Println(lineSplit) 
    } 
} 

string.Split功能在lineSplit片的末尾添加一個空元素時,看到的EOF(沒有解析)。因此,如果我想訪問該切片的第二個索引(lineSplit[1]),我會遇到panic: runtime error: index out of range。我必須通過這樣做來限制範圍

/* split line on field separator ; */ 
lenLines := len(lines) -1 
for _, line := range lines[:lenLines] { 
    lineSplit := strings.Split(line, ";") 
    fmt.Println(lineSplit[1]) 
} 

如果我想繼續使用ReadFile的簡潔性,還有更好的方法嗎?使用ioutil.ReadAll

回答

4

有作爲「EOF字節」或「EOF字符」沒有這樣的東西時,會發生

相同的問題。您所看到的可能是由文件最後的換行符('\n')引起的。

要逐行讀取一個文件中的行,它更習慣使用bufio.Scanner代替:

file, err := os.Open(airportsFile) 
if err != nil { 
    log.Fatal(err) 
} 
defer file.Close() 

scanner := bufio.NewScanner(file) 
for scanner.Scan() { 
    line := scanner.Text() 
    // ... use line as you please ... 
} 

if err := scanner.Err(); err != nil { 
    log.Fatal(err) 
} 

這實際上解決您的問題,因爲Scanner將讀取最終換行不啓動新的線,就是明證this playground example

+0

在文件末尾沒有尾部的\ n。 'bufio.Scanner'的解決方案確實很好,但我只是想知道是否有一個選項可以防止ioutil.ReadFile在看到EOF時添加一個空片段。如果沒有,我會使用bufio。謝謝。 – ripat

+0

你是如何確認沒有尾隨換行符的?有些編輯實際上並沒有將這些顯示爲空行,而是無聲地添加它們。嘗試'od -tc/path/to/file/ad_iata'並查看。 – Thomas

+0

我站好了。最後一行末尾有一個尾部的\ n。這使得'ioutil.ReadFile'的行爲更符合我的邏輯。我從你身上學到了一些答案。再次感謝。 – ripat

1

您可以使用scanner.Err()來檢查文件讀取時的錯誤。

// Err returns the first non-EOF error that was encountered by the Scanner. 
func (s *Scanner) Err() error { 
    if s.err == io.EOF { 
     return nil 
    } 
    return s.err 
} 

一般進去的慣用方式來閱讀和分析文件是使用bufio.NewScanner其接受作爲輸入參數文件中讀取並返回一個新Scanner

這裏考慮到上述言論是可以讀取和解析文件的方式:

package main 

import (
    "bufio" 
    "fmt" 
    "os" 
) 

func main() { 
    input, err := os.Open("example.txt") 

    if err != nil { 
     panic("Error happend during opening the file. Please check if file exists!") 
     os.Exit(1) 
    } 

    defer input.Close() 

    scanner := bufio.NewScanner(input) 
    for scanner.Scan() { 
     line := scanner.Text() 
     fmt.Printf("%v\n", line) 
    } 
    if err := scanner.Err(); err != nil { 
     fmt.Fprintln(os.Stderr, "reading input:", err) 
    }  
} 
2

輸入文件seeems是CSV文件,所以你可以使用encoding/csv

airportsFile := "/path/to/file/ad_iata" 
content, err := os.Open(airportsFile) 
    if err != nil { 
     log.Fatal(err) 
    } 
r := csv.NewReader(content) 
r.Comma = ';' 
records, err := r.ReadAll() /* split line on field separator ; */ 
if err != nil { 
    log.Fatal(err) 
} 
fmt.Println(records) 

看起來對我來說足夠簡潔,並提供正確的輸出

[[CZG KCZG一些文字] [EKY KEKY一些文字] [A50 KA50一些文字] [UKY UCFL一些文字] [MIC KMIC一些文字] [K2 M K23M一些文字]]