C для вставки текста в определенном месте в файле без перезаписи существующего текста

Я написал программу, которая берет файл в качестве входных данных и всякий раз, когда он находит строку длиной > 80, она добавляет\и\n к этому файлу, чтобы сделать ее шириной 80 символов.

Проблема заключается в том, что я использовал fseek для вставки\и\n всякий раз, когда длина превышает 80, поэтому она переопределяет два символа этой строки, которая превышает длину 80. Есть ли способ, с помощью которого я могу вставлять текст без переопределения существующего текст?

Вот мой код: -

#include<stdio.h>
#include<string.h>

int main(int argc, char *argv[])
{
  FILE *fp1,*fp2;
  int prev=0,now=0;
  char ch;
  int flag=0;
  long cur;
  fp1=fopen(argv[1],"r+");
  if(fp1==NULL){
    printf("Unable to open the file to read. Program will exit.");
    exit(0);
  }
  else{
    while((ch=fgetc(fp1))!=EOF){
      if(ch!=' ' && ch!='\n'){
        now=now+1;
      }
      else{
        if(now>=80){
            fseek(fp1,cur,SEEK_SET);
            fputc('\\',fp1);
            fputc('\n',fp1);
            now=0;
            continue;
        }
        if(ch=='\n'){
          flag=0;
          now=0;
          continue;
          }
        else{
          prev=now;
          cur=ftell(fp1);
        }
        now=now+1;
      }
    }
  }
  fclose(fp1);
  return 0;
}

Чтобы запустить его, вам необходимо выполнить следующие действия: -

[email protected]$ cc xyz.c
[email protected]$ ./a.out file_to_check.txt

Ответы

Ответ 1

Нет, нет возможности вставлять символы в существующий файл. Для этого вам понадобится второй файл.

Ответ 2

Хотя есть несколько способов сделать это на месте, вы работаете с текстовым файлом и хотите выполнять вставки. Операционные системы обычно не поддерживают вставки текстовых файлов в качестве примитива файловой системы, и нет причин, по которым они должны это делать.

Лучший способ сделать это - открыть файл для чтения, открыть новый файл для записи, скопировать часть файла до точки вставки, вставить данные, скопировать остальные, а затем переместить новый файл поверх старого.

Это обычная техника, и у нее есть цель. Если что-то пойдет не так (например, с вашей системой), у вас все еще есть исходный файл и вы можете повторить транзакцию позже. Если вы запускаете два экземпляра процесса и используете определенный шаблон, второй экземпляр может обнаружить, что транзакция уже запущена. Имея эксклюзивный доступ к файлам, он может даже определить, была ли транзакция прервана или все еще запущена.

Этот способ гораздо меньше подвержен ошибкам, чем любой из методов, выполняемых непосредственно на исходном файле, и используется всеми этими традиционными инструментами, такими как sed, даже если вы попросите их работать на месте (sed -i). Еще один бонус заключается в том, что вы всегда можете переименовать исходный файл в один с суффиксом резервного копирования, прежде чем перезаписывать его (sed также предлагает такую ​​опцию).

Такая же техника часто используется для файлов конфигурации, даже если ваша программа записывает совершенно новую версию и не использует для этого оригинальный файл. Уже давно многие интернет-журналы утверждали, что ext4 случайно обрезает конфигурационные файлы до нулевой длины. Это было именно потому, что некоторые приложения сохраняли файлы конфигурации открытыми и усеченными, когда система была принудительно отключена. Эти приложения часто подделывались исходными конфигурационными файлами, прежде чем они готовили данные, а затем даже открывали их, не синхронизируя их, что сделало окно для повреждения данных намного большим.

TL; версия DR:

Когда вы оцениваете свои данные, не уничтожайте их до того, как будете готовы к замене данных.

Ответ 3

Нет, нет способа. Вам нужно создать новый файл или переместить содержимое файла на 2 символа назад.

Ответ 4

Это функция, которую я использую для такого рода вещей:

int finsert (FILE* file, const char *buffer) {

    long int insert_pos = ftell(file);
    if (insert_pos < 0) return insert_pos;

    // Grow from the bottom
    int seek_ret = fseek(file, 0, SEEK_END);
    if (seek_ret) return seek_ret;
    long int total_left_to_move = ftell(file);
    if (total_left_to_move < 0) return total_left_to_move;

    char move_buffer[1024];
    long int ammount_to_grow = strlen(buffer);
    if (ammount_to_grow >= sizeof(move_buffer)) return -1;

    total_left_to_move -= insert_pos;

    for(;;) {
        u16 ammount_to_move = sizeof(move_buffer);
        if (total_left_to_move < ammount_to_move) ammount_to_move = total_left_to_move;

        long int read_pos = insert_pos + total_left_to_move - ammount_to_move;

        seek_ret = fseek(file, read_pos, SEEK_SET);
        if (seek_ret) return seek_ret;
        fread(move_buffer, ammount_to_move, 1, file);
        if (ferror(file)) return ferror(file);

        seek_ret = fseek(file, read_pos + ammount_to_grow, SEEK_SET);
        if (seek_ret) return seek_ret;
        fwrite(move_buffer, ammount_to_move, 1, file);
        if (ferror(file)) return ferror(file);

        total_left_to_move -= ammount_to_move;

        if (!total_left_to_move) break;

    }

    seek_ret = fseek(file, insert_pos, SEEK_SET);
    if (seek_ret) return seek_ret;
    fwrite(buffer, ammount_to_grow, 1, file);
    if (ferror(file)) return ferror(file);

    return 0;
}

Используйте его следующим образом:

FILE * file= fopen("test.data", "r+");
ASSERT(file);

const char *to_insert = "INSERT";

fseek(file, 3, SEEK_SET);
finsert(file, to_insert);

ASSERT(ferror(file) == 0);
fclose(file);

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

Ответ 5

Вы можете загрузить файл как фрагменты (в вашем случае - 80 символов), а затем добавить два символа (новая строка) и записать содержимое в файл anohter.