← Назад к вопросам

Что такое lseek?

1.8 Middle🔥 151 комментариев
#Linux и операционные системы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

lseek — изменение позиции в файле

lseek() — это системный вызов в Unix/Linux, который изменяет текущую позицию (offset) в открытом файле. Это позволяет перемещаться по файлу произвольным образом при последовательном чтении/записи.

Сигнатура функции

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

Параметры:

  • fd — файловый дескриптор (файл, открытый через open())
  • offset — смещение в байтах относительно whence
  • whence — точка отсчёта смещения

Возвращаемое значение:

  • Новая позиция в файле (в байтах от начала)
  • -1 при ошибке (и устанавливается errno)

Параметры whence

whence определяет, от какой точки считать смещение:

SEEK_SET  // 0 — от начала файла
SEEK_CUR  // 1 — от текущей позиции
SEEK_END  // 2 — от конца файла

Примеры использования

1. Перейти в начало файла:

#include <unistd.h>
#include <fcntl.h>
#include <iostream>

int main() {
    int fd = open("file.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    
    // Переместить указатель в начало
    off_t pos = lseek(fd, 0, SEEK_SET);
    std::cout << "Current position: " << pos << std::endl;  // 0
    
    close(fd);
    return 0;
}

2. Переместить указатель в конец файла:

int fd = open("file.txt", O_WRONLY);

// Переместиться в конец файла
lseek(fd, 0, SEEK_END);

// Теперь write() добавит данные в конец
write(fd, "new data\n", 9);

close(fd);

3. Получить размер файла:

int fd = open("file.txt", O_RDONLY);

// Переместиться в конец и получить текущую позицию
off_t size = lseek(fd, 0, SEEK_END);
std::cout << "File size: " << size << " bytes" << std::endl;

close(fd);

4. Переместиться на N байт вперёд:

int fd = open("file.txt", O_RDONLY);

// Читаем первые 100 байт
char buffer[100];
read(fd, buffer, 100);

// Перемещаемся на 50 байт назад
lseek(fd, -50, SEEK_CUR);

// Теперь читаем ещё 50 байт (с перекрытием)
read(fd, buffer, 50);

close(fd);

Практический пример: случайный доступ к файлу

#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <cstring>

struct Record {
    int id;
    char name[50];
    float salary;
};

int main() {
    int fd = open("records.bin", O_RDONLY);
    
    // Читаем 5-й запись (offset = 4 * sizeof(Record))
    int record_num = 4;  // 0-based index
    off_t offset = record_num * sizeof(Record);
    
    if (lseek(fd, offset, SEEK_SET) == -1) {
        perror("lseek");
        return 1;
    }
    
    Record rec;
    if (read(fd, &rec, sizeof(Record)) != sizeof(Record)) {
        perror("read");
        return 1;
    }
    
    std::cout << "Record: " << rec.id << " " << rec.name << std::endl;
    
    close(fd);
    return 0;
}

Важные особенности

1. Offset может быть больше размера файла:

int fd = open("file.txt", O_WRONLY);
lseek(fd, 1000000, SEEK_SET);
write(fd, "data", 4);  // Создаст "дыру" в файле
close(fd);

Этот файл будет занимать 1000004 байта, но на диске физически храниться будут только последние 4 байта. Остальное — разреженное пространство (sparse file).

2. Позиция отслеживается в процессе дескриптора:

int fd = open("file.txt", O_RDONLY);

read(fd, buf1, 10);      // читаем 10 байт, pos = 10
lseek(fd, 0, SEEK_SET);  // сбрасываем в начало
read(fd, buf2, 10);      // читаем ещё 10 байт (те же самые)

Каждый дескриптор имеет свою позицию. Если открыть один и тот же файл дважды, позиции будут независимы.

3. Ошибки при lseek:

off_t new_pos = lseek(fd, 100, SEEK_SET);
if (new_pos == -1) {
    if (errno == EINVAL) {
        std::cout << "Invalid file descriptor or whence value" << std::endl;
    } else if (errno == EOVERFLOW) {
        std::cout << "Offset value is too large" << std::endl;
    }
}

Применение в реальных проектах

1. Логирование — добавление данных в конец лога

void append_to_log(const char* filename, const char* message) {
    int fd = open(filename, O_WRONLY | O_APPEND);
    lseek(fd, 0, SEEK_END);
    write(fd, message, strlen(message));
    close(fd);
}

2. Работа с бинарными файлами — случайный доступ к записям БД

// Читаем запись N из структурированного бинарного файла
void read_record(int record_num, Record& rec) {
    int fd = open("database.bin", O_RDONLY);
    lseek(fd, record_num * sizeof(Record), SEEK_SET);
    read(fd, &rec, sizeof(Record));
    close(fd);
}

3. Работа с памятью — отладка

// Получаем размер файла
off_t file_size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);  // Возвращаемся в начало

Альтернативы

fseek() — версия для потоков (FILE*):

FILE* file = fopen("file.txt", "rb");
fseek(file, 100, SEEK_SET);
fclose(file);

mmap() — отображение файла в память для быстрого доступа:

int fd = open("file.txt", O_RDONLY);
char* data = (char*)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
// Теперь обращаемся к файлу как к массиву
data[100]  // доступ к 100-му байту
munmap(data, size);
close(fd);

Итог

lseek() — это фундаментальная функция для работы с файлами, позволяющая:

  • Получать размер файла
  • Осуществлять случайный доступ
  • Перемещаться по файлу произвольным образом
  • Добавлять данные в конец файла

Это критически важно для системного программирования на C/C++ и работы с файловой системой.

Что такое lseek? | PrepBro