Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2007.08.05;
Скачать: [xml.tar.bz2];

Вниз

Запись и чтение строк в FileStream   Найти похожие ветки 

 
Слава-17   (2007-07-03 18:04) [0]

При создании "псевдоархивера" возникла трудность с записью имени файлов при помощи TFileStream.Write. Вот код:


var
 Stream: TFileStream;
 SR: TSearchRec;
 Path: String;
 FileSize: Integer;
 FileName: String;
begin
 Path := "E:\Temp\TestPack\";
 with TFileStream.Create("test1", fmCreate) do try
   if FindFirst(Path + "*.*", faHidden or faSysFile, SR) = 0 then try
   repeat
     Stream := TFileStream.Create(Path + SR.Name, fmOpenRead);
     try
       FileSize := Stream.Size;
       FileName := SR.Name;
       Write(FileSize, SizeOf(FileSize));
       Write(FileName, SizeOf(FileName));
       CopyFrom(Stream, FileSize);
     finally
       Stream.Free;
     end;
   until FindNext(SR) <> 0;
   finally
     FindClose(SR);
   end;
 finally
   Free;
 end;
end;


При исполнении строки Write(FileName, SizeOf(FileName)) что-то записывается, но только не имя файла, так как длина записываемых данных намного меньше длины имени файла. Попытки "распаковать" такой псевдоархив ни к чему не приводят.

Кажется, я догадываюсь, в чем проблема -- имя файла пишется не полностью, так как SizeOf(FileName) возвращает размер переменной.

Если писать Write(FileName, Length(FileName)), имена записываются нормально. Только как читать их? Ведь длина имени каждого файла различна!

Извиняюсь за столь сумбурное сообщение. Чего-то я запутался. :-/


 
Плохиш ©   (2007-07-03 18:06) [1]


> Если писать Write(FileName, Length(FileName)), имена записываются
> нормально. Только как читать их? Ведь длина имени каждого
> файла различна!

Записывай сначала длину.


 
Германн ©   (2007-07-03 18:08) [2]


> Если писать Write(FileName, Length(FileName)), имена записываются
> нормально. Только как читать их? Ведь длина имени каждого
> файла различна!
>

Перед записью самой строки, записывай её длину. При чтении сначала прочитаешь длину строки, затем её саму.


 
Reindeer Moss Eater ©   (2007-07-03 18:11) [3]

Можно не париться с длиной если дополнительно создать экземпляр TWriter и использовать его методы ориентированные на строки


 
Инс ©   (2007-07-03 18:22) [4]

Все очень и очень плохо. Вы забыли, что переменная типа String - это указатель и SizeOf(String) всегда равно 4. При сохранении в файл - сначала записывайте размер. При чтении - сначала читайте размер, потом устанавливайте строке полученный размер с пом. SetLength, а потом - читайте.
Запись:
var
S: TFileStream;
str: String;
begin
S:=TFileStream.Create(FileName,fmCreate);
try
  S.WriteBuffer(Length(Str),SizeOf(Integer));
  S.WriteBuffer(Pointer(Str)^,Length(Str));
finally
  S.Free;
end;
end;

Чиение:
var
S: TFileStream;
Str: String;
Size: Integer;
begin
S:=TFileStream.Create(FileName,fmOpenRead);
try
  S.ReadBuffer(Size,SizeOf(Integer));
  SetLength(Str,Size);
  S.ReadBuffer(Pointer(Str)^,Size);
finally
  S.Free;
end;
end;


 
Слава-17   (2007-07-03 19:52) [5]

Точно! Большое всем спасибо за ответы.


 
Слава-17   (2007-07-04 13:39) [6]

И опять у меня непонятки с "псевдоархивером". Только на этот раз другого порядка. Такой способ записи данных, как у меня, — запись информации об одном файле и его содержимого, не особо подходит, когда надо считать конкретный файл.

Очевидно, что сначала надо записывать таблицу с данными обо всех файлах, включенных в архив, а затем уже их содержимое, одно за другим.

Только вот откуда брать значения смещений для этой таблицы? Ведь заранее они будут неизвестны. Записывать их позже? Не очень-то хорошо. Может, сложить в цикле размеры всех предыдущих файлов?

Не знаю. Наверное, голова перегрелась. Если она вообще есть...


 
{RASkov} ©   (2007-07-04 15:47) [7]

