0

Does memmove work on file pointer data?

I am trying to remove a line from a C file. I am trying to use memmove to make this more efficient than the internet's recommendation to create a duplicate file and overwrite it. I have debugged and I can't figure out why this code isn't working. I am asking for input. The logic is a for loop. Inside the loop, I have logic to do a memmove but it doesn't seem effective.

nt RemoveRow(int   iRowNum)
  {
    char sReplaceLineStart[m_MaxSizeRow]={0};
    char sTemp[m_MaxSizeRow]          ={0};

    size_t RemovalLength = 0;
    GoToBeginningOfFile();
    for(int i =0;i<m_iNumberOfRows;i++)
    {
      if(i == iRowNum)
      {
        // Line to remove
        fgets(m_sRemovalRow,m_MaxSizeRow,pFile);
      }

      if(m_sRemovalRow == NULL)
      {    
        // Were removing the last line
        // just make it null
      memset(m_sRemovalRow,0,sizeof(m_MaxSizeRow));
      }       

      }
      else if(i==iRowNum+1)
      {
        // replace removal line with this.
        RemovalLength+=strlen(sTemp); 
        fgets(sReplaceLineStart, m_MaxSizeRow, pFile);
      }
      else if(i>iRowNum)      {
        // start line to replace with
        RemovalLength+=strlen(sTemp); 
        fgets(sTemp, m_MaxSizeRow, pFile);
      }
      else
      {
        // were trying to get to the removal line
        fgets(m_sCurrentRow, m_MaxSizeRow, pFile);
        printf("(not at del row yet)iRow(%d)<iRowNum(%d) %s\n",
               i,
               m_iNumberOfRows,
               m_sCurrentRow);
      }
    }
   
    
    {  
      memmove(m_sRemovalRow,
              sReplaceLineStart,
              RemovalLength);
    }

    return 1;
  }

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
sasukenebe
  • 21
  • 3
  • 4
    No, `memmove()` does not work on "`FILE *` data". And you are unlikely to be quicker than the internet's recommendation. You could look at https://stackoverflow.com/questions/50996698 for an example of deleting text in the middle of a file, and https://stackoverflow.com/questions/10467711 for an example of inserting text in the middle of a file, but any solutions are not going to be quicker than copying the file minus the unwanted material. – Jonathan Leffler Jul 05 '22 at 03:05
  • 1
    Note that when deleting data from the file, you will also need to apply an operation analogous to POSIX [`ftruncate()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html) to remove the trailing data that is no longer wanted. – Jonathan Leffler Jul 05 '22 at 03:13
  • 1
    Also, you could consider whether using a memory-mapped file would help — you could at least use `memmove()` on the mapped memory, but you'd still need to reduce the length of the file. – Jonathan Leffler Jul 05 '22 at 03:23
  • 1
    The code isn't complete enough to see what you're trying to do. E.g. there's no declaration for `m_sRemovalRow`. C source files are not normally very big. Consider using `fread()` to get the whole thing into a buffer. Scan the buffer get the byte offsets of the deleted line's start and end. Then close the file, reopen it for writing, and use two `fwrite()`s to emit first the chunk up to the deleted line and then the chunk after. This will be simple and fast. – Gene Jul 05 '22 at 03:37
  • Thanks for the responses, I couldnt find the response to this basic question, maybe im a dumb guy idk. Thanks. I guess now im trying to figure out the best alternative to achieve this, in terms of trying to optimize timing, I have a CSV file of 3million+ lines and I would like to be able to edit it. – sasukenebe Jul 05 '22 at 03:38
  • @Gene sorry about that m_sRemovalRow is a char* of a MAX_ROW_SIZE for the class, maybe bad design idk,. Illl try to update that to clarify – sasukenebe Jul 05 '22 at 03:39
  • 1
    If you are looking for a more efficient solution than writing a new .csv file whenever you make changes to the file, then .csv is not the file format that you should be using. You should rather be using a more efficient file format that is also used by database systems. Instead of programming this yourself, it would probably be simpler to use an existing database system, such as [SQLite](https://en.wikipedia.org/wiki/SQLite). – Andreas Wenzel Jul 05 '22 at 04:48
  • Besides Lundin's answer which clarifies that you cannot access memory pointed to by a `FILE` pointer, you must also be aware that the content `FILE` structure is not related to the file content itself. – Gerhardh Jul 05 '22 at 07:13
  • If you want to manipulate a file in the memory space, you can use [`mmap()`](https://man7.org/linux/man-pages/man2/mmap.2.html) – Elzaidir Jul 05 '22 at 12:57

1 Answers1

0

FILE is a so-called opaque type, meaning that the application programmer is purposely locked out of its internals as per design - private encapsulation.

Generally one would create an opaque type using the concept of forward declaration, like this:

// stdio.h
typedef struct FILE FILE;

And then inside the private library:

// stdio.c - not accessible by the application programmer
struct FILE
{
  // internals
};

Since FILE was forward declared and we only have access to the header, FILE is now an incomplete type, meaning we can't declare an instance of that type, access its members nor pass it to sizeof etc. We can only access it through the API which does know the internals. Since C allows us to declare a pointer to an incomplete type, the API will use FILE* like fopen does.

However, the implementation of the standard library isn't required to implement FILE like this - the option is simply there. So depending on the implementation of the standard library, we may or may not be able to create an instance of a FILE objet and perhaps even access its internals. But that's all in the realm of non-standard language extensions and such code would be non-portable.

Lundin
  • 195,001
  • 40
  • 254
  • 396