Is there a way to delete or replace a character in a text file in c

517 views Asked by At

I need to make a C program that removes empty lines as homework since we didn't study a way to removing characters from files my first attempt was to overwrite all the characters but both fprintf and fputc inserts characters.

#include <stdio.h>

#define MAX_SIZE 1000

int main() {
    FILE *fp = fopen("sortie.txt", "r+");
    int off = 0;
    for (char c1 = '\n', c2;;) {
        if (((c2 = fgetc(fp)) == '\n') && (c1 == '\n')) {
            off++;
            continue;
        }
        if (c2 == EOF) {
            fseek(fp, -off ,SEEK_CUR);
            fputc(EOF, fp);
            break;
        }
        //if(!off)continue;

        fseek(fp, -off, SEEK_CUR);
        fprintf(fp, "%c", c1 = c2);

        fseek(fp, off, SEEK_CUR);
    }
    fclose(fp);

    return 0;
}

second attempt was to replace them with '\0'

#include <stdio.h>

int main() {
    FILE *fp = fopen("sortie.txt", "r+");
    for (char c1 = '\n', c2;;) {
        if (((c2 = fgetc(fp)) == '\n') && (c1 == '\n')) {
            fseek(fp, -1, SEEK_CUR);
            fputc('\0', fp);
            fseek(fp, 1, SEEK_CUR);
        }
    }
    fclose(fp);

    return 0;
}

none worked

overwriting characters and replacing with 0

1

There are 1 answers

4
chqrlie On

In order to remove characters from a file, the length of the file must be reduced. There is no portable way to truncate a file, except to a size of 0.

A portable way to achieve your goal is to read the file contents in memory, perform whatever processing in memory and write back the new contents into a newly created file with the same name.

Here is an example with error checking:

#include <stdlib.h>

int main(void) {
    FILE *fp = fopen("sortie.txt", "r");
    if (fp == NULL) {
        perror("cannot open sortie.txt for reading");
        return 1;
    }
    char *buffer = NULL;
    size_t length = 0, size = 0;
    int c, last = '\n';
    while ((c = getc(fp)) != EOF) {
        if (c != '\n' || last != '\n') {
            if (length >= size) {
                size_t new_size = size + size / 2 + 32;
                char *new_buffer = realloc(buffer, new_size);
                if (new_buffer == NULL) {
                    perror("cannot reallocate buffer");
                    free(buffer);
                    fclose(fp);
                    return 1;
                }
                buffer = new_buffer;
                size = new_size;
            }
            buffer[length++] = (char)c;
        }
        last = c;
    }
    fclose(fp);
    fp = fopen("sortie.txt", "w");
    if (fp == NULL) {
        perror("cannot open sortie.txt for writing");
        free(buffer);
        return 1;
    }
    int status = 0;
    size_t written = fwrite(buffer, 1, length, fp);
    free(buffer);
    if (written != length) {
        fprintf(stderr, "error writing to sortie.txt: %zu written, expected %zu\n",
                written, length);
        status = 1;
    }
    if (fclose(fp)) {
        perror("error closing sortie.txt");
        status = 1;
    }
    return status;
}

The problem with this approach is the potential data loss if the program is interrupted at the wrong time before the updated contents is fully written.

For an alternative, more classic approach, which may handle files too large to fit in memory, you can write the modified contents to a new file, once this process is complete, you can remove the original file and rename the new file to the original name.

Here is an example of this latter approach:

#include <stdio.h>

int main(void) {
    FILE *fp = fopen("sortie.txt", "r");
    if (fp == NULL) {
        perror("cannot open sortie.txt");
        return 1;
    }
    FILE *f2 = fopen("sortie-new.txt", "w");
    if (fp2 == NULL) {
        perror("cannot open sortie-new.txt");
        return 1;
    }
    int c, last = '\n';
    while ((c = getc(fp)) != EOF) { 
        if (c != '\n' || last != '\n')
            putc(c, fp2);
        last = c;
    }
    fclose(fp);
    if (fclose(fp2)) {
        perror("error writing to sortie-new.txt");
        remove("sortie-new.txt");
        return 1;
    }
    if (remove("sortie.txt")) {
        perror("cannot remove sortie.txt");
        return 1;
    }
    if (rename("sortie-new.txt", "sortie.txt")) {
        perror("cannot rename sortie-new.txt");
        return 1;
    }
    return 0;
}