【问题标题】:Recording and seeking to CSV file positions in Golang在 Golang 中记录和查找 CSV 文件位置
【发布时间】:2019-04-11 21:58:17
【问题描述】:

我需要读取一个 CSV 文件并将具有某些值的行的位置记录到一个数组中,然后返回并以没有特定顺序且性能良好的方式检索这些行,因此可以随机访问。

我的程序使用 csv.NewReader(file),但我看不到获取或设置它使用的文件偏移量的方法。我尝试使用 file.Seek(0,io.SeekCurrent) 返回文件位置,但在调用 reader.Read() 之间它不会改变。我还尝试了 fmt.Println("+v +v\n",reader,file) 来查看是否有任何东西存储了阅读器的文件位置,但我没有看到。如果找到文件位置,我也不知道使用文件位置的最佳方法。

这是我需要做的:

file,_ = os.Open("stuff.csv")
reader = csv.NewReader(file)

//read file and record locations
for {
    line,_ = reader.Read()
    if wantToRememberLocation(line) {
         locations = append(locations, getLocation()) //need this function
    }
}

//then revisit certain lines
for {
    reader.GoToLine(locations[random])  //need this function
    line,_ = reader.Read()
    doStuff(line)
}

有没有办法用 csv 库做到这一点,还是我必须使用更原始的文件 io 函数编写自己的方法?

【问题讨论】:

  • 您确定没有其他方法可以做您想做的事吗?你可以只保存你感兴趣的记录吗?如果您确实需要保存这些行的文件偏移量,您可以通过创建自己的阅读器来包装文件对象来完成这项工作。您可以编写阅读器的 Read 方法,使其永远不会超过结尾一通电话。如果您让您的阅读器保存当前行的偏移量并在 CSV 阅读器上执行 Read 后查看它,我认为这将为您提供正确的偏移量。你必须玩它。
  • XY 问题是询问您尝试的解决方案,而不是您的实际问题:The XY Problem
  • 我正在构建一个工具来有效地处理巨大的 csv 文件并且没有 ram 来保存所有的行,所以我需要保存它们的位置。我正在考虑构建自己的阅读器,但想先看看我是否可以使用现有阅读器进行随机访问。
  • 您也许可以在读取文件时记录所有换行位置(例如使用TeeReader),然后将记录编号与换行位置相关联。这仅适用于您的文件不包含 cmets、空行和带换行符的引用值。
  • 谢谢彼得,这行得通。使用io.TeeReader(file,buffer) 复制正在读取的行,line,_ = buffer.ReadBytes('\n') 获取行,然后使用len(line) 获取大小并将其添加到运行总数中以获取位置,但我希望这不会读取 10,000,000 行 CSV 时,速度太慢了。仍然需要找到一种快速返回并将单个行解析为 csv 的方法。

标签: file csv go seek


【解决方案1】:

这是一个使用 TeeReader 的解决方案。此示例只是保存所有位置并返回并重新读取其中一些位置。

//set up some vars and readers to record position and length of each line
type Record struct {
    Pos int64
    Len int 
}
records := make([]Record,1)
var buf bytes.Buffer
var pos int64
file,_ := Open("stuff.csv")
tr := io.TeeReader(file, &buf)
cr := csv.NewReader(tr)

//read first row and get things started
data,_ := cr.Read()
dostuff(data)
//length of current row determines position of next
lineBytes,_ := buf.ReadBytes('\n')
length := len(lineBytes)
pos += int64(length)
records[0].Len = length
records = append(records, Record{ Pos: pos })

for i:=1;;i++ {
    //read csv data
    data,err = c.Read()
    if err != nil {break}
    dostuff(data)
    //record length and position
    lineBytes,_ = buf.ReadBytes('\n')
    lenth = len(lineBytes)
    pos += int64(length)
    records[i].Len = length
    records = append(records, Record{ Pos: pos })
}

//prepare individual line reader
line := make([]byte,1000)
lineReader := bytes.NewReader(line)

//read random lines from file
for {
    i := someLineNumber()
    //use original file reader to fill byte slice with line
    file.ReadAt(line[:records[i].Len], records[i].Pos)
    //need new lineParser to start at beginning every time
    lineReader.Seek(0,0)
    lineParser := csv.NewReader(lineReader)
    data,_ = lineParser.Read()
    doStuff(data)
}

【讨论】:

    【解决方案2】:

    os.Open 返回一个 File,它实现了 io.Seeker。

    因此您可以这样做以将流倒回到开头:

    _, err = file.Seek(0, io.SeekStart)
    

    https://golang.org/src/os/file.go

    【讨论】:

      猜你喜欢
      • 2015-06-11
      • 1970-01-01
      • 1970-01-01
      • 2019-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-21
      • 1970-01-01
      相关资源
      最近更新 更多