Delete part of a file in C

心已入冬 提交于 2020-01-04 05:48:21

问题


How can I create a function to delete certain part of a file? For example, the file is:

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

If the input is

B12

Then the file shall become

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

I need this function for my program to work, but I have no idea how can I do that.


回答1:


Open a new file in the same directory as the original file. Write to that file what you want to replace the contents of the original file. Close the new file. Rename the new file on top of the original file.

But perhaps you should consider using a database instead of a text file.




回答2:


You could first read the file contents into a buffer. Then you could parse and insert data from this buffer into some data structure, such as an array of structs. Once you have that, you can just rewrite the filtered contents back to the file.

Below is some sample code(tweaked) that I wrote a while ago that does something similar to what you want. It takes in 2 command line arguements as input. The first is the file to read from, and the second one is the data to not include, in your case the chair value. You can amend it to read from stdin if you wanted to.

Code:

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

Output:

$ 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

Note: The above code is not the best way to do this task, and can certainly be improved. You can use this as a base, and improve upon it.




回答3:


Taking the question at face value, consider using a combination of:

  1. The converse of Write in the middle of a binary file without overwriting any existing content
  2. How to truncate a file in C?

You'd use the converse of step 1 to copy the material from the end of the file (after the part to be deleted) over the part to be deleted, and then use step 2 to set the file size to the new value.

Or, probably more simply, copy the material before and after the part to be deleted into a new file, and then move the (content of the) new file in place of the old.


Relevant code:

#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 */

The non-standard headers and corresponding source files are available in my SOQ (Stack Overflow Questions) repository on GitHub as files posixver.h, stderr.c and stderr.h in the src/libsoq sub-directory.



来源:https://stackoverflow.com/questions/50996698/delete-part-of-a-file-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!