¿Cómo puedo crear una función para eliminar cierta parte de un archivo? Por ejemplo, el archivo es:
-.
Presidente: A23
Número: 123
Nombre: Josué
-.
Presidente: B12
Número: 512
Nombre: Marco
-.
Presidente: C2
Numero 1
Nombre: Drake
Si la entrada es
B12
Entonces el archivo se convertirá
-.
Presidente: A23
Número: 123
Nombre: Josué
-.
Presidente: C2
Numero 1
Nombre: Drake
Necesito esta función para que mi programa funcione, pero no tengo idea de cómo puedo hacerlo.
Abra un nuevo archivo en el mismo directorio que el archivo original. Escriba en ese archivo lo que desea reemplazar el contenido del archivo original. Cierra el nuevo archivo. Cambie el nombre del nuevo archivo encima del archivo original.
Pero tal vez debería considerar usar una base de datos en lugar de un archivo de texto.
Primero podría leer el contenido del archivo en un búfer. Luego, podría analizar e insertar datos de este búfer en alguna estructura de datos, como una matriz de estructuras. Una vez que tenga eso, puede volver a escribir los contenidos filtrados en el archivo.
A continuación se muestra un código de muestra (modificado) que escribí hace un tiempo que hace algo similar a lo que desea. Toma 2 argumentos de línea de comando como entrada. El primero es el archivo para leer, y el segundo son los datos para no incluir, en su caso el valor de la silla. Puedes modificarlo para leer desde stdin
Si quieres.
Código:
#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;
}
Producción:
$ 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
Nota: El código anterior no es la mejor manera de realizar esta tarea y ciertamente se puede mejorar. Puede usar esto como base y mejorarlo.

jonathan leffler
Tomando la pregunta al pie de la letra, considere usar una combinación de:
- Lo contrario de Escribir en medio de un archivo binario sin sobrescribir ningún contenido existente
- ¿Cómo truncar un archivo en C?
Usaría lo contrario del paso 1 para copiar el material desde el final del archivo (después de la parte que se eliminará) sobre la parte que se eliminará, y luego usaría el paso 2 para establecer el tamaño del archivo en el nuevo valor.
O, probablemente más simple, copie el material antes y después de la parte que se eliminará en un archivo nuevo y luego mueva el (contenido del) archivo nuevo en lugar del anterior.
Código relevante:
#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 */
Los encabezados no estándar y los archivos fuente correspondientes están disponibles en mi SOQ (Preguntas de desbordamiento de pila) repositorio en GitHub como archivos posixver.h
, stderr.c
y stderr.h
en el src/libsoq subdirectorio.