|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
Именения данных в бинарном файле на большее, меньшее или равное количество
Здравствуйте. Повторюсь в в данном вопросе я ноль. Ибо не интересовался по работе с бинарным данными. Однако по ответам с друогих форумах имею представления что встроенного функционала на подобие функций Pos, Copy и Delete нету для работы с набором байтов.
Короче нужно 3 примера: 1. Замена байтов на такоеже количество. 2. Замена байт если длинна заменяемых данных больше длинны искомых. 3. Замена байт если длинна заменяемых данных меньше чем длина искомых. И да нужно именно заменить удалив лишнии Что до 2 варианта о данные например такие: Код:
PATTERN1: array [0..9] of Byte = ($03, $05, $00, $00, $00, $63, $69, $62, $3D, $31); PATTERN2: array [0..11] of Byte = ($03, $07, $00, $00, $00, $63, $69, $62, $3D, $39, $39, $39); Код:
PATTERN1: array [0..10] of Byte = ($03, $06, $00, $00, $00, $63, $69, $62, $3D, $2D, $31); PATTERN2: array [0..9] of Byte = ($03, $05, $00, $00, $00, $63, $69, $62, $3D, $39); Цитата:
|
#2
|
||||
|
||||
Я бы работал с классом TFileStream. Считали блок данных размером с искомый паттерн, и посимвольно пробегаем в поиске первого байта паттерна. Если нашли, то запоминаем позицию (начало искомой последовательности) и проверяем следующий бай. Совпал - продолжаем с третьим и т.д. на всю длину паттерна. (если дошли до конца прочитанного блока, то дочитываем недостающую длину и продолжаем сверку). Если же ничего не нашлось, то снова считываем следующую партию байт и повторяем процесс пока не найдем всю последовательность.
А дальше бы уже прибегнул к дополнительному потоку (хоть TFileStream, хоть TMemoryStream). Копируем в дополнительный поток все что до начала искомого паттерна, потом записываем нужный паттерн (PATTERN2) и повторяем все действия до конца файла. Если нужно, то переносите содержимое дополнительного потока в первый и сохраняете. Или же оригинальный файл переименовывем и сохраняем доп.поток в файл с прежним именем. С таким подходом не будет никакой разницы какой, какой паттерн длиннее или короче, хоть вообще "удалять" искомый набор байт. Грамотно поставленный вопрос содержит не менее 50% ответа. Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть. Последний раз редактировалось dr. F.I.N., 09.07.2020 в 19:24. |
#3
|
|||
|
|||
Цитата:
Нужно учитывать что позиция где эти байты находятся находится неизвестно. И более того находиться они будут всегда в разном месте всеже это не программа. Плюс размер файла большой - 10 - 50 МБ. Число варьируется между этим отрезком. Так что побайтовое чтение будет долгим. Да и мнебы пример. А точнее примеры 3 штуки на 3 варианта. |
#4
|
|||
|
|||
1. Если работать через TFileStream - то без разницы по байту ты читаешь или по несколько - там все-равно все идет через буфер.
2. Делал в свое время такую штуку (правда не через TFfileStream). Там код довольно сложный получается, если ты не хочешь дублировать файл. 10-50мб это не так и много, так что проще сделать через временный файл. Т.е. читаем исходный файл, меняем данные, если нашли паттерн, и пишем во временный. Потом заменяем файл целиком. |
#5
|
|||
|
|||
Да лин. Может кто приме мне сделать. Например поменять:
PATTERN1: array [0..9] of Byte = ($03, $05, $00, $00, $00, $63, $69, $62, $3D, $31); на PATTERN2: array [0..11] of Byte = ($03, $07, $00, $00, $00, $63, $69, $62, $3D, $39, $39, $39); и PATTERN1: array [0..10] of Byte = ($03, $06, $00, $00, $00, $63, $69, $62, $3D, $2D, $31); на PATTERN2: array [0..9] of Byte = ($03, $05, $00, $00, $00, $63, $69, $62, $3D, $39); Буду очень благодарен. Ну или если уже есть это то дайте ссылку. |
#6
|
||||
|
||||
Код:
//Функция поиска шаблона function PatternSearch(aPattern: TBytes; aFileName: String): LongInt; var F: TMemoryStream; Buf: TBytes; Len, Step: LongInt; begin F := TMemoryStream.Create; F.LoadFromFile(aFileName); try Len := Length(aPattern); SetLength(Buf, Len); Step := 0; try repeat F.Seek(Step, soFromBeginning); F.ReadBuffer(Buf, Len); Inc(Step); until CompareMem(Buf, aPattern, Len); Result := F.Position; except Result := -1; end; finally F.Free; end; end; Код:
//Процедура замены одного шаблона на другой procedure ReplacePattern(aFileName: String; aOffset: LongInt; OldPattern, NewPattern: TBytes); var InF, OutF: TMemoryStream; OldLen, NewLen: LongInt; FirstFilePart, SecondFilePart: TBytes; begin OldLen := Length(OldPattern); NewLen := Length(NewPattern); InF := TMemoryStream.Create; OutF := TMemoryStream.Create; InF.LoadFromFile(aFileName); RenameFile(aFileName, aFileName + '.bak'); // DeleteFile(aFileName); try if OldLen = NewLen then begin InF.Seek(aOffset, soFromBeginning); InF.WriteBuffer(NewPattern, NewLen); InF.SaveToFile(aFileName); end else begin SetLength(FirstFilePart, aOffset); InF.ReadBuffer(FirstFilePart, aOffset); InF.Seek(OldLen, soFromCurrent); SetLength(SecondFilePart, InF.Size - Length(FirstFilePart) - OldLen); InF.ReadBuffer(SecondFilePart, Length(SecondFilePart)); OutF.WriteBuffer(FirstFilePart, Length(FirstFilePart)); OutF.WriteBuffer(NewPattern, Length(NewPattern)); OutF.WriteBuffer(SecondFilePart, Length(SecondFilePart)); OutF.SaveToFile(aFileName); end; finally InF.Free; OutF.Free; SetLength(FirstFilePart, 0); SetLength(SecondFilePart, 0); end; end; Код:
//Пример использования procedure TForm1.Button1Click(Sender: TObject); const aFileName = 'd:\Test.pdf'; Buf1: TBytes = [$65, $0A, $2F, $58, $4F, $62, $6A, $65, $63, $74, $2F, $4C, $65, $6E, $67, $74]; Buf2: TBytes = [$FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF]; var Offset: LongInt; begin Offset := PatternSearch(Buf1, aFileName); if Offset >= 0 then ReplacePattern(aFileName, Offset, Buf1, Buf2); end; Хотелось бы узнать результаты проверки в боевых условиях. Всегда пишите код так, будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете. Последний раз редактировалось Kailon, 20.07.2020 в 06:34. |
#7
|
||||
|
||||
Kailon, функция поиска паттерна работать у Вас не будет. Пример:
Файл: #FF #0F #AE #12 #00...e.t.c. Паттерн для поиска: #0F #AE Работа Вашего алгоритма (опустим загрузки файла и т.п.): Считываем из файла в буфер (длина буфера = длине паттерна = 2) Содержимое буфера: #FF #0F Сравниваем буфер с паттерном: #FF #F0 <> #0F #AE, повторяем чтение буфера Содержимое буфера: #AE #12 ... и т.д. Как можете заметить, после первого сравнения Ваш алгоритм проскакивает мимо искомого паттерна. Грамотно поставленный вопрос содержит не менее 50% ответа. Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть. |
Этот пользователь сказал Спасибо dr. F.I.N. за это полезное сообщение: | ||
Kailon (19.07.2020)
|
#8
|
||||
|
||||
О, действительно. Что-то я как-то совсем не подумал об этом. Вот почему важен взгляд со стороны и тестирование. Дополнил немного функцию в моём предыдущем посте.
Всегда пишите код так, будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете. |
#9
|
||||
|
||||
Все равно ошибка
Цитата:
И по примеру использования: А кто сказал, что паттерн не может располагаться в самом начале файла (Offset= 0)? Стоит возвращать (-1) если паттерн не найден. А если найден, то уже значение от 0 до ... Аналогично функции Pos(). Вы не подумайте, что я критикую и не предлагаю. Я предложил решение первым ответом. Imikle внес замечания. По сути алгоритм есть, нужно только написать. Мне лень писать, но читать чужой код я люблю Грамотно поставленный вопрос содержит не менее 50% ответа. Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть. Последний раз редактировалось dr. F.I.N., 19.07.2020 в 13:04. |
#10
|
|||||
|
|||||
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Всегда пишите код так, будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете. Последний раз редактировалось Kailon, 20.07.2020 в 06:44. |
#11
|
||||
|
||||
Все же Вы не правы. После выполнения TStream.Read позиция курсора меняется и уже указывает не на начало последовательности, а на её конец. Проведите эксперимент:
Содержимое файла [123456789] Паттерн [23] Какой результат вернет Ваша функция? Отвечаю: 3. А все потому, что нашли паттерн в позиции 1 (как и полагается), произвели чтение паттерна длиною в 2 байта. Итого 2+1 = 3. Поправить можно так: Код:
function PatternSearch(aPattern: TBytes; aFileName: String): LongInt; var F: TMemoryStream; Buf: TBytes; Len, Step: LongInt; begin F := TMemoryStream.Create; F.LoadFromFile(aFileName); try Len := Length(aPattern); SetLength(Buf, Len); Step := -1; try repeat Inc(Step); F.Seek(Step, soFromBeginning); F.ReadBuffer(Buf, Len); until CompareMem(Buf, aPattern, Len); Result := Step; except Result := -1; end; finally F.Free; end; end; Грамотно поставленный вопрос содержит не менее 50% ответа. Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть. Последний раз редактировалось dr. F.I.N., 20.07.2020 в 09:33. |
#12
|
||||
|
||||
Автор, вот еще пример, дорабатывайте на разные размеры исходных и конечных данных + прикрутите отлов исключений:
Код:
const OriginalByte: array [0 .. 55] of Byte = ($64, $69, $6E, $67, $20, $6D, $65, $20, $68, $69, $73, $00, $00, $00, $00, $27, $00, $00, $28, $00, $00, $00, $21, $00, $00, $00, $63, $6F, $6D, $70, $75, $74, $65, $72, $2E, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $27, $00, $00, $28, $00, $00, $00, $00); BytetoWrite: array [0 .. 55] of Byte = ($63, $68, $6F, $67, $20, $6D, $65, $20, $68, $69, $73, $00, $00, $00, $00, $27, $00, $00, $28, $00, $00, $00, $21, $00, $00, $00, $63, $6F, $6D, $70, $75, $74, $65, $72, $2E, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $27, $00, $00, $28, $00, $00, $00, $06); procedure DoMyPatch(const FileName: String); var i: Int64; FileName: string; input: TFileStream; FileByteArray, ExtractedByteArray: Tbytes; begin input := TFileStream.Create(FileName, fmOpenReadWrite); try input.Position := 0; SetLength(FileByteArray, input.size); input.Read(FileByteArray[0], Length(FileByteArray)); for i := Low(FileByteArray) to High(FileByteArray) do begin ExtractedByteArray := Copy(FileByteArray, i, Length(OriginalByte)); if CompareMem(@FileByteArray[i], @OriginalByte[0], Length(OriginalByte)) = True then begin input.Seek(i, SoFromBeginning); input.Write(BytetoWrite[0], Length(BytetoWrite)); end; end; finally FreeAndNil(input); end; end; |