Форум: "Начинающим";
Текущий архив: 2015.09.10;
Скачать: [xml.tar.bz2];
ВнизDelphi 2005, проблема с TStream Найти похожие ветки
← →
Джованни (2014-02-11 21:22) [0]Доброг овремени суток, уважаемые.
Возникла проблема с TStream, а именно - необходимо удалить кусок данных в произвольном месте потока.
Вот, как пытаюсь реализовать идею:
procedure ChangeString(Source: TStream; Position, Count, Length: Int64; Data: WideString);
var
Buffer: array [0..BufMax] of byte;
readfrom, writeto, red, APos, ASize : int64;
begin
try
APos := Position;
ASize := Source.Size;
while APos > 0 do
begin
Source.Seek(Count, soFromCurrent);
Source.WriteBuffer(Buffer[0], BufMax);
Inc(APos);
Source.Position := APos - Length;
Source.Seek(Count, soFromCurrent);
Source.ReadBuffer(Buffer[0], BufMax);
end;
finally
end;
end;
Объясню на пальцах:
есть массив байт:
000000
001110
000000
Необходимо от позиции третьей слева единицы скопировать байты в буфер (Но не во второй TMemoryStream).
Затем с позиции первой единицы слева начать запись иных байт (не из буфера).
Затем поставить позицию в файле в конец новых записанных байт и прочитать туда же данные из ранее записанного буфера.
Приведенный код кстати кажется пишет в буфер (во всяком случае, ошибок не возникало), но не справляется со своей задачей.
ВРоде и алгоритм есть, но реализовать никак не осилю.
Буду благодарен любой помощи и правкам моего "кода".
← →
Rouse_ © (2014-02-11 21:33) [1]Не нужно делать дубли веток, тем более с разными никами. (на будущее..)
← →
Джованни (2014-02-11 21:35) [2]Rouse_ извините, просто браузер выдал ошибку из-за интернета и я подумал, что сообщение не опубликовалось, а проверить не сообразил.
Конечно учту это правило)
← →
MBo © (2014-02-12 08:19) [3]Я не понял, что нужно сделать. Объяснение на пальцах не согласуется с заявленной целью "удалить кусок данных"
Может, стоит показать на примере массива abcdefghijklmn и "иных байтов" 0123456
← →
brother © (2014-02-12 08:23) [4]> есть массив байт:
> 000000
> 001110
> 000000
это не байты...
← →
Джованни (2014-02-12 09:04) [5]brother
Я в курсе. Я показал, как вижу, как должна выглядеть работа процедуры.
MBo
Не особо понял насчет массива букв и чисел, но может так будет доступенее:
Есть строка в файле "abcdefghijklmn". После буквы "с"(третья слева) надо вставить строчку "0123456". Но не просто записать, а так, чтобы не повредились данные, которые идут после буквы "с".
Иными словами надо как бы "раздвинуть" данные в файле или потоке с необходимой позиции и записать туда нужные данные, учитывая их размер(длину).
Например, чтобы впихнуть в начало(в идеале - в любое место бы) потока/файл данные длиной 10 байт, сначала надо "отодвинуть" на 10 байт от начала все данные в потоке/файле и только потом записать данные, имеющие размер 10 байт.
Знаю про потоки и TMemoryStream, но не хочется создавать еще один "левый поток" и копировать сначала в него, потом из него и т.д. Громоздко это.
Я думал реализовать это через запись в буфер данных после указанной позиции, затем вписать свои данные (ту же строку, например) и затем произвести чтение из буфера в этот же поток или файл(отталкиваясь от того, с чем изначально оперировали).
А на примере
000000
001110
000000
я просто попытался наглядно продемонстрировать как должен выглядеть процесс в идеале.
← →
MBo © (2014-02-12 09:28) [6]В общем случае создание нового файла/потока и копирование в него нужных частей и проще всего, и быстрее.
← →
Inovet © (2014-02-12 14:09) [7]> [6] MBo © (12.02.14 09:28)
> проще всего, и быстрее
И старый для бэкапа сгодиться.
← →
Джованни (2014-02-12 20:09) [8]MBo
как быть в случае, если файл имеет размер около 1,5 ГБ?
Оперативки может банально не хватить.
Inovet
не понял, в каком смысле для бэкапа?
← →
Inovet © (2014-02-12 20:49) [9]> [8] Джованни (12.02.14 20:09)
> Оперативки может банально не хватить
А, кроме как в оперативку грузануть, больше никак? Читай и пиши в другой одновременно.
← →
Джованни (2014-02-14 09:31) [10]Хотелось бы вообще не использовать TEMP-файлы или TMemoryStream.
Говорю же - пробовал в буфер читать, но прога то виснет, то не читает вообще ничего.
← →
RWolf © (2014-02-14 09:41) [11]вставить данные в произвольном месте файла нельзя без перезаписи всего файла, так что только временный файл.
← →
MBo © (2014-02-14 09:47) [12]Ситуации, в которых действительно требуется "раздвинуть" файл, иногда встречаются.
Стоит описать детали реальной задачи, тогда можно будет что-то более конкретное посоветовать.
Например, для динамического массива байтов можно сделать так (не тестировал):SetLength(Arr, Length(Arr) + AddSize);
Move(Arr[AddPos], Arr[AddPos + AddSize], Length(Arr) - AddSize - AddPos);
Move(NewSlice[0], Arr[AddPos], AddSize);
← →
MBo © (2014-02-14 09:50) [13]>вставить данные в произвольном месте файла нельзя без перезаписи всего файла
Ну вообще можно увеличить размер файла, скопировать хвост на новое место (кусками, если большой), и дописать новое, но понятно, что это в большинстве случаев и медленнее, и всегда сложнее в реализации и опаснее, чем с новым файлом
← →
Джованни (2014-02-14 21:56) [14]MBo
У меня есть нетипизированный файл, который я создаю лично. То есть это не чужой ЕХЕ, не чужой МП3 или что-то в этом роде.
Я пытаюсь сделать небольшую программу для напоминания задач. Эдакая напоминалка для личного использования (каюсь, еще один велосипед, зато свой).
Суть в том, что данные в файле разнородны: сначала идет версия файла, дата последнего доступа, количество записей.
Каждый элемент (заметка) имеет следующую структуру:
TNote = Packed Record
cNoteSize: LongInt;
cDateCreate: TDate;
cNameSize: LongInt;
cCommentSize: LongInt;
cName: WideString;
cComment: WideString;
Тут я думаю все понятно.
В результате, приходится записывать данные поочередно. Не просто банальным
var Note: TNote;
...
Stream.WriteBuffer(Note, SizeOf(Note));
но сначала все фиксированные данные, затем только строки.
теперь к сути проблемы - всегда есть необходимость удаления заметки из моего файла. И вот никак не пойму, как это можно реализовать, не прибегая к TEMP-файлам или стороннему TMemoryStream.
По-моему, есть вариант с чтением данных с указанной позиции в некий буфер, и затем перемещение позиции и последующая запись данных из этого буфера. Грубо это можно охарактеризовать как наложение одной картинки на другую. В итоге мы будем видеть только одну картинку (не беря во внимание их содержание).
Я пытался сделать чтение в буфер и чтение из буфера, но как-то не пошло это дело. Алгоритм даже писал на бумажке, думал, может это поможет, но все-таки тяму не хватает осилить проблему.
Кстати, идея с дин. массивом вполне интересна, однако для решения данной проблемы она не подходит, ибо записи разного размера (длины)(((
← →
Inovet © (2014-02-14 22:52) [15]Ну так добавить ещё одно поле
Deleted : Boolean;
Что длина разная — плохо. Надо одинаковую, вместо строк хранить смещения, сами строки в специальных кусках файла, не входит — с указанием смещения на следующий. Т.е. тоже некая структура у этух кусков.
И вообще возьми СУБД и не парься.
← →
Джованни (2014-02-15 16:10) [16]Зачем еще одно поле? Если сделаю функцию удаления, то я там буду указывать заранее известные параметры смещения. Флаг (Удалено) будет полезен только в случае не физического удаления, но удаления "ссылки" на данные в определенном секторе. Но такой подход плох тем, что я не смогу записать данные бОльшей длины, чем были в удаленном секторе. Например, если сектор содержал в себе 10 строк по 5 символов каждая, то я не смогу записать 11 строк по 5 символов каждая, я буду ограничен в редактировании, а это не есть хорошо.
СУБД слишком для такого маленького проекта. Была идея сохранять записи в разные файлы (Например, "Заметка_1.тхт", "Заметка_2.тхт" и т.д.), но захотелось попробовать все держать в одном файле.
← →
Inovet © (2014-02-15 16:19) [17]> [16] Джованни (15.02.14 16:10)
> Но такой подход плох тем, что я не смогу записать данные
> бОльшей длины, чем были в удаленном секторе
Я тебе объяснил, как сделать, чтобы смог записать любой длинны. Только это же самое уже сделано в СУБД.
> [16] Джованни (15.02.14 16:10)
> СУБД слишком для такого маленького проекта.
Ты ошибаешься. СУБД - они разные бывают, надо взять подходящую.
← →
RWolf © (2014-02-15 16:27) [18]SQLite или Firebird Embedded в самый раз.
← →
Джованни (2014-02-16 16:47) [19]Ладно, всем спасибо, попытаюсь сделать чтение с массивами.
Теперь такой вопрос: как корректно сохранять\читать динамические массивы в файл?
Запись пытаюсь реализовать так:
try
MS := TMemoryStream.Create;
...
SetLength(TCC, 10);
...
for i:=Low(TCC) to High(TCC) do
begin
MS.WriteBuffer(TCC[i], SizeOf(Category));
end;
...
finally
MS.Free;
end;
Чтение - то же самое, только
MS.ReadBuffer(TCC[i], SizeOf(Category));
Но если запись вроде проходит, чтение выдает не совсем то, что записывал.
З.Ы.
TCatControl = array of TCategory;
TCC: TCatControl;
Category: TCategory;
...
type
TCategory = Packed Record
cCatSize: LongInt;
cDateCreate: TDate;
cNameSize: LongInt;
cCommentSize: LongInt;
end;
Что может быть причиной неудач? или я не правильно записываю?
← →
RWolf © (2014-02-16 17:42) [20]
if Length(TCC) <> 0 then
MS.WriteBuffer(TCC[0], SizeOf(Category) * Length(TCC));
← →
Джованни (2014-02-16 17:44) [21]RWolf
Не могли бы Вы,пожалуйста, пояснить немного данный код?
← →
RWolf © (2014-02-16 17:47) [22][21]
это то же, что
> for i:=Low(TCC) to High(TCC) do begin
> MS.WriteBuffer(TCC[i],
> SizeOf(Category));
> end;
← →
Джованни (2014-02-16 17:56) [23]RWolf
Помогло! Спасибо большое!
Страницы: 1 вся ветка
Форум: "Начинающим";
Текущий архив: 2015.09.10;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.05 c