Skip to main content

Command Palette

Search for a command to run...

Seeking a position within a file

Updated
4 min readView as Markdown
Seeking a position within a file
S
DevRel at StackGen | Formerly at Deepfence ,Tenable , Accurics | AWS Community Builder also Docker Community Award Winner at Dockercon2020 | CyberSecurity Innovator of Year 2023 award by Bsides Bangalore | Docker/HashiCorp Meetup Organiser Bangalore & Co-Author of Learn Lightweight Kubernetes with k3s (2019) , Packt Publication & also run Non Profit CloudNativeFolks / CloudSecCorner Community To Empower Free Education reach out me twitterhttps://twitter.com/sangamtwts or just follow on GitHub -> https://github.com/sangam14 for Valuable Resources
  • In some cases, you need to read from or write to a particular location in a file, such as an indexed file. The recipe will show you how to use the position seeking in the context of flat file operations.

Create the file flatfile.txt with the following content:

      123.Jun.......Wong......
      12..Novak.....Jurgen....
      10..Thomas....Sohlich...

Create the fileseek.go file with the following content:

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")
}

output:

sangam:golang-daily sangam$ go run fileseek.go
Sohlich.. <nil>
Radomir.. <nil>
Andrew... <nil>
10..Radomir...Sohlich... <nil>
sangam:golang-daily sangam$

Display the file in hex xxd flatfile.txt.

sangam:golang-daily sangam$ 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 0a00 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....
sangam:golang-daily sangam$

How it works...

  • The preceding example uses the flatfile as an illustration of how to seek, read and write at the position in the file. In general, for moving the position of the current pointer in the File, the Seek method can be used. It takes two arguments and these are, position and how to count the position, 0 - relative to file origin, 1 - relative to current position, 2 - relative to the end of file. This way you are able to move the cursor within the file.

  • Note : The flatfile is the most basic form of how to store the data. The record structure has a fixed length and the same for the record parts. The structure of the flat file in the example is: ID - 4 chars, FirstName - 10 chars, LastName - 10 chars. The whole record is 24 chars long, ended by a line break which is the 25th character. The Seek method is used in the implementation of the readLine function in the preceding code.

  • The os.File also contains the ReadAt and WriteAt methods. These methods consume that the bytes to be written/read and the offset where to start. These simplify the writing and reading to a certain position in a file.

2 views

GopherLabs

Part 1 of 50

Ultimate Series of Blogs for All Golang Developer

More from this blog

C

CloudNativeFolks Community

140 posts

CloudNativeFolks is non profit community that empower and educate about cloud native technology !