【问题标题】:Golang: Reading a text file with multi-line rowsGolang:读取具有多行行的文本文件
【发布时间】:2015-12-26 03:05:07
【问题描述】:

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

 .
 .
 .

【问题讨论】:

  • 请提供您尝试阅读的文件样本。
  • @PrashantThakkar 现在在原始帖子中提供示例。一些值对可能在一个记录中而不在其他记录中,并且顺序也不固定。
  • 谢谢,您收到的错误清楚地表明“)”丢失。更正:如果 i := bytes.IndexAny(data, "\n\n");我 >= 0 {
  • @PrashantThakkar 啊不!我一直盯着那个代码,没有拿起它。啊。感谢那。我这样做的方式是推荐的方式吗?

标签: go


【解决方案1】:

这是一种替代方法,使用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】:

    您的方式可行,但我建议您使用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)
    }
    

    【讨论】:

    • 您的代码不起作用。在 Go 操场上查看:play.golang.org/p/v5aOCK7zRG
    • 感谢您的提及,我有效地反转了一个条件,同时从我的代码中删除了不相关的上下文。我修复了它,并在最后添加了一个检查以说明文件的结尾。
    【解决方案3】:

    爆发了。首先了解扫描并确保其正常工作:

    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

    【讨论】:

    • 非常感谢!
    • 你基本上接受了我的答案并重新发布......你甚至没有更改变量名称来模拟自己解决问题。可怜……
    • 目的是解决方案,而不是知识产权或天才独创性。你的代码不工作。我提供了另一个迭代。我指出了我觉得我的代码可以改进的地方。有人改进了它,提供了另一个迭代。团队合作专注于解决方案。对我来说,这就是 stackoverflow 的意义所在:程序员们聚在一起互相帮助。我不是要窃取你的代码。冷静下来,玩得开心。点名是没有必要的。
    【解决方案4】:

    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

    附言查看您的输入,我怀疑您希望将结果作为映射而不是串联字符串返回。

    【讨论】:

      猜你喜欢
      • 2015-05-13
      • 2020-08-10
      • 1970-01-01
      • 1970-01-01
      • 2019-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-19
      相关资源
      最近更新 更多