在C语言中删除文件的一部分

3
我该如何创建一个函数来删除文件中的某些部分呢?例如,文件内容如下:

-.
Chair: A23
Number: 123
Name: Joshua
-.
Chair: B12
Number: 512
Name: Marcus
-.
Chair: C2
Number: 1
Name: Drake

如果输入为"B12",那么文件内容将变为:

-.
Chair: A23
Number: 123
Name: Joshua
-.
Chair: C2
Number: 1
Name: Drake

我需要这个函数让我的程序正常运行,但我不知道该怎样做。
3个回答

2
在与原始文件相同的目录中打开一个新文件。将要替换原始文件内容的内容写入该文件。关闭新文件。将新文件重命名为原始文件的名称。
但是也许您应该考虑使用数据库而不是文本文件。

但是我该怎么做呢?我需要找到我将要覆盖的内容,它应该是由B12标识的。所以“光标”将在B12之后,我只能覆盖接下来的内容。 - Rafael Santos
你可以按照自己的方式进行操作。你可以读取整个条目,然后决定是否将它们写出。如果你写入了太多内容,你可以截断文件。你可以按照自己的方式进行操作。 - David Schwartz
问题是我确实需要使用一个文本文件 xD。 - Rafael Santos
1
如果文件很小,何不将整个文件读入内存数据库中呢?这样您就可以在内存中随意修改数据库。完成后,将其写入文件并将该新文件重命名为现有文件。 - David Schwartz
内存数据库可以直接使用C结构体实现。 - Benjamin Smus

1
考虑使用以下组合:
1. 在不覆盖任何现有内容的情况下在二进制文件中间写入 2. 如何在C中截断文件? 您可以使用步骤1的逆来将文件末尾(要删除的部分之后)的内容复制到要删除的部分上,然后使用步骤2将文件大小设置为新值。
或者更简单地,将要删除部分之前和之后的内容复制到一个新文件中,然后将新文件(的内容)移动到旧文件的位置。

相关代码:

#include "posixver.h"
#include <sys/stat.h>
#include <unistd.h>

#if !defined(BUFFERSIZE)
#if defined(DO_NOT_TEST)
enum { BUFFERSIZE = 64 * 1024 };
#else
enum { BUFFERSIZE = 4 };
#endif /* DO_NOT_TEST */
#endif /* !BUFFERSIZE */

static inline size_t min_size(size_t x, size_t y) { return (x < y) ? x : y; }

static int shrink_file_and_delete(int fd, size_t offset, size_t dellen)
{
    char buffer[BUFFERSIZE];
    struct stat sb;
    int rc = -1;

    if (fstat(fd, &sb) == 0)
    {
        size_t file_size = sb.st_size;   /* off_t to size_t conversion */
        if (file_size > offset && dellen > 0)
        {
            /* Move data after offset + dellen bytes down by dellen bytes */
            if (file_size > offset + dellen)
            {
                size_t tbytes = file_size - offset - dellen;
                size_t rd_pos = offset + dellen;
                size_t wr_pos = offset;
                while (tbytes != 0)
                {
                    ssize_t nbytes = min_size(BUFFERSIZE, tbytes);
                    lseek(fd, rd_pos, SEEK_SET);
                    if (read(fd, buffer, nbytes) != nbytes)
                        return -1;
                    lseek(fd, wr_pos, SEEK_SET);
                    if (write(fd, buffer, nbytes) != nbytes)
                        return -1;
                    tbytes -= nbytes;
                    rd_pos += nbytes;
                    wr_pos += nbytes;
                }
                ftruncate(fd, file_size - dellen);
            }
            else
                ftruncate(fd, offset);
        }
        rc = 0;
    }
    return rc;
}

#if !defined DO_NOT_TEST

#include "stderr.h"
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

typedef struct Data
{
    size_t      offset;
    size_t      length;
} Data;

static const Data delete_ops[] =
{
    {    2,    3 },
    {   84,   33 },
    {  212,  333 },
    { 1022, 1233 },
    { 1024, 2048 },
};
enum { NUM_DELETE = sizeof(delete_ops) / sizeof(delete_ops[0]) };

