C 函数在文件中的特定位置插入文本而不覆盖现有文本

发布于 2024-12-29 11:01:38 字数 1122 浏览 3 评论 0原文

我编写了一个程序,它接受一个文件作为输入,每当它找到长度>的行时80,它会向该文件添加 \ 和 \n,使其最大宽度为 80 个字符。

问题是,只要长度超过 80,我就使用 fseek 插入 \ 和 \n,因此它会覆盖长度超过 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

I have written a program which takes a file as input and whenever it finds a line with length > 80, it adds \ and \n to that file to make it 80 chars in width max.

The problem is that I have used fseek to insert \ and \n whenever the length exceeds 80, so it overrides two characters of that line which exceeds length 80. Is there a way using which I can insert text without overriding the existing text?

Here is my code:-

#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;
}

To run it, you need to do following:-

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

鱼窥荷 2025-01-05 11:01:38

虽然有多种技术可以就地完成此操作,但您正在使用文本文件并希望执行插入。操作系统通常不支持将文本文件插入作为文件系统原语,并且它们没有理由这样做。

执行此类操作的最佳方法是打开文件进行读取,打开一个新文件进行写入,复制插入点之前的文件部分,插入数据,复制其余部分,然后将新文件移到旧文件上。

这是一种常见的技术,并且有其目的。如果出现任何问题(例如您的系统),您仍然拥有原始文件,并且可以稍后重复交易。如果您启动该流程的两个实例并使用特定模式,则第二个实例能够检测到事务已启动。通过独占文件访问,它甚至可以检测事务是否已中断或仍在运行。

这种方式比直接在原始文件上执行的任何技术更不容易出错,并且由所有这些传统工具(如 sed)使用,即使您要求它们就地工作(sed -i)。另一个好处是,您始终可以在覆盖原始文件之前将其重命名为带有备份后缀的文件(sed 也提供了这样的选项)。

即使您的程序正在编写全新版本并且不使用原始文件,同样的技术也经常用于配置文件。不久前,许多网络杂志声称 ext4 意外地将配置文件截断为零长度。这正是因为某些应用程序在系统强制关闭时保持配置文件打开并被截断。这些应用程序经常在数据准备好之前就篡改原始配置文件,甚至在不同步它们的情况下保持打开状态,这使得数据损坏的窗口变得更大。

TL;DR 版本:

当您重视数据时,在准备好替换数据之前不要销毁它。

While there are a couple of techniques to do it in-place, you're working with a text file and want to perform insertions. Operating systems typically don't support text file insertions as a file system primitive and there's no reason they should do that.

The best way to do that kind of thing is to open your file for reading, open a new file for writing, copy the part of the file before the insertion point, insert the data, copy the rest, and then move the new file over the old one.

This is a common technique and it has a purpose. If anything goes wrong (e.g. with your system), you still have the original file and can repeat the transaction later. If you start two instances of the process and use a specific pattern, the second instance is able to detect that the transaction has already been started. With exclusive file access, it can even detect whether the transaction was interrupted or is still running.

That way is much less error prone than any of the techniques performed directly on the original file and is used by all of those traditional tools like sed even if you ask them to work in-place (sed -i). Another bonus is that you can always rename the original file to one with a backup suffix before overwriting it (sed offers such an option as well).

The same technique is often used for configuration files even if your program is writing an entirely new version and doesn't use the original file for that. It hasn't been long since many internet magazines claimed that ext4 accidentally truncates configuration files to zero length. This was exactly because some applications kept the configuration files open and truncated while the system was forcedly shut down. Those application often tampered with the original configuration files before they had the data ready and then even kept them open without syncing them, which made the window for data corruption much larger.

TL;DR version:

When you value your data, don't destroy it before you have the replacement data ready.

2025-01-05 11:01:38

不,无法将字符插入现有文件中。您将需要使用第二个文件来执行此操作。

No, there's no way to insert characters into an existing file. You will need to use a second file to do that.

好听的两个字的网名 2025-01-05 11:01:38

这是我用于此类事情的函数:

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

理论上,如果出现错误,这(正如其他人提到的那样)可能会损坏文件,但这里有一些实际执行此操作的代码...像这样就地通常没问题,但如果您担心的话,应该备份文件......

This is the function I use for this kind of thing:

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

Use it like this:

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

This (as others here have mentioned) can theoretically corrupt a file if there is an error, but here is some code to actually do it... Doing it in-place like this is usually fine, but you should backup the file if you are worried about it...

你对谁都笑 2025-01-05 11:01:38

不,没有办法。您必须创建一个新文件或将文件内容向后移动 2 个字符。

No, there is no way. You have to create a new file or move the contents of the file 2 characters backwards.

む无字情书 2025-01-05 11:01:38

您可以将文件作为块加载(在您的情况下为 80 个字符),然后附加两个字符(换行)并将内容写入另一个文件中。

You can load the file as chunks (in your case is 80 characters) and then append two character (new line) and write the content into anohter file.

花开半夏魅人心 2025-01-05 11:01:38

另一个实现使用

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

FILE *tmp_buf;
int finsert(FILE *f, const char* msg){
    fseek(tmp_buf, 0, SEEK_SET);
    fpos_t f_pos;
    assert (fgetpos(f, &f_pos)==0);

    char buf[50];
    while(fgets(buf, 50, f))
        fputs(buf, tmp_buf);

    long tmp_buf_pos = ftell(tmp_buf);

    fsetpos(f, &f_pos);
    fputs(msg, f);
    fseek(tmp_buf, 0, SEEK_SET);

    while(--tmp_buf_pos>=0)
        fputc(fgetc(tmp_buf), f);

    return ferror(f);
}

int main()
{
    FILE *f = fopen("result.txt", "wb+");
    assert (f!=NULL);
    fputs("some text", f);
    tmp_buf = tmpfile();
    assert (tmp_buf!=NULL);
    assert(finsert(f, "another text")==0);
    fclose (f);
    perror("");
}

在 Cygwin64 中测试的tmpfile()

another implementation use tmpfile()

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

FILE *tmp_buf;
int finsert(FILE *f, const char* msg){
    fseek(tmp_buf, 0, SEEK_SET);
    fpos_t f_pos;
    assert (fgetpos(f, &f_pos)==0);

    char buf[50];
    while(fgets(buf, 50, f))
        fputs(buf, tmp_buf);

    long tmp_buf_pos = ftell(tmp_buf);

    fsetpos(f, &f_pos);
    fputs(msg, f);
    fseek(tmp_buf, 0, SEEK_SET);

    while(--tmp_buf_pos>=0)
        fputc(fgetc(tmp_buf), f);

    return ferror(f);
}

int main()
{
    FILE *f = fopen("result.txt", "wb+");
    assert (f!=NULL);
    fputs("some text", f);
    tmp_buf = tmpfile();
    assert (tmp_buf!=NULL);
    assert(finsert(f, "another text")==0);
    fclose (f);
    perror("");
}

tested in Cygwin64

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文