> [6] Слава-17   (04.07.07 13:39)
> Очевидно, что сначала надо записывать таблицу с данными
> обо всех файлах, включенных в архив

Этими значениями можно заполнить динмассив, например:

type TFileData = record
     Name: String;
     Size: Cardinal;
     Attr: Byte;
     Pos: Cardinal; //Например, хранить позицию файла в общем файле("архиве"). Но здесь нужно тщательно все просчитать...
    ....
    end;
    TFileDatas = array of TFileData;


1 Установить размер массива кол-ву файлов-1
2 Заполнить массив данными
Перед записью в "архив" в начало записать размер массива, затем данные из массива, ну а после сами файлы....


 
Слава-17   (2007-07-04 16:46) [8]


> Pos: Cardinal; //Например, хранить позицию файла в общем
> файле("архиве"). Но здесь нужно тщательно все просчитать.
> ..


Вот как раз с этим у меня и проблема. :)


 
Reindeer Moss Eater ©   (2007-07-04 16:56) [9]

Арифметика целых чисел и вдруг проблема?
:)


 
Инс ©   (2007-07-04 17:04) [10]

Если не хотите писать в файл непоследовательно (возвращаясь назад, и записывая сначала), то вот варианты.
1. Сохраняйте в TMemoryStream, там можно записывать в произвольную часть, без установки текущей позиции, а напрямую, через свойство Memory, кажется... А в конце - TMemoryStream.SaveToFile
2. Цепляйте таблицу не в начало файла, а в конец, а в заголовке файла напишите смещение таблицы, равное размеру заголовка + суммарный размер всех файлов.


 
{RASkov} ©   (2007-07-04 17:10) [11]

> [8] Слава-17   (04.07.07 16:46)

Можно поступить так:
В самое начало файла "архива" записать размер (смещение) "заголовка"... т.е. 4 байта на размер массива + 4 байта на размер заголовка + сам размер заголовка...
Поступить так... в самом начале записи файла в размер заголовка записать 0, после записи всего заголовка - записать (сместившись в начало файла) размер (т.е. туда где был ноль в начале).....
А Pos - у первого 0, у второго - Size первого, у третьего - Size второго.... и т.д.

Для доступа к конкретному файлу - Размер заголовка + Pos.... Вроде так...Я такой "фигней" не заморачивался, только что придумал :) поэтому все это может оказаться лажей (


 
Слава-17   (2007-07-04 21:02) [12]

Х-ммм... Попробую, о результатах доложу.


 
Слава-17   (2007-07-05 15:39) [13]

Продолжая тему. Вот код:


var
 Files: array of TFileRecord;
 SR: TSearchRec;
 Stream: TFileStream;
 Path: String;
 i, j: Integer;
 Zope: TextFile;
begin
 i := 0;
 Path := DirPath.Text;
 AssignFile(ReportFile, "report.txt");
 Rewrite(ReportFile);
 if FindFirst(Path + "*.*", faHidden or faSysFile, SR) = 0 then try
   repeat
     Inc(i);
     SetLength(Files, i);
     Files[i - 1].SizeOfFile := SR.Size;
     for j := 0 to i - 1 do
       Files[i - 1].FileOffset := Files[i - 1].FileOffset + Files[j].SizeOfFile;
     Files[i - 1].SizeOfName := Length(SR.Name);
     Files[i - 1].FileName := SR.Name;
     WriteLn(ReportFile, Files[i - 1].SizeOfFile, " ", Files[i - 1].FileOffset, " ", Files[i - 1].SizeOfName, " ", Files[i - 1].FileName);
   until FindNext(SR) <> 0;
 finally
   FindClose(SR);
 end;
 Write(ReportFile, "Всего файлов: " + IntToStr(Length(Files)));
 CloseFile(ReportFile);
end;


Открываем файл report.txt и видим таую картину:

<QUOTE>
7457 7457 8 end.html
20652 28109 11 glava1.html
16902 45011 11 glava2.html
11569 56580 11 glava3.html
21601 78181 11 glava4.html
18506 96687 11 glava5.html
17212 113899 11 glava6.html
5572 119471 11 glava7.html
11814 131285 11 glava8.html
10166 141451 10 index.html
Всего файлов: 10
</QUOTE>

Странно. Ведь первый файл должен иметь нулевой оффсет, второй -- оффсет, равный размеру первого файлы, последний -- оффсет, равный сумме размеров всех файлов, исключая его.

Вроде бы и простая задача -- немного поправить цикл, вычисляющий оффсет, но почему-то у меня ничего не получается... :(


 
Сергей М. ©   (2007-07-05 15:46) [14]


> Слава-17   (05.07.07 15:39) [13]
>
> Продолжая тему. Вот код


Интересное "продолжение")

В теме - работа с FileStream и записью туда строк, а в ее "продолжении" от FileStream"a уже остались одни ножки да рожки в виде сиротливо торчащего объявления лок.переменной Stream..


 
{RASkov} ©   (2007-07-05 16:08) [15]

> repeat
>     Inc(i);
>     SetLength(Files, i);
>     Files[i - 1].SizeOfFile := SR.Size;
>     for j := 0 to i - 1 do
>       Files[i - 1].FileOffset := Files[i - 1].FileOffset + Files[j].SizeOfFile;
>     Files[i - 1].SizeOfName := Length(SR.Name);
>     Files[i - 1].FileName := SR.Name;
>     WriteLn(ReportFile, Files[i - 1].SizeOfFile, " ", Files[i- 1].FileOffset, " ", Files[i - 1].SizeOfName, " ", Files[i- 1].FileName);
>   until FindNext(SR) <> 0;

Логику вот здесь поменяй...

> от FileStream"a уже остались одни ножки да рожки в виде
> сиротливо торчащего объявления лок.переменной Stream..

Которую автор и вставил намеренно, чёб в тему попадал вопрос :) так как эта переменная и не используется вообще...:)


 
{RASkov} ©   (2007-07-05 16:15) [16]

