Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
470 Users Online NOW
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi







Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (3)


Splitting Files (Small Code & Blazing Fast)Format this article printer-friendly!Bookmark function is only available for registered users!
Splitting a big file into smaller pieces of specified size.
Product:
Delphi 3.x (or higher)
Category:
Algorithm
Skill Level:
Scoring:
Last Update:
03/23/2001
Search Keys:
delphi delphi3000 article borland vcl code-snippet File-split Breaking-up-files Splitting-files Chopping-up-files
Times Scored:
2
Visits:
2654
Uploader: Paramjeet Reen
Company:
Reference: N/A
 
Question/Problem/Abstract:
How can I split up a file into smaller pieces of specified size and have the source code simple at the same time.
Answer:



Now why would one want to split up files? A reason could be that it is too large to be transferred reliably to another computer. Hence you chop it up into snmaller manageable pieces, transfer the pieces and re-assemble them in the target computer. Here is a very small, simple and very fast function for splitting a specified file into smaller files of specified size (in bytes). The function uses streams & is more or less self explainatory. Error handling is currently minimal & can be extended. The function does not modify the original file in any manner, but merely creates new files in the same directory as the original file with sequenced extensions (.001, .002, ...).

What's the use of splitting if you cannot put them together again? To join up the split files, you can use the command line:
Copy /B File1 + File2 + File3 ... TargetFile

Save the following code to a file named "SplitFl.pas", use it in your source with the "Uses SplitFl" clause and you are ready to split (hopefully not of laughter)!



{******************************************************}
{* Description: Splits a specified file into pieces   *}
{*              of specified size.                    *}
{******************************************************}
{* Last Modified : 12-Mar-2001                        *}
{* Author        : Paramjeet Reen                     *}
{******************************************************}
{* I do not gurantee the fitness of this program.     *}
{* Please use it at your own risk.                    *}
{******************************************************}
{* Category :Freeware.                                *}
{******************************************************}

Unit SplitFl;

interface

procedure SplitFile(const pFileName :AnsiString; const pSplitSize :LongInt);

implementation

uses
  Classes, SysUtils, Dialogs;

function Smaller(const a,b:LongInt) :LongInt;
begin
  if(a < b)then
  begin
    Result := a;
  end else
  if(b > 0)then
  begin
    Result := b
  end else Result := 0;
end;

procedure SplitFile(const pFileName :AnsiString; const pSplitSize :LongInt);
var
  vInpFl :TFileStream;
  vOutFl :TFileStream;
  vCtr   :Integer;
begin
  vInpFl := TFileStream.Create(pFileName,fmOpenRead);

  if(vInpFl.Size > pSplitSize) then
  begin
    vCtr := 0;
    while(vInpFl.Position < vInpFl.Size)do
    begin
      Inc(vCtr);
      vOutFl := TFileStream.Create(pFileName + '.' + FormatFloat('000', vCtr),fmCreate);
      vOutFl.CopyFrom(vInpFl,Smaller(pSplitSize,vInpFl.Size - vInpFl.Position));
      vOutFl.Free;
    end;
  end else MessageDlg('File too small to split!',mtInformation,[mbOk],0);

  vInpFl.Free;
end;

end.


============== File Split Act-I Scene-II ==============

The story so far was that I believed that I had made a decent file splitting function that was both small & fast. However, it was pointed out that it is not fast when it comes to handling HUGE files. I then discovered the $F000 limit to the intermediate memory buffer & thought it to be the cause. Also another suggestion of using the "FILE_FLAG_SEQUENTIAL_SCAN" flag for opening the input & output files would yield performance benefits. Keeping all the above in mind, I re-worked my original code to the one given below. However, surprisingly, there is no appreciable speed benefit!! Perhaps someone can tell me why and suggest improvements...


Unit SplitFl;

interface

procedure SplitFile(const pFileName :AnsiString; const pSplitSize :LongInt);

implementation

uses
  Classes, SysUtils, Dialogs, Windows;

function Smaller(const a,b:LongInt) :LongInt;
begin
  if(a < b)then
  begin
    Result := a;
  end else
  if(b > 0)then
  begin
    Result := b
  end else Result := 0;
end;

