![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Запись и чтение строк в FileStream
Слава-17 © (2007-07-03 18:04:00)При создании "псевдоархивера" возникла трудность с записью имени файлов при помощи 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)), имена записываются нормально. Только как читать их? Ведь длина имени каждого файла различна!
Извиняюсь за столь сумбурное сообщение. Чего-то я запутался. :-/
Слава-17 (2007-07-05 18:03:00) [21]Эту проблему решил. Но... Не надоел ли я вам еще своими вопросами "по теме"? :))
Раз уж записал файловую таблицу для "псевдоархива", ее надо считать:
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... Очевидно, что я где-то напутал. Только вот где, не могу понять. Вроде бы и порядок чтения данных соответсвует порядку записи... :-/
Слава-17 (2007-07-05 17:18:00) [22]
> Интересное "продолжение") В теме - работа с FileStream и
> записью туда строк, а в ее "продолжении" от FileStream'a
> уже остались одни ножки да рожки в виде сиротливо торчащего
> объявления лок.переменной Stream..
Решил немного сэкономить БД форума... :)) Ну а если серьезно, то ведь еще раньше обсуждение записи строк переросло в обсуждение создания "псевдоархивера". Так зачем создавать новую тему?
> Которую автор и вставил намеренно, чёб в тему попадал вопрос
> :) так как эта переменная и не используется вообще...:)
Нет. :)
Там была запись в файл, но я ее убрал до того, пока не разберусь с вычислением оффсетов. А про переменную забыл.
Спасибо за ответы. Как проробую -- доложу. :)
Anatoly Podgoretsky (2007-07-05 16:32:00) [23]> Слава-17 (05.07.2007 15:39:13) [13]
Оно так и будет, если SizeOfFile будет равен 0
{RASkov} (2007-07-05 16:15:00) [24]Так попробуй:
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;
{RASkov} (2007-07-05 16:08:00) [25]> 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..
Которую автор и вставил намеренно, чёб в тему попадал вопрос :) так как эта переменная и не используется вообще...:)
Сергей М. (2007-07-05 15:46:00) [26]
> Слава-17 (05.07.07 15:39) [13]
>
> Продолжая тему. Вот код
Интересное "продолжение")
В теме - работа с FileStream и записью туда строк, а в ее "продолжении" от FileStream'a уже остались одни ножки да рожки в виде сиротливо торчащего объявления лок.переменной Stream..
Слава-17 (2007-07-05 15:39:00) [27]Продолжая тему. Вот код:
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>
Странно. Ведь первый файл должен иметь нулевой оффсет, второй -- оффсет, равный размеру первого файлы, последний -- оффсет, равный сумме размеров всех файлов, исключая его.
Вроде бы и простая задача -- немного поправить цикл, вычисляющий оффсет, но почему-то у меня ничего не получается... :(
Слава-17 (2007-07-04 21:02:00) [28]Х-ммм... Попробую, о результатах доложу.
{RASkov} (2007-07-04 17:10:00) [29]> [8] Слава-17 (04.07.07 16:46)
Можно поступить так:
В самое начало файла "архива" записать размер (смещение) "заголовка"... т.е. 4 байта на размер массива + 4 байта на размер заголовка + сам размер заголовка...
Поступить так... в самом начале записи файла в размер заголовка записать 0, после записи всего заголовка - записать (сместившись в начало файла) размер (т.е. туда где был ноль в начале).....
А Pos - у первого 0, у второго - Size первого, у третьего - Size второго.... и т.д.
Для доступа к конкретному файлу - Размер заголовка + Pos.... Вроде так...Я такой "фигней" не заморачивался, только что придумал :) поэтому все это может оказаться лажей (
Инс (2007-07-04 17:04:00) [30]Если не хотите писать в файл непоследовательно (возвращаясь назад, и записывая сначала), то вот варианты.
1. Сохраняйте в TMemoryStream, там можно записывать в произвольную часть, без установки текущей позиции, а напрямую, через свойство Memory, кажется... А в конце - TMemoryStream.SaveToFile
2. Цепляйте таблицу не в начало файла, а в конец, а в заголовке файла напишите смещение таблицы, равное размеру заголовка + суммарный размер всех файлов.
Reindeer Moss Eater (2007-07-04 16:56:00) [31]Арифметика целых чисел и вдруг проблема?
:)
Слава-17 (2007-07-04 16:46:00) [32]
> Pos: Cardinal; //Например, хранить позицию файла в общем
> файле("архиве"). Но здесь нужно тщательно все просчитать.
> ..
Вот как раз с этим у меня и проблема. :)
{RASkov} (2007-07-04 15:47:00) [33]> [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 13:39:00) [34]И опять у меня непонятки с "псевдоархивером". Только на этот раз другого порядка. Такой способ записи данных, как у меня, — запись информации об одном файле и его содержимого, не особо подходит, когда надо считать конкретный файл.
Очевидно, что сначала надо записывать таблицу с данными обо всех файлах, включенных в архив, а затем уже их содержимое, одно за другим.
Только вот откуда брать значения смещений для этой таблицы? Ведь заранее они будут неизвестны. Записывать их позже? Не очень-то хорошо. Может, сложить в цикле размеры всех предыдущих файлов?
Не знаю. Наверное, голова перегрелась. Если она вообще есть...
Слава-17 (2007-07-03 19:52:00) [35]Точно! Большое всем спасибо за ответы.
Инс (2007-07-03 18:22:00) [36]Все очень и очень плохо. Вы забыли, что переменная типа 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;
Reindeer Moss Eater (2007-07-03 18:11:00) [37]Можно не париться с длиной если дополнительно создать экземпляр TWriter и использовать его методы ориентированные на строки
Германн (2007-07-03 18:08:00) [38]
> Если писать Write(FileName, Length(FileName)), имена записываются
> нормально. Только как читать их? Ведь длина имени каждого
> файла различна!
>
Перед записью самой строки, записывай её длину. При чтении сначала прочитаешь длину строки, затем её саму.
Плохиш (2007-07-03 18:06:00) [39]
> Если писать Write(FileName, Length(FileName)), имена записываются
> нормально. Только как читать их? Ведь длина имени каждого
> файла различна!
Записывай сначала длину.