是否可以从文件的开头删除字节?

问题描述:

我知道我可以有效地截断文件并从文件末尾删除字节。是否可以从文件的开头删除字节?

是否有相应的有效方法通过从文件开头删除内容到文件中间点来截断文件?

当我读到这个问题时,你需要从文件的开始处移除文件中的内容。换句话说,您希望在文件开始时删除内容并将其余内容向下移动。

这是不可能的。您只能从最后截断文件,而不能从头开始。您需要将剩余内容复制到新文件中,或者将其复制到同一个文件中。

但是,你这样做没有捷径有效的方式来做到这一点。您必须复制数据,例如@kobik描述的那样。

雷蒙德陈写了一篇关于该主题的好文章:How do I delete bytes from the beginning of a file?


只是为了好玩,这里有一个简单的实现基于流的方法从文件中的任何位置删除内容。你可以使用这个读/写文件流。我没有测试过代码,我会把它留给你!

procedure DeleteFromStream(Stream: TStream; Start, Length: Int64); 
var 
    Buffer: Pointer; 
    BufferSize: Integer; 
    BytesToRead: Int64; 
    BytesRemaining: Int64; 
    SourcePos, DestPos: Int64; 
begin 
    SourcePos := Start+Length; 
    DestPos := Start; 
    BytesRemaining := Stream.Size-SourcePos; 
    BufferSize := Min(BytesRemaining, 1024*1024*16);//no bigger than 16MB 
    GetMem(Buffer, BufferSize); 
    try 
    while BytesRemaining>0 do begin 
     BytesToRead := Min(BufferSize, BytesRemaining); 
     Stream.Position := SourcePos; 
     Stream.ReadBuffer(Buffer^, BytesToRead); 
     Stream.Position := DestPos; 
     Stream.WriteBuffer(Buffer^, BytesToRead); 
     inc(SourcePos, BytesToRead); 
     inc(DestPos, BytesToRead); 
     dec(BytesRemaining, BytesToRead); 
    end; 
    Stream.Size := DestPos; 
    finally 
    FreeMem(Buffer); 
    end; 
end; 
+0

实际上,引用稀疏文件相当具有误导性。这不是关于在偏移量0处删除** N个字节,而是关于**设置**将0..N-1个字节的区域设置为零(或任何预设的填充字节值) – OnTheFly 2012-03-07 12:59:26

+2

@user对不起,什么是误导? – 2012-03-07 13:00:45

+1

你太棒了!谢谢你,大卫,那样做! – TheDude 2012-03-07 19:21:47

一个非常简单的解决方案将是数据的(移动)块从移位“目标位置偏移” 朝BOF,然后修剪(截断)剩菜:

-------------------------- 
|******|xxxxxx|yyyyyy|zzz| 
-------------------------- 
BOF <-^ (target position offset) 


-------------------------- 
|xxxxxx|yyyyyy|zzz|******| 
-------------------------- 
       ^EOF 

由于@大卫发布基于TStream代码,这里是根据“低水平” I/O帕斯卡尔风格的一些代码:

function FileDeleteFromBOF(const FileName: string; const Offset: Cardinal): Boolean; 
var 
    Buf: Pointer; 
    BufSize, FSize, 
    NumRead, NumWrite, 
    OffsetFrom, OffsetTo: Cardinal; 
    F: file; 
begin 
    {$IOCHECKS OFF} 
    Result := False; 
    AssignFile(F, FileName); 
    try 
    FileMode := 2; // Read/Write 
    Reset(F, 1); // Record size = 1 
    FSize := FileSize(F); 
    if (IOResult <> 0) or (Offset >= FSize) then Exit; 
    BufSize := Min(Offset, 1024 * 64); // Max 64k - This value could be optimized 
    GetMem(Buf, BufSize); 
    try 
     OffsetFrom := Offset; 
     OffsetTo := 0; 
     repeat 
     Seek(F, OffsetFrom); 
     BlockRead(F, Buf^, BufSize, NumRead); 
     if NumRead = 0 then Break; 
     Seek(F, OffsetTo); 
     BlockWrite(F, Buf^, NumRead, NumWrite); 
     Inc(OffsetFrom, NumWrite); 
     Inc(OffsetTo, NumWrite); 
     until (NumRead = 0) or (NumWrite <> NumRead) or (OffsetFrom >= FSize); 
     // Truncate and set to EOF 
     Seek(F, FSize - Offset); 
     Truncate(F); 
     Result := IOResult = 0; 
    finally 
     FreeMem(Buf); 
    end; 
    finally 
    CloseFile(F); 
    end; 
end; 
+2

**非常聪明的想法Kobik,但我可以在实践中做到这一点吗? (在德尔福我的意思) – TheDude 2012-03-07 13:47:22

+1

当然你可以:'BlockRead' /'Seek' /'BlockWrite' ...或者直接使用'TFileStream'。 – kobik 2012-03-07 14:02:37

+0

@Gdhami这是你所能做的。您必须复制文件的其余内容。重点在于在文件的开始部分截断时不会有微不足道的有效方式,与文件末尾的截断效果截然不同。 – 2012-03-07 14:40:59