我开始学习Go,我对使用ioutil.ReadFile函数时包含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函数在看到EOF(无需解析)时在lineSplit切片的末尾添加一个空元素。因此,如果我想访问该切片的第二个索引(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来实现它的简洁性,还有更好的方法吗?
时会出现同样的问题答案 0 :(得分: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可以证明这一点。
答案 1 :(得分:2)
您的输入文件看起来是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一些文字] [K2M K23M一些文字]]答案 2 :(得分: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)
}
}