Функция C для вставки текста в определенном месте в файле без перезаписи существующего текста - PullRequest
15 голосов
/ 27 января 2012

Я написал программу, которая принимает файл в качестве входных данных и всякий раз, когда он находит строку длиной> 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;
}

Чтобы запустить его, нужно сделать следующее: -

user@ubuntu$ cc xyz.c
user@ubuntu$ ./a.out file_to_check.txt

Ответы [ 5 ]

17 голосов
/ 15 октября 2013

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

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

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

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

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

TL; версия DR:

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

8 голосов
/ 27 января 2012

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

2 голосов
/ 02 июля 2016

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

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);

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

2 голосов
/ 27 января 2012

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

0 голосов
/ 27 января 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...