static void make_data_file(const char *name)
{
    FILE *fp = fopen(name, "w");
    if (fp == 0)
        err_syserr("failed to open '%s' for writing: ", name);
    printf("%s:\n", name);
    char format[] = "%.3d: ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234 abcdefghijklmnopqrstuvwxyz\n";
    for (int i = 0; i < 64; i++)
    {
        fprintf(fp, format, i);
    }
    fclose(fp);
}

int main(int argc, char **argv)
{
    if (argc > 0)
        err_setarg0(argv[0]);
    const char filename[] = "test.dat";

    make_data_file(filename);
    printf("BUFFERSIZE = %d\n", BUFFERSIZE);

    int fd = open(filename, O_RDWR);
    if (fd > 0)
    {
        for (int i = 0; i < NUM_DELETE; i++)
        {
            printf("Delete: offset %4zu, length %4zu\n", delete_ops[i].offset, delete_ops[i].length);
            if (shrink_file_and_delete(fd, delete_ops[i].offset, delete_ops[i].length) != 0)
                break;
            lseek(fd, 0, SEEK_SET);
            char buffer[BUFFERSIZE];
            ssize_t nbytes;
            size_t  tbytes = 0;
            char lastbyte = '\n';
            while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0)
            {
                printf("%.*s", (int)nbytes, buffer);
                lastbyte = buffer[nbytes-1];
                tbytes += nbytes;
            }
            if (lastbyte != '\n')
                putchar('\n');
            printf("Total bytes: %zu\n", tbytes);
        }
        close(fd);
    }
    return(0);
}

#endif /* !DO_NOT_TEST */

非标准头文件及其对应的源文件可在我的SOQ(Stack Overflow Questions)GitHub代码库中找到,位于src/libsoq子目录下的posixver.hstderr.cstderr.h文件。

1
你可以先将文件内容读入缓冲区。然后,你可以从这个缓冲区解析并插入数据到某个数据结构中,比如一个结构体数组。一旦你拥有了这个数据结构,你就可以将过滤后的内容重写回文件。
以下是我之前编写的一些示例代码(稍作调整),它与你想要的功能类似。它以两个命令行参数作为输入。第一个参数是要读取的文件,第二个参数是要排除的数据,在你的情况下是“chair”值。如果你愿意,你可以修改它以从标准输入读取。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define START_SIZE 10;
#define BASE 10

typedef struct {
    char *chair;
    int number;
    char *name;
} data_t;

typedef struct {
    data_t *data;
    size_t n;
} file_data_t;

char *get_file_contents(const char *);
file_data_t *insert_data(char *, const char *);

int main(int argc, char *argv[]) {

    // Check arguments
    if (argc != 3) {
        fprintf(stderr, "Usage: ./deletefile [file] [chair]\n");
        exit(EXIT_FAILURE);
    }

    // Get buffer
    char *buffer = get_file_contents(argv[1]);

    // Get structure holding data
    file_data_t *file_data = insert_data(buffer, argv[2]);

    // Free buffer
    free(buffer);

    // Open file to write to
    FILE *fp = fopen(argv[1], "w");
    if (fp == NULL) {
        fprintf(stderr, "Count not open file\n");
        exit(EXIT_FAILURE);
    }

    // Write to file, and free contents
    for (size_t i = 0; i < file_data->n; i++) {
        fprintf(fp, "-.\nChair: %s\nNumber: %d\nName: %s\n",
                    file_data->data[i].chair,
                    file_data->data[i].number,
                    file_data->data[i].name);

        free(file_data->data[i].chair);
        free(file_data->data[i].name);
    }

    // Free everything else
    free(file_data->data);
    free(file_data);

    fclose(fp);

    return EXIT_SUCCESS;
}

