我有一个包含多行行的文本文件,用空行分隔。在Go中读取行的最佳方法是什么?
我想我可能不得不使用具有我自己的分割功能的扫描仪,但只是想知道是否有更好/更简单的方法,我错过了。
我尝试过使用基于bufio.ScanLines的自己的Splitfunc:
func MyScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexAny(data, "\n\n"); i >= 0 {
return i + 1, dropCR(data[0:i]), nil
}
if atEOF {
return len(data), dropCR(data), nil
}
return 0, nil, nil
}
但是我在IndexAny调用上遇到错误: "语法错误:意外的分号或换行符,期待)" - 修正了
更新:修正了上面提到的语法错误,但我只返回了第一行。我正在阅读文件如下:
scanner.Split(MyScanLines)
scanner.Scan()
fmt.Println(scanner.Text())
有什么建议吗?
我试图阅读的测试文件示例:
Name = "John"
Surname = "Smith"
Val1 = 700
Val2 = 800
Name = "Pete"
Surname = "Jones"
Val1 = 555
Val2 = 666
Val3 = 444
.
.
.
答案 0 :(得分:2)
你的方式有效,但我建议你使用bufio.Scanner
,默认是逐行扫描。
然后,您只需逐行开始读取文件并填充结构。遇到空行时,将结构放入切片并从新切片开始。
以下是我的一个开源项目的示例:
buffer := [][]string{}
block := []string{}
scanner := bufio.NewScanner(strings.NewReader(data))
for scanner.Scan() {
l := scanner.Text()
if len(strings.TrimSpace(l)) != 0 {
block = append(block, l)
continue
}
// At this point, the script has reached an empty line,
// which means the block is ready to be processed.
// If the block is not empty, append it to the buffer and empty it.
if len(block) != 0 {
buffer = append(buffer, block)
block = []string{}
}
}
if len(block) != 0 {
buffer = append(buffer, block)
}
答案 1 :(得分:2)
以下是使用bufio.Reader
执行相同操作的替代方法。逻辑与Elwiner的答案几乎相似。
myReadLine
函数使用bufio.Reader
来读取文件中的下一个多行条目。
func myReadLine(file *os.File, reader *bufio.Reader) (lines []string, err error){
for {
line, _, err := reader.ReadLine()
if err != nil || len(line) == 0 {
break
}
lines = append(lines, string(line))
}
return lines, err
}
以下代码示例说明了上述函数的示例用法:
reader := bufio.NewReader(file)
for {
lines, err := myReadLine(file, reader)
if err != nil || len(lines) == 0 {
break
}
fmt.Println(lines)
}
答案 2 :(得分:1)
爆发。首先要了解扫描并确保其正常工作:
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
scanner := bufio.NewScanner(strings.NewReader(data))
for scanner.Scan() {
l := scanner.Text()
fmt.Println(l)
}
}
var data = `
Name = "John"
Surname = "Smith"
Val1 = 700
Val2 = 800
Name = "Pete"
Surname = "Jones"
Val1 = 555
Val2 = 666
Val3 = 444
`
这是the code on the Go playground。
接下来,将所需数据收集到切片中。可能有一种方法来检查文件结束,EOF,但我无法找到它。这就是我提出的,这是有效的:
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
buffer := [][]string{}
block := []string{}
scanner := bufio.NewScanner(strings.NewReader(data))
for scanner.Scan() {
l := scanner.Text()
if len(l) != 0 {
block = append(block, l)
continue
}
if len(l) == 0 && len(block) != 0 {
buffer = append(buffer, block)
block = []string{}
continue
}
if len(l) == 0 {
block = []string{}
continue
}
}
if len(block) != 0 {
buffer = append(buffer, block)
block = []string{}
}
fmt.Println("PRINTING BUFFER - END OF PROGRAM - ALL DATA PROCESSED:", buffer)
}
var data = `
Name = "John"
Surname = "Smith"
Val1 = 700
Val2 = 800
Name = "Pete"
Surname = "Jones"
Val1 = 555
Val2 = 666
Val3 = 444
`
答案 3 :(得分:1)
bufio.Scan()在EOF上返回false。 我们将返回第二个' ok'参数,所以我们的来电者可以判断我们是否有 点击了我们的输入结束。
最好在一段字符串中累积我们的记录,并在最后连接。 将每一行依次附加到结果字符串的明显方法是可行的,但行数为O(n ^ 2)。
全部放在一起:
func ReadBlock(scanner *bufio.Scanner) (string, bool) {
var o []string
if scanner.Scan() == false {
return "", false
}
for len(scanner.Text()) > 0 {
o = append(o, scanner.Text())
if scanner.Scan() == false {
break
}
}
return strings.Join(o, " "), true
}
https://play.golang.org/p/C_fB8iaYJo
P.S。看着你的输入,我怀疑你想要将结果作为地图而不是连接的字符串返回。