Так попробуй:
 i:=0;
......
  repeat
    SetLength(Files, i+1);
    Files[i].SizeOfFile := SR.Size;
    if i=0 then Files[i].FileOffset := 0 else Files[i].FileOffset := Files[i-1].FileOffset + Files[i-1].SizeOfFile;
    Files[i].SizeOfName := Length(SR.Name);
    Files[i].FileName := SR.Name;
    WriteLn(ReportFile, Files[i].SizeOfFile, " ", Files[i].FileOffset, " ", Files[i].SizeOfName, " ", Files[i].FileName);
    Inc(i);
  until FindNext(SR) <> 0;


 
Anatoly Podgoretsky ©   (2007-07-05 16:32) [17]

> Слава-17  (05.07.2007 15:39:13)  [13]

Оно так и будет, если SizeOfFile будет равен 0


 
Слава-17   (2007-07-05 17:18) [18]


> Интересное "продолжение") В теме - работа с FileStream и
> записью туда строк, а в ее "продолжении" от FileStream"a
> уже остались одни ножки да рожки в виде сиротливо торчащего
> объявления лок.переменной Stream..


Решил немного сэкономить БД форума... :)) Ну а если серьезно, то ведь еще раньше обсуждение записи строк переросло в обсуждение создания "псевдоархивера". Так зачем создавать новую тему?


> Которую автор и вставил намеренно, чёб в тему попадал вопрос
> :) так как эта переменная и не используется вообще...:)


Нет. :)
Там была запись в файл, но я ее убрал до того, пока не разберусь с вычислением оффсетов. А про переменную забыл.

Спасибо за ответы. Как проробую -- доложу. :)


 
Слава-17   (2007-07-05 18:03) [19]

Эту проблему решил. Но... Не надоел ли я вам еще своими вопросами "по теме"? :))

Раз уж записал файловую таблицу для "псевдоархива", ее надо считать:


procedure Чтение_файловой_таблицы;
var
 Stream: TFileStream;
 Files: array of TFileRecord;
 i: Integer;
 ReportFile: TextFile;
begin
 i := 0;
 AssignFile(ReportFile, "readed.txt");
 Rewrite(ReportFile);
 Stream := TFileStream.Create(FilePath.Text, fmOpenRead);
 try
   repeat
     SetLength(Files, i+1);
     Stream.Read(Files[i].SizeOfFile, SizeOf(Integer));
     Stream.Read(Files[i].FileOffset, SizeOf(Integer));
     Stream.Read(Files[i].SizeOfName, SizeOf(Integer));
     Stream.Read(Pointer(Files[i].FileName)^, Files[i].SizeOfName);
     WriteLn(ReportFile, Files[i].SizeOfFile, " ", Files[i].FileOffset, " ", Files[i].SizeOfName, " ", Files[i].FileName);
     Inc(i);
   until Stream.Position = Stream.Size;
 finally
   Stream.Free;
 end;
 Write(ReportFile, "Всего считано файлов: " + IntToStr(Length(Files)));
 CloseFile(ReportFile);
end;

procedure Запись_файловой_таблицы;
var
 Files: array of TFileRecord;
 SR: TSearchRec;
 Stream: TFileStream;
 Path: String;
 i, j: Integer;
 ReportFile: TextFile;
begin
 i := 0;
 Path := DirPath.Text;
 AssignFile(ReportFile, "report.txt");
 Rewrite(ReportFile);
 with TFileStream.Create("test", fmCreate) do try
   if FindFirst(Path + "*.*", faHidden or faSysFile, SR) = 0 then try
     repeat
       SetLength(Files, i+1);
       Files[i].SizeOfFile := SR.Size;
       if i = 0 then
         Files[i].FileOffset := 0
       else
         Files[i].FileOffset := Files[i-1].FileOffset + Files[i-1].SizeOfFile;
       Files[i].SizeOfName := Length(SR.Name);
       Files[i].FileName := SR.Name;
       WriteLn(ReportFile, Files[i].SizeOfFile, " ", Files[i].FileOffset, " ", Files[i].SizeOfName, " ", Files[i].FileName);
       Inc(i);
     until FindNext(SR) <> 0;
     j := Length(Files) - 1;
     for i := 0 to j do begin
       Write(Files[i].SizeOfFile, SizeOf(Integer));
       Write(Files[i].FileOffset, SizeOf(Integer));
       Write(Files[i].SizeOfName, SizeOf(Integer));
       Write(Pointer(Files[i].FileName)^, Files[i].SizeOfName);
     end;
   finally
     FindClose(SR);
   end;
 finally
   Free;
 end;
 Write(ReportFile, "Всего файлов: " + IntToStr(Length(Files)));
 CloseFile(ReportFile);


Теперь открываем файл readed.txt, и видим следующее:


7457 0 8
778333797 1819112552 20652
7457 11 1986096231
1747857761 107769204 -855637950
184549485 1728053248 1635148140
1952984626 758213741 -1345126400
720896 1818689536 862025313
1836345390 5529964 14484480
2816 1634494208 775184758
1819112552 18506 78181
11 1986096231 1747858785
1013738868 -1358954429 184549753
1728053248 1635148140 1952984630
365194349 -1125449728 720897
1818689536 929134177 1836345390
3024492 30584576 2816
1634494208 775446902 1819112552
10166 131285 10
1701080681 1952984696 27757
Всего считано файлов: 19


Что-то совсем несуразное. Хотя, есть некоторые знакомые числа из первого списка -- 7457 0 8, 20652 7457 11, 18506 78181 11... Очевидно, что я где-то напутал. Только вот где, не могу понять. Вроде бы и порядок чтения данных соответсвует порядку записи... :-/


 
{RASkov} ©   (2007-07-05 18:40) [20]

> [19] Слава-17   (05.07.07 18:03)

> procedure Чтение_файловой_таблицы;
.....
>   repeat
>     SetLength(Files, i+1);
>     Stream.Read(Files[i].SizeOfFile, SizeOf(Integer));
>     Stream.Read(Files[i].FileOffset, SizeOf(Integer));
>     Stream.Read(Files[i].SizeOfName, SizeOf(Integer));
>     Stream.Read(Pointer(Files[i].FileName)^, Files[i].SizeOfName);
>     WriteLn(ReportFile, Files[i].SizeOfFile, " ", Files[i].FileOffset, " ", Files[i].SizeOfName, " ", Files[i].FileName);
>     Inc(i);
>   until Stream.Position = Stream.Size;

Ну вопервых... немного не так... сам подумай, у тебя ж после заголовка идут сами файлы(если заголовок вначале "архива") См[11]
При чтении один раз делать SetLength(Files, n); и дальше цикл for
Во вторых я бы делал запись строк так:
var N: Integer;
.....
N:=Length(Files[i].FileName);
Stream.WriteBuffer(N, SizeOf(N));
Stream.WriteBuffer(Files[i].FileName[1], N);
...

Соответственно чтение так:
var N: Integer;
.....
  Stream.ReadBuffer(N, SizeOf(N));
  SetLength(Files[i].FileName, N);
  Stream.ReadBuffer(Files[i].FileName[1], N);
....

И не забываем про то, что в начало "архива" пишем кол-во элементов TFileRecord и весь размер заголовка, который после записи заголовка равен Stream.Size
И еще.... намешал в кучу и текстовый файл и "потоковый"... тогда лучше без with и сразу оба обрабатывать(своими переменными)... имхо.


 
{RASkov} ©   (2007-07-05 18:51) [21]

> И еще.... намешал в кучу и текстовый файл и "потоковый"...
> тогда лучше без with и сразу оба обрабатывать(своими переменными)
> ... имхо.

Процедуры Чтения и Записи практически не должны отличаться...
Вот на основе твоей процедуры чтения - сделай процедуру записи с некоторыми поправками, например из [20]...
Да... наполнение массива TFileRecord я бы вынес в отдельный(свой) для этого метод...
Тогда именно то получиться, что я тебе и говорю в этом посте.....
В итоге, нечто такое получается:

type TFileRecords = array of TFileRecord;
.....
function CreateHeadFileArch(const Path, Mask: String): TFileRecords;
procedure SaveFileArch(const FlNm: String);
procedure LoadFileArch(const FlNm: String);


 
{RASkov} ©   (2007-07-05 19:01) [22]

Кстати... твой "архив" это нечто склейки файлов...
Можно организовать и так:
Тупо сливаешь все файлы в один файл(т.е. пишешь файлы один за другим) при этом наполняешь "заголовок", а сам заголовок пишешь в другой файл.
Получиться:
FileName.myarc
FileName.head
Остальное все также, только в итоге два файла (свои плюсы... свои минусы).
Опять же говорю - все мои посты в этой ветке - это только мысли вслух :)


 
Слава-17   (2007-07-05 20:26) [23]


> Ну вопервых... немного не так... сам подумай, у тебя ж после
> заголовка идут сами файлы(если заголовок вначале "архива")


Мне хотя бы организовать запись и чтение заголовка. :) А с файлами буду возиться уже после этого...


> И еще.... намешал в кучу и текстовый файл и "потоковый".
> .. тогда лучше без with и сразу оба обрабатывать(своими
> переменными)... имхо.


Вывод в текстовый файл -- это для проверки. Как будет все нормально читаться, уберу его.


> Кстати... твой "архив" это нечто склейки файлов...Можно
> организовать и так:Тупо сливаешь все файлы в один файл(т.
> е. пишешь файлы один за другим) при этом наполняешь "заголовок",
>  а сам заголовок пишешь в другой файл.Получиться:FileName.
> myarcFileName.headОстальное все также, только в итоге два
> файла (свои плюсы... свои минусы).Опять же говорю - все
> мои посты в этой ветке - это только мысли вслух :)


Нет... :) Мой "архив" будет приклеен к exe-файлу в качестве оверлея, так что это не подходит. Делаю не SFX.

Ладно, еще раз всем спасибо. Попробую. Отпишусь о результатах скорее всего завтра.


 
Слава-17   (2007-07-06 12:14) [24]

И опять ошибка при чтении. Наверное, я никогда от вас не отстану. :)


function CreateArchiveHeader(Path, Mask: String): TFileRecords;
var
 SR: TSearchRec;
 i: Integer;
begin
 i := 0;
 if FindFirst(Path + Mask, faHidden or faSysFile, SR) = 0 then try
   repeat
     SetLength(Result, i + 1);
     Result[i].SizeOfFile := SR.Size;
     if i = 0 then
       Result[i].FileOffset := 0
     else
       Result[i].FileOffset := Result[i - 1].FileOffset + Result[i - 1].SizeOfFile;
     Result[i].SizeOfName := Length(SR.Name);
     Result[i].FileName := SR.Name;
     Inc(i);
   until FindNext(SR) <> 0;
 finally
   FindClose(SR);
 end;
end;

procedure TMainForm.Button1Click(Sender: TObject);
var
 Files: TFileRecords;
 i, j: Integer;