file_data_t *insert_data(char *buffer, const char *dont_keep) {
    size_t buffsize = START_SIZE;
    const char *delim_section = "-.", *delim_line = "\n";
    const char delim_colon = ':';
    char *token = NULL;
    char *rest = buffer;
    size_t count = 0;

    // Create main structure
    file_data_t *file_data = malloc(sizeof *file_data);
    if (file_data == NULL) {
        fprintf(stderr, "Could not allocate file data\n");
        exit(EXIT_FAILURE);
    }

    // Allocate data elements
    file_data->data = malloc(buffsize * sizeof *file_data->data);
    if (file_data->data == NULL) {
        fprintf(stderr, "Could not allocate %zu bytes for data\n", buffsize);
        exit(EXIT_FAILURE);
    }

    while ((token = strtok_r(rest, delim_section, &rest)) != NULL) {

        // Reallocate data if necessary
        if (count == buffsize) {
            buffsize *= 2;
            void *ptr = realloc(file_data->data, buffsize * sizeof *file_data->data);
            if (ptr == NULL) {
                fprintf(stderr, "Could not reallocate %zu bytes for buffer\n", buffsize);
                exit(EXIT_FAILURE);
            }

            file_data->data = ptr;
        }

        char *saveptr = NULL, *endptr = NULL;

        // Parse chair
        char *chair = strtok_r(token, delim_line, &saveptr);
        char *chair_value = strchr(chair, delim_colon);
        chair_value += 2;

        // If chair value is not the same as dont_keep, proceed
        if (strcmp(chair_value, dont_keep) != 0) {

            // Copy chair value over
            file_data->data[count].chair = strdup(chair_value);
            if (file_data->data[count].chair == NULL) {
                fprintf(stderr, "Could not copy chair buffer\n");
                exit(EXIT_FAILURE);
            }

            // Parse number
            char *number = strtok_r(NULL, delim_line, &saveptr);
            char *number_value = strchr(number, delim_colon);
            number_value += 2;

            // Convert number to integer
            long val = strtol(number_value, &endptr, BASE);

            // Didnt find a value number
            if (endptr == number_value || *endptr  != '\0') {
                fprintf(stderr, "Count not parse number\n");
                exit(EXIT_FAILURE);
            }

            // Add number value
            file_data->data[count].number = val;

            // Parse name
            char *name = strtok_r(NULL, delim_line, &saveptr);
            char *name_value = strchr(name, delim_colon);
            name_value += 2;

            // Copy name over
            file_data->data[count].name = strdup(name_value);
            if (file_data->data[count].name == NULL) {
                fprintf(stderr, "Coul not copy name buffer\n");
                exit(EXIT_FAILURE);
            }

            // Increment count
            count++;
        }
    }

    file_data->n = count;

    return file_data;
}

char *get_file_contents(const char *path) {

    // Open file
    FILE *fp = fopen(path, "r");
    if (fp == NULL) {
        fprintf(stderr, "Failed to open %s\n", path);
        exit(EXIT_FAILURE);
    }

    // Go to end of file
    int end = fseek(fp, 0L, SEEK_END);
    if (end != 0) {
        fprintf(stderr, "Could not go to end of file\n");
        exit(EXIT_FAILURE);
    }

    // Get size of file
    long buffsize = ftell(fp);
    if (buffsize == -1) {
        fprintf(stderr, "Count not get size of file\n");
        exit(EXIT_FAILURE);
    }

    // Allocate buffer
    char *buffer = malloc(buffsize + 1);
    if (buffer == NULL) {
        fprintf(stderr, "Could not allocate %ld bytes for buffer\n", buffsize);
        exit(EXIT_FAILURE);
    }

    // Go back to start of file
    int start = fseek(fp, 0L, SEEK_SET);
    if (start != 0) {
        fprintf(stderr, "Could not go to start of file\n");
        exit(EXIT_FAILURE);
    }

    // Read contents of file
    size_t newlen = fread(buffer, 1, buffsize, fp);
    if (ferror(fp) != 0) {
        fprintf(stderr, "Error reading contents of file into buffer\n");
        exit(EXIT_FAILURE);
    }

    fclose(fp);

    // Null terminate buffer
    buffer[newlen++] = '\0';

    return buffer;
}

输出:

$ cat file.txt
-.
Chair: A23
Number: 123
Name: Joshua
-.
Chair: B12
Number: 512
Name: Marcus
-.
Chair: C2
Number: 1
Name: Drake
$ gcc -Wall -Wextra -o deletefile deletefile.c
$ ./deletefile file.txt B12
$ cat file.txt
-.
Chair: A23
Number: 123
Name: Joshua
-.
Chair: C2
Number: 1
Name: Drake

注意:上述代码并不是完成此任务的最佳方式,可以进行改进。您可以将其用作基础,并进行改进。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接