procedure SplitFile(const pFileName :AnsiString; const pSplitSize :LongInt);
var
  vInpFlHandle :Integer;
  vOutFlHandle :Integer;
  vInpBytesLft :Integer;
  vOutBytesLft :Integer;
  vBufferSize  :Integer;
  vBytesDone   :Integer;
  vBuffer      :Pointer;
  vCtr         :Integer;
begin

  //Use one of the following options to open the file.
  //vInpFlHandle := Integer(CreateFile(PChar(pFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,FILE_FLAG_SEQUENTIAL_SCAN));
  vInpFlHandle := FileOpen(pFileName,0);

  vInpBytesLft := FileSeek(vInpFlHandle,0,2);

  if(vInpBytesLft > pSplitSize)then
  begin
    vBufferSize := Smaller(GetHeapStatus.TotalUncommitted,pSplitSize);
    GetMem(vBuffer,vBufferSize);

    FileSeek(vInpFlHandle,0,0);
    vCtr := 0;

    while(vInpBytesLft > 0)do
    begin
      Inc(vCtr);

      //Use one of the following options to open the file.
      //vOutFlHandle := Integer(CreateFile(PChar(pFileName + '.' + FormatFloat('000', vCtr)),GENERIC_READ or GENERIC_WRITE,0,nil,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,FILE_FLAG_SEQUENTIAL_SCAN));
      vOutFlHandle := FileCreate(pFileName + '.' + FormatFloat('000', vCtr));

      vOutBytesLft := Smaller(vInpBytesLft,pSplitSize);

      while(vOutBytesLft > 0)do
      begin
        vBytesDone := FileRead(vInpFlHandle,vBuffer^,Smaller(vOutBytesLft,vBufferSize));
        FileWrite(vOutFlHandle,vBuffer^,vBytesDone);
        Dec(vInpBytesLft,vBytesDone);
        Dec(vOutBytesLft,vBytesDone);
      end;

      FileClose(vOutFlHandle);
    end;

    FreeMem(vBuffer);
  end else MessageDlg('File too small to split!',mtInformation,[mbOk],0);

  FileClose(vInpFlHandle);
end;

end.





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
The $F000 limit
    wes turner (Mar 14 2001 7:19PM)

the TFileStream.Create calls the FileCreate in SysUtils, I've had some success, by creating a separate TFileStream constructor called TFileStream.CreateSeqScan that calles this SeqScanFileCreate instead of FileCreate, and adds the FILE_FLAG_SEQUENTIAL_SCAN to the windows API CreateFile

function SeqScanFileCreate(const FileName: string): Integer;
begin
  Result := Integer(CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE,
    0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL  or FILE_FLAG_SEQUENTIAL_SCAN, 0));
end;

this allows the operating system to read ahead as much as memory allows and write in larger chunks than the $F000

Respond

splitting files
    Eddy Sterckx (Mar 13 2001 8:58AM)

Hi,

TFileStream isn't the right approach as it chokes on very large files - depending on your OS and swapfile size - but starting as low as 64 MB on my test-system.

Greetz,

Eddy Sterckx




Respond

RE: splitting files
Paramjeet (Mar 14 2001 12:45AM)

Hi Eddie,

I am using Delphi 5.0

I had a look at the classes involved. TFileStream extends THandleStream which in turn extends TStream which in turn extends TObject.

The CopyFrom() method that I am using exists is implemented in the TStream Class. What I noticed in the source (graciously provided by Borland) is that this method uses low level file operations (FileRead, FileWrite) to copy data from one file to another using an intermediate memory buffer. However, this intermediate buffer size is limited to $F000!!

Hence if the file that you are splitting is HUGE & the split size is Huge (HUGE > Huge !!) then the number of FileWrite operations increase dramatically & that's what causes the "Choking" that you mentioned. If this buffer can be dynamically set to a pre-determined percentage of available memory (keeping in mind the buffer size limit as well!!), then I guess things will improve (of course we need to ignore virtual memory here since it will result in thrashing).

I will re-work on this problem & try to come out with a better solution - Keeping the simplicity of the code in place as soon as I can spare some time. Please bear with me meanwhile.

And lastly - Thanks for the feedback - If it wasn't for it, I probably would not have discovered all the above! (I was using the function to split MP3 files - which don't usually go beyond a couple of MBs)

Regards.
Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
R. Lefter
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)