begin
 Files := CreateArchiveHeader(DirPath.Text, "*.*");
 with TFileStream.Create("test", fmCreate) do try
   j := Length(Files) - 1;
   WriteBuffer(j, SizeOf(Integer));
   Position := Position + 4;
   for i := 0 to j do begin
     WriteBuffer(Files[i].SizeOfFile, SizeOf(Integer));
     WriteBuffer(Files[i].SizeOfName, SizeOf(Integer));
     WriteBuffer(Pointer(Files[i].FileName)^, Files[i].SizeOfName);
     WriteBuffer(Files[i].FileOffset, SizeOf(integer));
   end;
   i := Position;
   Seek(4, soFromBeginning);
   WriteBuffer(i, SizeOf(Integer));
 finally
   Free;
 end;
end;

procedure TMainForm.Button2Click(Sender: TObject);
var
 Files: TFileRecords;
 ReportFile: TextFile;
 i, FilesCount, StreamSize: Integer;
begin
 with TFileStream.Create(FilePath.Text, fmOpenRead) do try
   ReadBuffer(FilesCount, SizeOf(Integer));
   SetLength(Files, FilesCount);
   ReadBuffer(StreamSize, SizeOf(Integer));
   for i := 0 to FilesCount do begin
     ReadBuffer(Files[i].SizeOfFile, SizeOf(Integer));
     ReadBuffer(Files[i].SizeOfName, SizeOf(Integer));
     ReadBuffer(Pointer(Files[i].FileName)^, Files[i].SizeOfName);
     ReadBuffer(Files[i].FileOffset, SizeOf(integer));
   end;
 finally
   Free;
 end;
end;


FilesCount и StreamSize считываются нормально. При чтении информации о файлах выдает Stream Read Error. У меня такое подозрение, что я где-то напутал со счетчиками цикла. Только вот где?.. :-/


 
{RASkov} ©   (2007-07-06 16:21) [25]

>
>   WriteBuffer(j, SizeOf(Integer));
>   Position := Position + 4;
>   for i := 0 to j do begin

  WriteBuffer(j, SizeOf(Integer));
  WriteBuffer(0, SizeOf(Integer));
  for i := 0 to j do begin
......
  i := Size;
  Seek(SizeOf(Integer), soFromBeginning);
  WriteBuffer(i, SizeOf(Integer));


И на чтении
> i, FilesCount, StreamSize: Integer;

Я думаю лучше будет HeadSize смотреться...:)


 
Слава-17   (2007-07-06 18:44) [26]

И опять выскакивает Stream Read Error... :-/


 
{RASkov} ©   (2007-07-06 19:39) [27]

> [26] Слава-17   (06.07.07 18:44)

Попробуй строки писать/читать как в [20]...


 
{RASkov} ©   (2007-07-06 19:44) [28]

> [26] Слава-17   (06.07.07 18:44)

Ну конечно же

>   WriteBuffer(Files[i].SizeOfName, SizeOf(Integer));
>   WriteBuffer(Pointer(Files[i].FileName)^, Files[i].SizeOfName);

И опять имя с толку сводит... вроде как должно не SizeOfName а LengthOfName
WriteBuffer(Files[i].SizeOfName, Length(Files[i].FileName));


 
{RASkov} ©   (2007-07-06 19:49) [29]

> ReadBuffer(Files[i].SizeOfFile, SizeOf(Integer));

Не принципиально, но лучше так
ReadBuffer(Files[i].SizeOfFile, SizeOf(Files[i].SizeOfFile));
А то сменишь тип Files.SizeOfFile на Byte, например, в описании типа TFileRecord, и будешь потом долго искать проблему....
Это и к записи(WriteBuffer) относиться...


 
Слава-17   (2007-07-06 20:40) [30]

