简体   繁体   English

如何从C中的文本文件中删除特定行?

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

Example:例子:

George 50 40乔治 50 40
Lime 30 20石灰 30 20
Karen 10 60凯伦 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乔治 50 40
Karen 10 60凯伦 10 60

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/参考 - http://www.sanfoundry.com/c-program-delete-line-text-file/

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.编辑:上述解决方案适用于小文件,但根据注释,它不适用于大文件,因此这里有一个替代解决方案(GCC C99),它读取整个文件,找到名称然后在此之后移动行在缓冲区中向前行。

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

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 ).您可以使用r+打开原始文件并将其内容存储到数组中(按照以下步骤操作)。 Then you can use a for loop to scan the array for the line you want to skip, and delete that line.然后您可以使用 for 循环扫描数组中要跳过的行,并删除该行。 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.然后,您可以使用fseek(filename,0,SEEK_SET) (重置文件的位置指示符)并使用 for 循环和 fprintf 将内容从修改后的数组复制到文件中来覆盖文件的内容。 (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.) (但是,使用此方法,您需要在最后一个 for 循环中输入一个额外的空行以覆盖原始文件的最后一行。)

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 处的元素,我们将所有元素从 i+1 向左移动一步,然后逻辑上减小数组的大小。

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.最后 ftruncate() 用于减小文件的大小,从而删除文件的末尾部分。此代码也适用于大文件,因为在任何时候都只有一个字节被存储在内存中。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM