Удаление строки из файла С++

Подскажите, пожалуйста, возможно ли удалить строку из файла не перезаписав данные в другой файл? Т.е. просто удалить одну какую-либо строку не создав новый файл для копирования оставшихся данных


Ответы (2 шт):

Автор решения: DmitryK

Можно, но сложнее в реализации - можно допустить ошибку. Копируете остаток файла после удаляемой строки в буфер в памяти. Потом записываете его с адреса начала удаляемой строки. В конце вычисляете новый размер файла и "обрезаете" его с помощью std::filesystem::resize_file.
Если памяти мало (остаток не помещается в память), то ограничиваете размер буфера (например 1 Mb) и повторяете в цикле так пока не перепишете весь остаток файла.

→ Ссылка
Автор решения: avp

Посмотрите на man fseek (а также ftell там же) и вперед.

Например, вот так

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

// first argument -- `file-name`, second -- line number for delete (counts from zero)

int
main (int ac, char *av[])
{
  if (ac != 3) {
    fprintf(stderr, "Usage: %s file-name line-number\n", av[0]);
    exit(1);
  }
  size_t lineno = atoi(av[2]);
  FILE *file = fopen(av[1], "r+");
  if (!file) {
    perror(av[1]);
    exit(1);
  }
  struct stat st;
  fstat(fileno(file), &st);
  
  char *line;
  ssize_t ll;
  size_t n = 0, lsz = 0;
  long pos = -1;

  while ((ll = getline(&line, &lsz, file)) > 0) {
    if (n == lineno) {
      pos = ftell(file);
      break;
    }
    n++;
  }

  if (pos == -1) {
    fprintf(stderr, "line %zd not found (%zd lines in the file)\n",
        lineno, n);
    exit(2);
  }
  free(line);

  long wpos = pos - ll;
  //  printf("line %zd found at pos %zd (end at pos %zd)\n",
  //     lineno, wpos, pos);

  size_t bsize = 1024 * 128;
  char *buf = (char *)malloc(bsize);
  size_t lr;
  
  while ((lr = fread(buf, 1, bsize, file))) {
    pos = ftell(file);
    fseek(file, wpos, SEEK_SET);
    fwrite(buf, 1, lr, file);
    wpos = ftell(file);
    fseek(file, pos, SEEK_SET);
  }

  ftruncate(fileno(file), st.st_size - ll);

  return puts("End") == EOF;
}

Ага, g++ компайлер признает этот код вполне крестовым (т.е. с++-шным)


P.S.

Конечно, переписывание данных внутри файла подобным образом не самый быстрый вариант (перемещение по файлу будет сбивать алгоритм опережающего чтения файла ядром ОС в кэш файловой системы, imho переписать в другой файл и переименовать его будет работать быстрее).

Самым же оптимальным, видимо, должен быть вариант с использованием mmap (в результате этого вызова часть адресного пространства пользователя будет отображаться на адреса в файловом кэше ядра, содержащие файл).

Затем несколькими memchr() в цикле фиксируется нужная строка и вызывается memmove() для сдвига данных файла прямо в памяти ядра. В конце (как и в приведенном коде) вызов truncate() обрезает файл до нужного размера.

→ Ссылка