Все равно... :(( Шо за дела??


 
{RASkov} ©   (2007-07-06 20:46) [31]

> [30] Слава-17   (06.07.07 20:40)

Ну кидай исправленные
procedure TMainForm.Button1Click(Sender: TObject);
procedure TMainForm.Button2Click(Sender: TObject);
и описание TFileRecord...


 
{RASkov} ©   (2007-07-06 20:49) [32]

Ты пишешь:
> j := Length(Files) - 1;  

А читаешь и выставляешь:
>   ReadBuffer(FilesCount, SizeOf(Integer));
>   SetLength(Files, FilesCount);


 
Слава-17   (2007-07-06 21:06) [33]

Исправил я, называется. Теперь Stream Read Error и при записи. Голова у меня уже не работает. :) Выкладываю то, что просили:


type
 TFileRecord = record
   SizeOfFile: Integer;
   SizeOfName: Integer;
   FileName: String;
   FileOffset: Integer;
 end;

 TFileRecords = array of TFileRecord;

procedure TMainForm.Button1Click(Sender: TObject);
var
 Files: TFileRecords;
 i, j: Integer;
begin
 i := 0;
 Files := CreateArchiveHeader(DirPath.Text, "*.*");
 with TFileStream.Create("test.ebook", fmCreate) do try
   j := Length(Files) - 1;
   WriteBuffer(j, SizeOf(Integer));
   WriteBuffer(i, SizeOf(Integer));
   for i := 0 to j do begin
     WriteBuffer(Files[i].SizeOfFile, SizeOf(Files[i].SizeOfFile));
     WriteBuffer(Files[i].SizeOfName, Length(Files[i].FileName));
     WriteBuffer(Pointer(Files[i].FileName)^, Files[i].SizeOfName);
     WriteBuffer(Files[i].FileOffset, SizeOf(Files[i].FileOffset));
   end;
   i := Size;
   Seek(SizeOf(Integer), soFromBeginning);
   WriteBuffer(i, SizeOf(Integer));
 finally
   Free;
 end;
end;

procedure TMainForm.Button2Click(Sender: TObject);
var
 Files: TFileRecords;
 ReportFile: TextFile;
 i, FilesCount, StreamSize: Integer;
begin
 with TFileStream.Create(FilePath.Text, fmOpenRead) do try
   ReadBuffer(FilesCount, SizeOf(Integer));
   SetLength(Files, FilesCount);
   ReadBuffer(StreamSize, SizeOf(Integer));
   for i := 0 to FilesCount do begin
     ReadBuffer(Files[i].SizeOfFile, SizeOf(Files[i].SizeOfFile));
     ReadBuffer(Files[i].SizeOfName, Length(Files[i].FileName));
     ReadBuffer(Pointer(Files[i].FileName)^, Files[i].SizeOfName);
     ReadBuffer(Files[i].FileOffset, SizeOf(Files[i].FileOffset));
   end;
 finally
   Free;
 end;



> Ты пишешь:
> А читаешь и выставляешь:


Так ведь Length(Files) - 1 и FilesCount -- это одно и то же!


 
{RASkov} ©   (2007-07-06 21:12) [34]

> Так ведь Length(Files) - 1 и FilesCount -- это одно и то же!

Смотри сам N:=Length-1
          X:=SetLength(N);
Чему равен Х? Ясен перец, что это не верно синтаксически, но смысл.... уловил? :)


 
{RASkov} ©   (2007-07-06 21:15) [35]

Вообщем [34] и возможно [20] про строки....


 
{RASkov} ©   (2007-07-06 21:18) [36]

> SizeOfName: Integer;  

Ну исправь на LengthName: Integer;
Самому же проще будет разбираться в исходнике....


 
Слава-17   (2007-07-06 21:36) [37]


> Чему равен Х? Ясен перец, что это не верно синтаксически,
>  но смысл.... уловил? :)


Так я пробовал писать SetLength(Files, FilesCount + 1), и ничего...


 
{RASkov} ©   (2007-07-06 22:23) [38]

> [37] Слава-17   (06.07.07 21:36)

Вот держи... я немного изменил, думаю найдешь и разберешься, что к чему...
Так же возможно придется заменить некоторые компоненты и uses соответственно.
В кратце: на форме Мемо, пару кнопок, и, грубо, два Едита....
unit Unit1;
interface
uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, Mask, ToolEdit;

type
 TForm1 = class(TForm)
   Button1: TButton;
   Button2: TButton;
   DirPath: TDirectoryEdit;
   FilePath: TFilenameEdit;
   Memo1: TMemo;
   procedure Button1Click(Sender: TObject);
   procedure Button2Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}
type
TFileRecord = record
  SizeOfFile: Integer;
  LengthName: Integer;
  FileName: String;
  FileOffset: Integer;
end;
TFileRecords = array of TFileRecord;

function CreateArchiveHeader(Path, Mask: String): TFileRecords;
var
SR: TSearchRec;
i: Integer;
begin
i := 0;
if FindFirst(IncludeTrailingPathDelimiter(Path) + Mask, faAnyFile, SR) = 0 then try
  repeat
    SetLength(Result, i + 1);
    Result[i].SizeOfFile := SR.Size;
    if i = 0 then
      Result[i].FileOffset := 0
    else
      Result[i].FileOffset := Result[i - 1].FileOffset + Result[i - 1].SizeOfFile;
    Result[i].LengthName := Length(SR.Name);
    Result[i].FileName := SR.Name;
    Inc(i);
  until FindNext(SR) <> 0;
