go基础库之在文件中寻找位置

在某些情况下,需要从文件中的特定位置读取或写入,例如索引文件。本文将向你展示如何在平面文件操作上下文中使用位置搜索。

在文件中寻找位置

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还包含ReadAtWriteAt方法。这些方法将字节数据写入/读取从何处开始的偏移量。这样可以将写和读简化到文件中的某个位置。

注意,本例假设每个rune只有一个字节,对于特殊字符,不必为true,以此类推。