Интерфейс io.ReaderAt

Интерфейс io.ReaderAt определяет метод ReadAt, который позволяет читать данные из файла, начиная с указанного смещения. В отличие от стандартного io.Reader, который читает последовательно, ReaderAt позволяет выполнять произвольный доступ к данным. Метод принимает срез байтов для записи данных и смещение в байтах, с которого начинается чтение.

Синтаксис

gotype ReaderAt interface {
  ReadAt(p []byte, off int64) (n int, err error)
}

Метод ReadAt читает данные в срез p, начиная с позиции off в источнике. Возвращает количество прочитанных байтов и ошибку.

Пример

Откроем файл и прочитаем несколько байт с определенной позиции:

gopackage main

import (
  "fmt"
  "io"
  "os"
)

func main() {
  file, err := os.Open("example.txt")
  if err != nil {
    fmt.Println("Error opening file:", err)
    return
  }
  defer file.Close()

  buf := make([]byte, 5)
  n, err := file.ReadAt(buf, 10)
  if err != nil && err != io.EOF {
    fmt.Println("Error reading:", err)
    return
  }

  fmt.Printf("Read %d bytes: %s\n", n, string(buf[:n]))
}

Результат выполнения кода:

go"Read 5 bytes: world"

Пример

Используем io.NewSectionReader для чтения участка файла:

gopackage main

import (
  "fmt"
  "io"
  "os"
)

func main() {
  file, err := os.Open("example.txt")
  if err != nil {
    fmt.Println("Error opening file:", err)
    return
  }
  defer file.Close()

  sectionReader := io.NewSectionReader(file, 5, 10)
  buf := make([]byte, 6)
  n, err := sectionReader.ReadAt(buf, 2)
  if err != nil && err != io.EOF {
    fmt.Println("Error reading:", err)
    return
  }

  fmt.Printf("Read %d bytes: %s\n", n, string(buf[:n]))
}

Результат выполнения кода:

go"Read 6 bytes: 345678"

Пример

Чтение с произвольной позиции в нескольких горутинах:

gopackage main

import (
  "fmt"
  "io"
  "os"
  "sync"
)

func main() {
  file, err := os.Open("largefile.dat")
  if err != nil {
    fmt.Println("Error opening file:", err)
    return
  }
  defer file.Close()

  var wg sync.WaitGroup
  positions := []int64{0, 100, 200}

  for _, pos := range positions {
    wg.Add(1)
    go func(offset int64) {
      defer wg.Done()
      buf := make([]byte, 10)
      n, err := file.ReadAt(buf, offset)
      if err != nil && err != io.EOF {
        fmt.Printf("Error at offset %d: %v\n", offset, err)
        return
      }
      fmt.Printf("Offset %d: %s\n", offset, string(buf[:n]))
    }(pos)
  }

  wg.Wait()
}

Результат выполнения кода:

go"Offset 0: 0123456789"
"Offset 100: abcdefghij"
"Offset 200: ABCDEFGHIJ"

Смотрите также

  • интерфейс io.Reader,
    который используется для последовательного чтения данных
  • интерфейс io.WriterAt,
    который позволяет записывать данные с произвольной позиции
  • функцию os.File.ReadAt,
    которая реализует интерфейс для файлового дескриптора
  • тип io.SectionReader,
    который предоставляет доступ только к определенному участку данных