Golang:读取包含多行行的文本文件

时间:2015-12-23 12:00:51

标签: go

我有一个包含多行行的文本文件,用空行分隔。在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

 .
 .
 .

4 个答案:

答案 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
`

这是the code on the playground

答案 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。看着你的输入,我怀疑你想要将结果作为地图而不是连接的字符串返回。