Hansen Derrick
Hansen Derrick

Reputation: 151

How do I delete a specific line from text file in C?

Example:

George 50 40
Lime 30 20
Karen 10 60

do {
    printf("\nInput player name:[1..10] ");
    fgets(name,10,stdin);
}

Input name: Lime

Then the text file will be:

George 50 40
Karen 10 60

Upvotes: 11

Views: 87150

Answers (4)

Vishwesh Pujari
Vishwesh Pujari

Reputation: 31

Consider a file like an array. If we want to delete an element at index i of an array, we shift all elements from i+1 one step towards left and then logically reduce the size of array.

I am applying same princliple on files. In the end ftruncate() is used to reduce the size of file and hence removing the end parts of the file.This code works fine for large files too, as at any time only one byte is being stored in memory.

#include<errno.h>
#include<unistd.h>
#include<stdio.h>
/*
 * Description: fdelete() deletes 'bytes' bytes of data from the stream pointed to by fp. 
 *              bytes will be deleted from the CURRENT FILE POSITION.
 *              File must be opened in read + write mode while passing file pointer 
 *              to this function.
 *              File position before calling the function and after the 
 *              function returns will be the same.
 * Return Values: returns 0 on success and errno on failure. Kindly use perror("") 
 *                to print error if non-0 return value returned.
 */
int fdelete(FILE* fp, int bytes) {
    
    // to store each byte/char from file
    char byte;
    long readPos = ftell(fp) + bytes, writePos = ftell(fp), startingPos = writePos;
    // start reading from the position which comes after the bytes to be deleted
    fseek(fp, readPos, SEEK_SET);
    while (fread(&byte, sizeof(byte), 1, fp)) {
        // modify readPos as we have read right now
        readPos = ftell(fp);
        // set file position to writePos as we are going to write now
        fseek(fp, writePos, SEEK_SET);
        
        // if file doesn't have write permission
        if (fwrite(&byte, sizeof(byte), 1, fp) == 0) 
            return errno;
        // modify writePos as we have written right now
        writePos = ftell(fp);
        // set file position for reading
        fseek(fp, readPos, SEEK_SET);
    }

    // truncate file size to remove the unnecessary ending bytes
    ftruncate(fileno(fp), writePos);
    // reset file position to the same position that we got when function was called.
    fseek(fp, startingPos, SEEK_SET); 
    return 0;
}

Upvotes: 3

Andrew
Andrew

Reputation: 301

You don't need to create a new file. You can open the original file with r+ and store its contents into an array (following these steps). Then you can use a for loop to scan the array for the line you want to skip, and delete that line. Then you can overwrite the contents of the file using fseek(filename,0,SEEK_SET) (to reset the position indicator of the file) and using a for loop and fprintf to copy the contents from the modified array to the file. (However, with this method you will need to enter an extra blank line in the last for loop to overwrite the last line of the original file.)

Upvotes: 1

user3115056
user3115056

Reputation: 1280

Try this:

 /* C Program Delete a specific Line from a Text File
 */
#include <stdio.h>

int main()
{
    FILE *fileptr1, *fileptr2;
    char filename[40];
    char ch;
    int delete_line, temp = 1;

    printf("Enter file name: ");
    scanf("%s", filename);
    //open file in read mode
    fileptr1 = fopen(filename, "r");
    ch = getc(fileptr1);
   while (ch != EOF)
    {
        printf("%c", ch);
        ch = getc(fileptr1);
    }
    //rewind
    rewind(fileptr1);
    printf(" \n Enter line number of the line to be deleted:");
    scanf("%d", &delete_line);
    //open new file in write mode
    fileptr2 = fopen("replica.c", "w");
    ch = 'A';
    while (ch != EOF)
    {
        ch = getc(fileptr1);
        //except the line to be deleted
        if (temp != delete_line)
        {
            //copy all lines in file replica.c
            putc(ch, fileptr2);
        }
        if (ch == '\n')
        {
            temp++;
        }
    }
    fclose(fileptr1);
    fclose(fileptr2);
    remove(filename);
    //rename the file replica.c to original name
    rename("replica.c", filename);
    printf("\n The contents of file after being modified are as follows:\n");
    fileptr1 = fopen(filename, "r");
    ch = getc(fileptr1);
    while (ch != EOF)
    {
        printf("%c", ch);
        ch = getc(fileptr1);
    }
    fclose(fileptr1);
    return 0;
}

Reference - http://www.sanfoundry.com/c-program-delete-line-text-file/

Upvotes: 5

AndersK
AndersK

Reputation: 36082

There are several ways you can delete a line, one simple method is to open two files, one in and one out.

then copy line by line and skip the line you want to delete after you are done, delete the old file and rename the new one to the old name.

fopen()
fgets()
fputs()
rename()
unlink()

EDIT: the above solution would work fine with a small file but as by comment it is not suitable for a large file so here comes an alternative solution (GCC C99) which reads in the whole file, finds the name then moves the lines after that line forward in the buffer.

#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdbool.h>

static size_t deleteLine( char*, size_t, const char* );

int main(int argc, char* argv[])
{
  char file[] = "yourfile.txt";

  if ( --argc )
  { 
    struct stat st;
    if ( stat( file, &st ) != -1 )
    {
      // open the file in binary format
      FILE* fp = fopen( file, "rb" );
      if ( fp != NULL )
      {
        // allocate memory to hold file
        char* buffer = malloc( st.st_size ); 

        // read the file into a buffer
        if ( fread(buffer, 1, st.st_size, fp) == st.st_size)
        {
          fclose(fp);

          size_t newSize = deleteLine( buffer, st.st_size, argv[1] );

          fp = fopen( file, "wb" );
          if ( fp != NULL )
          {
            fwrite(buffer, 1, newSize, fp);
            fclose(fp);
          }
          else
          {
            perror(file);
          }
        }
        free(buffer);
      }
      else
      {
        perror(file);
      }
    }
    else
    {
      printf( "did not find %s", file );
    }
  }
  return 0;
}

static size_t deleteLine( char* buffer, size_t size, const char* playerName )
{
  // file format assumed to be as specified in the question i.e. name{space}somevalue{space}someothervalue\n
  // find playerName
  char* p = buffer; 
  bool done = false;
  size_t len = strlen(playerName);
  size_t newSize = 0;
  do
  {
    char* q = strchr( p, *playerName ); // look for first letter in playerName
    if ( q != NULL )
    {
      if ( strncmp( q, playerName, len ) == 0 ) // found name?
      {
        size_t lineSize = 1; // include \n already in line size

        // count number of characters the line has.
        for ( char* line = q; *line != '\n'; ++line) 
        {
          ++lineSize;
        }

        // calculate length left after line by subtracting offsets
        size_t restSize = (size_t)((buffer + size) - (q + lineSize));

        // move block with next line forward
        memmove( q, q + lineSize, restSize );

        // calculate new size
        newSize = size - lineSize;
        done = true;
      }
      else
      {
        p = q + 1; // continue search
      }
    }
    else
    {
      puts( "no such name" );
      done = true;
    }
  }
  while (!done);

  return newSize;
}

Upvotes: 4

Related Questions