如何在不创建额外临时文件的情况下从文件中删除标题?

3
我正在为运行Linux的嵌入式系统开发应用程序。
在我的情况下,我有一个相当大的文件(与系统能力相比),作为输入。该文件有一个小的头部,其大小仅为几百字节。在我的应用程序中,我需要从文件中删除该头部,使文件没有头部并只包含相关数据。通常,我会按照以下方式实现(伪代码):
char *input_file  = "big_input.bin";
char *tmp_file1 = "header.bin";
char *tmp_file2 = "data.bin";
/* Copy the content of header from input file to tmp_file1 */ 
_copy_header(tmp_file1, input_file); 
/* Copy the data from input file to tmp_file2 */ 
_copy_data(tmp_file2, input_file);
/* Rename temp file to input file */
unlink(input_file);
rename(tmp_file2, input_file);

这种方法的问题是它创建了一个临时文件 tmp_file2,其大小几乎与输入文件一样大(因为头部很小)。在我的系统中,所有东西都存储在内存中,而内存非常有限。创建一个大的临时文件会导致 out-of-memory 错误。

那么我该如何避免创建大的临时文件?

移动而非复制。 - alk
3
将其 mmap。移动它。截断它。如果没有足够的内存来将整个文件映射到内存,请分段进行操作。 - n. m.
3个回答

6

打开同一文件两次,一次用于读取,一次用于写入。

将读指针定位到头部之后。

从读指针处读取数据并写入到写指针处。

确保每次读取和写入的大小不超过头部的大小。

在文件末尾截断头部的大小。


3
假设您事先知道标头的确切大小,可以使用以下方式完成:
#define HEADER_SIZE 128

// size the buffer as appropriate for you RAM limits
char buffer[ 4096 ];
int fd = open( filename, O_RDWR );
size_t totalBytes = 0UL;
for ( ;; )
{
    ssize_t bytes_read = pread( fd, buffer,
        sizeof( buffer ), totalBytes + HEADER_SIZE );
    if ( bytes_read <= 0L )
    {
        break;
    }
    pwrite( fd, buffer, bytes_read, totalBytes );
    total_bytes += bytes_read;
}

ftruncate( fd, total_bytes );

close( fd );

您需要添加正确的头文件并进行一些错误检查。


1
在你的情况下,你可以:
  • 以读写方式打开文件
  • 从你的偏移量逐个字符地读取并写入开头,循环直到文件末尾(听起来不够高效,但你在RAM驱动器上,并且这很简单。更快的方法可能意味着读取更多字节,可能更难实现,并且速度增益必须测量)
  • 最后,你可以使用truncateftruncate截断文件,如此处所述:如何在C中截断文件?

4
逐个字符地阅读可能并不是最有效的方式。 - Jabberwocky
从一个能够工作的RAM系统中获取数据是至关重要的,而内存大小在这个问题中也是至关重要的。 - Jean-François Fabre
1
头部很小,所以我想他可以有一个相同长度的读取缓冲区。 - Jabberwocky
或者您可以分配一个“适当大小”的临时缓冲区(比如一页长),每次读写那么多字节。另一种方法(如果支持虚拟内存)是将文件映射到虚拟地址,使用memmove移动数据,然后再使用munmap取消映射。 - Ian Abbott
头文件很小,我提前知道它的大小。所以我可以按照@MichaelWalz的建议读取一个长度为头文件的缓冲区。 - theman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接