Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[34.228.168.200] |
|
Сообщ.
#1
,
|
|
|
Нужно найти в файле сигнатуру, и отрезать всё что от начала файла до этой сигнатуры, не подскажете каким способом это делается быстрее всего?
Может есть уже какие библиотеки или примеры, компоненты для этого? Моя реализация на текущий момент через TmemoryStream, но мне кажется что можно быстрее.. |
Сообщ.
#2
,
|
|
|
Подумать над реализацией двух случаев:
1.Маленький файл (несколько МБ, до гига). 2.Большой файл. В первом случае - всё прочитать махом в ОЗУ и там ужо найти и выписать хвост. Во втором - читать порциями, аккуратно в них искать сигнатуру (может быть на границе!), выписывать посегментно хвост. Добавлено Впрочем, может оказаться, что реализация только 2 варианта (даже с 16 КБ буфером) будет достаточной и быстрее всего. Но может оказаться, что сигнатура очень длинная (скажем, мегабайт), а тогда возникнет ряд проблем. |
Сообщ.
#3
,
|
|
|
Тогда может оцените мой код, по сути нужно найти сигнатуру pdf файла и далее отсечь лишнее, сигнатура всего 4 байта, размер файла ну максимум 10mb, в среднем 1mb.
Меня единственное что смущает так это мой поиск, но как сделать круче не знаю \ не умею ... function ExtractPDFFromContainerAndSaveToFile(Stream: TStream; FileName: String; const StartPos: Cardinal = 500): Integer; var i: Cardinal; FStream: TStream; begin i:= StartPos; Result := -1; while i+4 < Stream.Size do begin if ( PByteArray(TMemoryStream(Stream).Memory)^[i] = $25) then if ( PByteArray(TMemoryStream(Stream).Memory)^[i+1] = $50) and ( PByteArray(TMemoryStream(Stream).Memory)^[i+2] = $44) and ( PByteArray(TMemoryStream(Stream).Memory)^[i+3] = $46) then begin FStream := TFileStream.Create(FileName, fmCreate); try FStream.WriteBuffer(@PByteArray(TMemoryStream(Stream).Memory)^, i, Stream.Size-i); finally FStream.Free; Result := 0; end; Exit; end; Inc(i); end; end; |
Сообщ.
#4
,
|
|
|
Крайне невыгодно 4 раза по одному байту сравнивать (процессорно дорого). Я в Паскале крайне слаб, но точно можно же как-то перевести и сразу Dword'ы сравнить! (но шагать, конечно, всё равно по байтику надо!)
|
Сообщ.
#5
,
|
|
|
Jiro, в твоем варианте нет смысла сравнивать побайтно, легче сразу сравнивать с 4-байтным числом. Но ты точно уверен, что сигнатура не будет располагаться, к примеру, с 2-го по 6-й байт?
Славян Зависит от количества ложных срабатываний, возможно, быстрее классически искать первый байт и потом сравнивать хвосты. Jiro, 10 мб - это ничто, попробуй заюзать Pos для Ansi строк SetLength(sFile, FileStream.Size); FileStream.ReadBuffer(sFile[1], FileStream.Size); sSign := RawByteString(#$25#$....); p := Pos(sSign, sFile); |
Сообщ.
#6
,
|
|
|
Цитата Fr0sT @ Но ты точно уверен, что сигнатура не будет располагаться, к примеру, с 2-го по 6-й байт? Уверен. Fr0sT размер мусора в среднем 908 байт, ну там плюс\минус, в принципе можно начинать с позиции 500-того байта и буферить гденить 1024 байта и в них искать как вы предложили через Pos, но это итак понятно. Но вот тут возник вопрос сам собой, если использовать FileStream.ReadBuffer(sFile[1], FileStream.Size) то это получается лишнее копирование из памяти в память, насколько это быстро и затратно по ресурсам и можно ли избежать лишнего копирования передав данные в указатель и потом уже применять Pos? |
Сообщ.
#7
,
|
|
|
Цитата Jiro @ это получается лишнее копирование из памяти в память А копирование и так и так будет. Но оно одно, и от него никуда не деться. Можно, конечно, извратиться через маппинг, но оптимизированной функции поиска по указателю в RTL нет, поэтому смысла в этом немного. А какие требования по быстродействию? |
Сообщ.
#8
,
|
|
|
Цитата Fr0sT @ А какие требования по быстродействию? Тут требований нету, это я так на будущее узнаю, мало ли пригодится. |
Сообщ.
#9
,
|
|
|
Работа с диском на порядок-три медленнее, чем с памятью, так что скорость будет определяться именно им.
|
Сообщ.
#10
,
|
|
|
Кстати, было бы оптимально узнать начальные кластеры файла, а потом ненужные убрать и указать с какого байта начало. Но я технически не знаю, есть ли такая возможность, чтобы у гигабайтного, например, файла отрезать первые пару байт, но при этом не переписывать всё длинное тело?..
|
Сообщ.
#11
,
|
|
|
Славян
Насколько я понимаю, файл (по крайней мере, в старых файловых системах) стартует с начала кластера, т.е минимальная гранулярность перезаписи от 512 и т.п. байт |
Сообщ.
#12
,
|
|
|
Да, я тоже так думал. Просто это же сильнейшее ограничение, кое могли как-то и побороть. В линухах (в btrfs?) наверняка ж можно!.. Ладно, пустое гадание; надо спецов поспрашивать.
|
Сообщ.
#13
,
|
|
|
Цитата Jiro @ while i+4 < Stream.Size do наверное стоит добавить: while i+4 < Stream.Size-4 - чисто что бы не лезть за границы файлы... |
Сообщ.
#14
,
|
|
|
Цитата Руслан @ наверное стоит добавить: while i+4 < Stream.Size-4 - чисто что бы не лезть за границы файлы... выхода за границы не может быть, инкремент же на +1, а при нахождении сигнатуры выход. файл итак читается не доходя до последних 4 байт, а в вашем случаи будет до 8 байт. ну как бы да, быстрее на пустых файлах, но тогда можно и 10 и 256 и тд ... ну а если кому интересно то код теперь такой за что спс Fr0sT-у: unction ExtractPDFFromContainerAndSaveToFile(Stream: TStream; FileName: String; const StartPos: Cardinal = 500): Integer; var FStream: TStream; sSign: RawByteString; AData: AnsiString; P: Int64; begin Result := -1; SetLength(AData, 900+1024); //Буфер поиска, если искать по всему файлу то Stream.Size Stream.Position := StartPos; Stream.ReadBuffer(AData[1], 900+1024);//Буфер поиска, если искать по всему файлу то Stream.Size sSign := RawByteString(#$25#$50#$44#$46); P := StartPos + Pos(sSign, AData); if P > StartPos then begin FStream := TFileStream.Create(FileName, fmCreate); try FStream.WriteBuffer(@PByteArray(TMemoryStream(Stream).Memory)^, P-1, Stream.Size-P+1); finally FStream.Free; Result := 0; end; end; end; |
Сообщ.
#15
,
|
|
|
Цитата Jiro @ файл итак читается не доходя до последних 4 байт А, точно. Чёт я внесенные изменения проигнорил. |