在某些情况下,需要从文件中的特定位置读取或写入,例如索引文件。本文将向你展示如何在平面文件操作上下文中使用位置搜索。
在文件中寻找位置
Golang 版本
1.12.1
前言
在某些情况下,需要从文件中的特定位置读取或写入,例如索引文件。本文将向你展示如何在平面文件操作上下文中使用位置搜索。
实现
创建文件flatfile.txt
,内容如下:
123.Jun.......Wong......
12..Novak.....Jurgen....
10..Thomas....Sohlich...
创建文件fileseek.go
,代码如下:
package main
import (
"errors"
"fmt"
"os"
)
const lineLegth = 25
func main() {
f, e := os.OpenFile("flatfile.txt", os.O_RDWR|os.O_CREATE,
os.ModePerm)
if e != nil {
panic(e)
}
defer f.Close()
fmt.Println(readRecords(2, "last", f))
if err := writeRecord(2, "first", "Radomir", f); err != nil {
panic(err)
}
fmt.Println(readRecords(2, "first", f))
if err := writeRecord(10, "first", "Andrew", f); err != nil {
panic(err)
}
fmt.Println(readRecords(10, "first", f))
fmt.Println(readLine(2, f))
}
func readLine(line int, f *os.File) (string, error) {
lineBuffer := make([]byte, 24)
f.Seek(int64(line*lineLegth), 0)
_, err := f.Read(lineBuffer)
return string(lineBuffer), err
}
func writeRecord(line int, column, dataStr string, f *os.File)error {
definedLen := 10
position := int64(line * lineLegth)
switch column {
case "id":
definedLen = 4
case "first":
position += 4
case "last":
position += 14
default:
return errors.New("Column not defined")
}
if len([]byte(dataStr)) > definedLen {
return fmt.Errorf("Maximum length for '%s' is %d",
column, definedLen)
}
data := make([]byte, definedLen)
for i := range data {
data[i] = '.'
}
copy(data, []byte(dataStr))
_, err := f.WriteAt(data, position)
return err
}
func readRecords(line int, column string, f *os.File)(string, error) {
lineBuffer := make([]byte, 24)
f.ReadAt(lineBuffer, int64(line*lineLegth))
var retVal string
switch column {
case "id":
return string(lineBuffer[:3]), nil
case "first":
return string(lineBuffer[4:13]), nil
case "last":
return string(lineBuffer[14:23]), nil
}
return retVal, errors.New("Column not defined")
}
$ go run fileseek.go
Sohlich.. <nil>
Radomir.. <nil>
Andrew... <nil>
10..Radomir...Sohlich... <nil>
我们可以使用十六进制看一下flatfile.txt
,使用xxd flatfile.txt
。
$ xxd flatfile.txt
00000000: 3132 332e 4a75 6e2e 2e2e 2e2e 2e2e 576f 123.Jun.......Wo
00000010: 6e67 2e2e 2e2e 2e2e 0a31 322e 2e4e 6f76 ng.......12..Nov
00000020: 616b 2e2e 2e2e 2e4a 7572 6765 6e2e 2e2e ak.....Jurgen...
00000030: 2e0a 3130 2e2e 5261 646f 6d69 722e 2e2e ..10..Radomir...
00000040: 536f 686c 6963 682e 2e2e 0000 0000 0000 Sohlich.........
00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 416e ..............An
00000100: 6472 6577 2e2e 2e2e drew....
原理
前面的示例使用平面文件展示了如何在文件中进行位置查找、读取和写入。通常,要移动File
当前指针的位置,可以使用Seek
方法。它有两个参数,分别是位置和如何计算位置,0 -相对于文件原点,1 -相对于当前位置,2 -相对于文件结束。这样就可以在文件中移动光标。在前面代码中的readLine
函数的实现中使用了Seek
方法。
平面文件是存储数据的最基本形式。其记录格式一般具有固定的长度,并且每一条记录的格式也是相同的。示例中的平面文件的结构是:ID-4个字符,FirstName-10个字符,LastName-10个字符。 整个记录长24个字符,以第25个字符的换行符结束。
os.File
还包含ReadAt
和WriteAt
方法。这些方法将字节数据写入/读取从何处开始的偏移量。这样可以将写和读简化到文件中的某个位置。
注意,本例假设每个rune只有一个字节,对于特殊字符,不必为true,以此类推。