在 Golang 中记录和查找 CSV 文件位置
Recording and seeking to CSV file positions in Golang
我需要读取一个 CSV 文件并将具有特定值的行的位置记录到一个数组中,然后返回并以不特定的顺序检索这些行并且性能良好,因此随机访问。
我的程序使用 csv.NewReader(file),但我看不到获取或设置它使用的文件偏移量的方法。我尝试 file.Seek(0,io.SeekCurrent) 到 return 文件位置,但它在调用 reader.Read() 之间不会改变。我还尝试了 fmt.Println("+v +v\n",reader,file) 来查看是否有任何东西存储了 reader 的文件位置,但我没有看到它。如果找到文件位置,我也不知道使用文件位置的最佳方法。
这是我需要做的:
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 函数编写自己的函数?
这是一个使用 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)
}
os.Open returns 一个实现 io.Seeker 的文件。
所以您可以这样做来将流倒回到开头:
_, err = file.Seek(0, io.SeekStart)
我需要读取一个 CSV 文件并将具有特定值的行的位置记录到一个数组中,然后返回并以不特定的顺序检索这些行并且性能良好,因此随机访问。
我的程序使用 csv.NewReader(file),但我看不到获取或设置它使用的文件偏移量的方法。我尝试 file.Seek(0,io.SeekCurrent) 到 return 文件位置,但它在调用 reader.Read() 之间不会改变。我还尝试了 fmt.Println("+v +v\n",reader,file) 来查看是否有任何东西存储了 reader 的文件位置,但我没有看到它。如果找到文件位置,我也不知道使用文件位置的最佳方法。
这是我需要做的:
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 函数编写自己的函数?
这是一个使用 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)
}
os.Open returns 一个实现 io.Seeker 的文件。
所以您可以这样做来将流倒回到开头:
_, err = file.Seek(0, io.SeekStart)