如何从二进制文件中删除前n个字符

时间:2016-01-07 14:18:45

标签: c file

我有一个二进制文件,我希望在进一步处理之前删除第一个n个字符。我有文件指针作为输入。

我尝试使用ftruncate,但为此我必须创建另一个我不想要的文件指针。我试过下面的代码,但没有用。

#include <stdio.h>
#include <unistd.h>

int main(void) {

        FILE*f,ftemp;
        f=fopen("./temp","a");
        scanf("%d",&n);
        fseek(f,n,SEEK_SET);
        ftruncate(fileno(f),/*end of file*/ );
        ftemp=f;
        return 0;
}

请以任何其他方式提出建议。

实际上前n个字节是二进制的,部分的其余部分就像asn。

由于

2 个答案:

答案 0 :(得分:2)

这是一个程序,它会删除 ./ temp 的第一个 N 字节,离开原始文件为 ./ temp-old 。 它假定文件适合内存。 您可以在命令行中指定 N

/*
 * THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
 * EITHER EXPRESSED OR IMPLIED.
 */
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

#define ERR (-1)

int main (
        int     argc,
        char ** argv
) {
        int             fdin;
        int             fdout;
        unsigned long   n;
        struct stat     st;
        size_t          sz;
        ssize_t         ssz;
        char *          data;

        if (argc != 2) {
                fprintf(stderr, "usage: %s nbytes\n", argv[0]);
                return 1;
        }
        n = strtoul(argv[1], NULL, 10);
        if (errno) {
                fprintf(stderr, "nbytes (%s) is suspect\n", argv[1]);
                return 1;
        }
        fdin = open("./temp", O_RDONLY, 0);
        if (fdin == ERR) {
                fprintf(stderr, "open input: %s\n", strerror(errno));
                return 1;
        }
        if (fstat(fdin, &st) == ERR) {
                fprintf(stderr, "stat input: %s\n", strerror(errno));
                return 1;
        }
        sz = st.st_size;
        if (sz < n) {
                fprintf(stderr, "file is not that big\n");
                return 1;
        }
        data = malloc(sz);
        if (data == NULL) {
                fprintf(stderr, "insufficient memory\n");
                return 1;
        }
        ssz = read(fdin, data, sz);
        if (ssz < 0) {
                fprintf(stderr, "read input: %s\n", strerror(errno));
                return 1;
        }
        if ((size_t)ssz != sz) {
                fprintf(stderr, "read was short\n");
                return 1;
        }
        (void)close(fdin);
        fdout = open("./temp-new", O_CREAT|O_EXCL|O_WRONLY, st.st_mode);
        if (fdout == ERR) {
                fprintf(stderr, "open output: %s\n", strerror(errno));
                return 1;
        }
        sz -= n;
        ssz = write(fdout, data + n, sz);
        if (ssz < 0) {
                fprintf(stderr, "write output: %s\n", strerror(errno));
                return 1;
        }
        if ((size_t)ssz != sz) {
                fprintf(stderr, "write was short\n");
                return 1;
        }
        if (close(fdout) == ERR) {
                fprintf(stderr, "write close: %s\n", strerror(errno));
                return 1;
        }
        if (link("./temp", "./temp-old") == ERR) {
                fprintf(stderr, "link input: %s\n", strerror(errno));
                return 1;
        }
        if (rename("./temp-new", "./temp") == ERR) {
                fprintf(stderr, "rename output: %s\n", strerror(errno));
                return 1;
        }
        return 0;
}

我仔细地写了它,但是,当然你应该在使用之前制作文件的备份副本,以防万一......

答案 1 :(得分:1)

使用dd实用程序。一种方法是将块大小设置为等于要删除的字节数,然后告诉dd在输入文件中找到一个块:

# N is number of bytes to delete: wherever N is seen
# substitute this value.

dd if=infile of=outfile bs=N seek=1

现在outfileinfile的副本,但删除了前N个字节。

以下命令也可以使用;不幸的是,它会执行1个字节readwrite系统调用,因为块大小是1个字节:

# N is number of bytes to delete
dd if=infile of=outfile bs=1 seek=N

如果N太小而不能成为一个好的缓冲因子,我们可以使用单独的输入和输出块大小(ibsobs),这样至少我们可以写入更大的块:

# read N at a time, skipping first N; write 64 kB at a time:
dd if=infile of=outfile ibs=N obs=65536 seek=1

我们可以通过将outfile重命名为infile来模拟就地删除。

应该可以实际执行此操作。以下几乎有效:

dd if=inoutfile of=inoutfile bs=N conv=notrunc skip=1

唯一的问题是没有conv=notrunc选项,文件会立即截断为零长度。内容消失了! with 该选项,文件在写入后根本不会被截断。

GNU Coreutils有truncate命令可以使用:

dd if=inoutfile of=inoutfile bs=N conv=notrunc skip=1
truncate -s -N inoutfile

-中size参数的truncate前缀表示“将该字符数减少很多字节”。