finally
  FindClose(SR);
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
Files: TFileRecords;
i, j: Integer;
S: String;
begin
Files := CreateArchiveHeader(DirPath.Text, "*.*");
with TFileStream.Create("C:\test.ebook", fmCreate) do try
  i := Size;
  j := Length(Files);
  WriteBuffer(i, SizeOf(Size));
  WriteBuffer(j, SizeOf(Integer));
  Memo1.Lines.Add("Сохранение ["+IntToStr(j)+"] файла(ов)");
  for i := 0 to j-1 do begin
    WriteBuffer(Files[i].SizeOfFile, SizeOf(Files[i].SizeOfFile));
    WriteBuffer(Files[i].FileOffset, SizeOf(Files[i].FileOffset));

    WriteBuffer(Files[i].LengthName, SizeOf(Files[i].LengthName));
    WriteBuffer(Files[i].FileName[1], Files[i].LengthName);
   
    S:=Format("%-40s %-10d %-10d", [Files[i].FileName, Files[i].SizeOfFile, Files[i].FileOffset]);
    Memo1.Lines.Add(S);
  end;
  i := Size;
  Memo1.Lines.Add("Размер заголовка: "+IntToStr(Size)+" байт");
  Memo1.Lines.Add(StringOfChar("-", 100));
  Seek(0, soFromBeginning);
  WriteBuffer(i, SizeOf(i));
finally
  Free;
end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
Files: TFileRecords;
i, FilesCount, HeadSize: Integer;
S: String;
begin
with TFileStream.Create(FilePath.Text, fmOpenRead) do try
  Memo1.Lines.Add("Чтение файла: "+FilePath.Text);
  ReadBuffer(HeadSize, SizeOf(Size));
  ReadBuffer(FilesCount, SizeOf(Integer));
  SetLength(Files, FilesCount);
  Memo1.Lines.Add(Format("Файлов: %d, Размер заголовка: %d", [FilesCount, HeadSize]));
  for i := 0 to FilesCount-1 do begin
    ReadBuffer(Files[i].SizeOfFile, SizeOf(Files[i].SizeOfFile));
    ReadBuffer(Files[i].FileOffset, SizeOf(Files[i].FileOffset));

    ReadBuffer(Files[i].LengthName, SizeOf(Files[i].LengthName));
    SetLength(Files[i].FileName, Files[i].LengthName);
    ReadBuffer(Files[i].FileName[1], Files[i].LengthName);
   
    S:=Format("%-40s %-10d %-10d", [Files[i].FileName, Files[i].SizeOfFile, Files[i].FileOffset]);
    Memo1.Lines.Add(S);
  end;
  Memo1.Lines.Add(StringOfChar("-", 100));
finally
  Free;
end;
end;

end.


 
Слава-17   (2007-07-07 10:41) [39]

Огромное вам спасибо! Наконец-таки!! :) Думаю, что после этого реализовать чтение самих файлов из "архива" особого труда не составит...

Как ра таки по чтению у меня и вопрос. Данные о файлах, находящихся в архиве, хранятся в массиве. Как я понимаю, чтобы считать нужный файл, надо создать подобную процедуру:


procedure Чтение_файла_из_архива(Имя_Файла: Integer);
var
 i: Integer;
begin
 for i := 0 to FilesCount - 1 do
   if Files[i].FileName = Имя_файла then begin
     Считать_данные
   end;
end;


Но ведь поиск в массиве может занимать долгое время! если файлов десять, то это будет и незаметно... А если десять тысяч? Или я ошибаюсь?



Страницы: 1 вся ветка

Форум: "Начинающим";
Текущий архив: 2007.08.05;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.6 MB
Время: 0.045 c
4-1171893224
Brc
2007-02-19 16:53
2007.08.05
Select file


2-1183904053
b52
2007-07-08 18:14
2007.08.05
создание для программы функции Open


15-1183510913
Рубль
2007-07-04 05:01
2007.08.05
День рождения независимости


2-1183881661
Triax
2007-07-08 12:01
2007.08.05
Как это сделать?


15-1183964561
boriskb
2007-07-09 11:02
2007.08.05
Просьба к сайтостроителям





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский