( ゚Д゚)ノ Delギコ猫のプログラミング相談室part2
117 :
104 :
01/09/05 05:06 ID:0bwO4T7E 第一段階:複数ファイルの(ヘッダ尽きでの)結合。 {Files で指定されたファイルをヘッダ付きで結合する ファイル名は256バイトまでで、それ以上は切り捨てられる 上書き確認はしない} procedure MakeArchive(const Files:TStrings; const Newfile:string); var i,p,Size:integer; Source:TMemoryStream; Dest:TMemoryStream; Buf:array [0..256-1] of Char; SizeBuf:array [0..16-1] of Byte; Name:string; begin Source:=TMemoryStream.Create; Dest := TMemoryStream.Create; try for i:=0 to Files.Count-1 do begin {ファイルを読み込む} if not FileExists( Files[i] ) then begin showmessage('FILE NOT FOUND'+#13+Files[i]); Exit; end; Source.LoadFromFile( Files[i] ); {ファイル名をBufにセット} Name := ExtractFileName( Files[i] ); StrPLCopy( Buf, Name, SizeOf( Buf ) ); {ヘッダ:最初の256バイトはファイル名を示す} Dest.WriteBuffer( Buf, 256 ); {ヘッダ:次の 16 バイトはファイルサイズを示す} Size := Source.Size; for p:=0 to High( SizeBuf ) do begin SizeBuf[p] := Size and $FF; Size := Size shr 8; end; Dest.WriteBuffer( SizeBuf, 16 ); {データ:バイナリを書き込む} Dest.CopyFrom( Source, 0 ); end; Dest.SaveToFile( Newfile ); finally Dest.Free; Source.Free; end; end;
118 :
104 :01/09/05 05:10 ID:0bwO4T7E
すまん、インデント忘れ。 {Files で指定されたファイルをヘッダ付きで結合する ファイル名は256バイトまでで、それ以上は切り捨てられる 上書き確認はしない} procedure MakeArchive(const Files:TStrings; const Newfile:string); var i,p,Size:integer; Source:TMemoryStream; Dest:TMemoryStream; Buf:array [0..256-1] of Char; SizeBuf:array [0..16-1] of Byte; Name:string; begin Source:=TMemoryStream.Create; Dest := TMemoryStream.Create; try for i:=0 to Files.Count-1 do begin {ファイルを読み込む} Source.LoadFromFile( Files[i] ); {ファイル名をBufにセット} Name := ExtractFileName( Files[i] ); StrPLCopy( Buf, Name, SizeOf( Buf ) ); {ヘッダ:最初の256バイトはファイル名を示す} Dest.WriteBuffer( Buf, 256 ); {ヘッダ:次の 16 バイトはファイルサイズを示す} Size := Source.Size; for p:=0 to High( SizeBuf ) do begin SizeBuf[p] := Size and $FF; Size := Size shr 8; end; Dest.WriteBuffer( SizeBuf, 16 ); {データ:バイナリを書き込む} Dest.CopyFrom( Source, 0 ); end; Dest.SaveToFile( Newfile ); finally Dest.Free; Source.Free; end; end;
119 :
104 :01/09/05 05:12 ID:0bwO4T7E
続き。
>>103 で結合したファイルを分離。
{ MakeArchive関数で結合されたアーカイブ Filename を分離し
DestPath ディレクトリ内に保存する ファイル名は 256 バイトまででそれ以上は切り捨て
上書き確認はしない}
procedure SeparateArchive(const Filename, DestPath:string);
var
Filesize:Int64;
i:integer;
Source:TMemoryStream;
Dest:TMemoryStream;
Buf:array [0..256-1] of Char;
SizeBuf:array [0..16-1] of Byte;
Name:string;
begin
Source:=TMemoryStream.Create;
Dest := TMemoryStream.Create;
try
Source.LoadFromFile( Filename );
repeat
{ヘッダ:最初の256バイトをGET}
Source.ReadBuffer( Buf, 256 );
Name := Buf;
{ヘッダ:次の16バイトをGET}
Source.ReadBuffer( SizeBuf, 16 );
Filesize:=0;
for i:=0 to 16-1 do
Inc( Filesize, SizeBuf[i] shl (i*8) );
{ファイルを抽出}
Dest.Clear;
Dest.CopyFrom( Source, Filesize );
{保存}
Dest.SaveToFile( IncludeTrailingPathDelimiter(DestPath) + Name );
until Source.Position >= Source.Size;
finally
Source.Free;
Dest.Free;
end;
end;
120 :
104 :01/09/05 05:14 ID:0bwO4T7E
>>119 >続き。
>>103 で結合したファイルを分離。
103じゃなくて118だ…
両関数とも動作確認済み。
もうすこし効率よくかけそうなのでアドバイスキボン。
121 :
104 :01/09/05 05:33 ID:0bwO4T7E
さらにつくってみた。動作確認済み。 { MakeArchive関数で結合されたアーカイブ Filename をから、 Target で指定されたファイルを探し出し Stream に抽出する。 アーカイブ内に目的のファイルが存在するならば True を返す} function ExtractFromArchiveToStream(Stream:TStream;const Archive, Target:string):Boolean; var Filesize:Int64; i:integer; Source:TMemoryStream; Dest:TMemoryStream; Buf:array [0..256-1] of Char; SizeBuf:array [0..16-1] of Byte; Name:string; begin result := False; Source:=TMemoryStream.Create; Dest := TMemoryStream.Create; try Source.LoadFromFile( Archive ); repeat {ヘッダ:最初の256バイトをGET} Source.ReadBuffer( Buf, 256 ); Name := Buf; {ヘッダ:次の16バイトをGET} Source.ReadBuffer( SizeBuf, 16 ); Filesize:=0; for i:=0 to 16-1 do Inc( Filesize, SizeBuf[i] shl (i*8) ); {ターゲットファイルかどうか} if AnsiSameText( Name, Target ) then begin {ファイルを抽出} Dest.Clear; Dest.CopyFrom( Source, Filesize ); Dest.SaveToStream( Stream ); {終了} Result := True; Exit; end else {POSITION をサイズぶんだけ移動} Source.Position := Source.Position + Filesize; until Source.Position >= Source.Size; finally Source.Free; Dest.Free; end; end;
122 :
104 :01/09/05 05:34 ID:0bwO4T7E
コレで最後。 { MakeArchive関数で結合されたアーカイブ Filename をから、 Target で指定されたファイルを探し出し DestPath ディレクトリ内に保存する アーカイブ内に目的のファイルが存在するならば True を返す} function ExtractFromArchive(const Archive, Target, DestPath:string):Boolean; var Stream:TMemoryStream; begin Result := False; Stream := TMemoryStream.Create; try if ExtractFromArchiveToStream(Stream,Archive,Target) then begin Stream.SaveToFile( IncludeTrailingPathDelimiter(DestPath)+Target ); result := True; end; finally Stream.Free; end; end;
123 :
104 :01/09/05 05:36 ID:0bwO4T7E
うーむ、ソースUP板に書くべきだったか…? しかし、これに圧縮関数を組み合わせれば 相当いろんな事ができると思うのだが。 有益な関数だと自負するぞ。
124 :
104 :01/09/05 06:00 ID:0bwO4T7E
さらに調子に乗ってみた。動作確認済み。 { MakeArchive関数で結合されたアーカイブ Archibve から Target で指定されたファイルを削除する。アーカイブ内に目的のファイルがない場合は Falseを返す} function RemoveFromArchive(const Archive, Target:string):Boolean; var Filesize:Int64; i:integer; Source,Dest:TMemoryStream; NameBuf:array [0..256-1] of Char; SizeBuf:array [0..16-1] of Byte; Name:string; begin result := False; Source:=TMemoryStream.Create; Dest := TMemoryStream.Create; try Source.LoadFromFile( Archive ); repeat {ヘッダ:最初の256バイトをGET} Source.ReadBuffer( NameBuf, SizeOf( NameBuf ) ); Name := NameBuf; {ヘッダ:次の16バイトをGET} Source.ReadBuffer( SizeBuf, SizeOf( SizeBuf ) ); Filesize:=0; for i:=0 to High( SizeBuf ) do Inc( Filesize, SizeBuf[i] shl (i*8) ); {ターゲットファイルかどうか} if AnsiSameText( Name, Target ) then begin { position をサイズ分だけ移動し、読み飛ばす} Source.Position := Source.Position + Filesize; end else begin {新アーカイブへ内容をコピー} Dest.WriteBuffer( NameBuf,SizeOf( NameBuf ) ); Dest.WriteBuffer( SizeBuf,SizeOf( SizeBuf ) ); Dest.CopyFrom( Source, Filesize ); end; until Source.Position >= Source.Size; Dest.SaveToFile( Archive ); finally Source.Free; Dest.Free; end; end;
125 :
104 :01/09/05 06:01 ID:0bwO4T7E
とどめだ。動作確認済み。 { MakeArchive関数で結合されたアーカイブ内のファイル名リストを得る Files.strings にファイル名が、 Files.objects にファイルサイズ(バイト)が格納される} procedure ArchivedFiles(const Archive:string; Files:TStrings); var Filesize:Int64; i:integer; Source:TMemoryStream; NameBuf:array [0..256-1] of Char; SizeBuf:array [0..16-1] of Byte; begin Files.Clear; Source:=TMemoryStream.Create; try Source.LoadFromFile( Archive ); repeat {ヘッダ:最初の256バイトをGET} Source.ReadBuffer( NameBuf, 256 ); {ヘッダ:次の16バイトをGET} Source.ReadBuffer( SizeBuf, 16 ); Filesize:=0; for i:=0 to 16-1 do Inc( Filesize, SizeBuf[i] shl (i*8) ); {ファイル名とサイズを追加} Files.AddObject( NameBuf , TObject(Filesize) ); { position をサイズ分だけ移動し、読み飛ばす} Source.Position := Source.Position + Filesize; until Source.Position >= Source.Size; finally Source.Free; end; end;