Помогите перевести следующие функции на язык дельфи, это функции чтения-записи в файл. Код (Text): PboEntry::PboEntry() { filename[0]=0; size=0; memset(unknown,0,16); } int PboEntry::Read(FILE *f) { while(1) { char c=fgetc(f); if (c==char(0)) break; filename+=c; } fread(unknown,1,16,f); fread(&size,1,4,f); if (size==0) return 0; return 1; } int PboEntry::Write(FILE *f) { fwrite(filename.c_str(),1,filename.size(),f); fputc(char(0),f); fwrite(unknown,1,16,f); fwrite(&size,1,4,f); return 1; } На всякий случай, программа на С++ лежит тут Хочу написать подобную, но на Дельфи.
Quantum Конечно можно. Searcher Проще самому написать чем переводить.Мог перевести с ошибками. constructor PboEntry.creat; begin filename[0]:=0; size:=0; fillchar(unknown,16,0); end; function PboEntry.Read(f:FILE of byte):integer; begin while (true) do begin system.read(f,c); if (c=chr(0)) then break; filename:=filename+chr(c); end; blockread(f,unknown,16); blockread(f,size,4); if (size=0) then result:=0; result:=1; end; function PboEntry.Write(f:FILE of byte):integer; begin blockwrite(f,filename.c_str,filename.size); system.write(f,byte(0)); blockwrite(f,unknown^,16); blockwrite(f,size,4,); result:=1; end;
Searcher > "попробую написать так" Попробуй-попробуй, надеюсь ты знаком с Delphi и сам исправишь кучу ошибок "перевода" Pavia: 1) конструктор переопределять не нужно вообще, т.к. наследуемый от TObject автоматом зануляет все поля. 2) переменная типа file передается всегда как var параметр, т.е. Read(var f:file) и Write(var f:file) 3) на system.read(f,..) и system.write(f,..) компилятор выдаст ошибку, т.к. эти функции - для работы с текстовыми файлами и консолью, а для двоичных файлов нужно использовать blockread(f,..) 4) в дельфах result - это просто обозначение переменной результата и никакого ret она не делает, поэтому в функции read результат надо определять так if size = 0 then result:=0 else result:=1 или без else переставить присвоение единицы до проверки size. 5) если filename это string, то в дельфах никаких мемберов через точку указать нельзя и write надо делать так: blockwrite(f,filename[1],length(filename)), а еще лучше length()+1, тогда не нужно будет отдельно записывать завершающий ноль; 6) ну и мелкие незуразности: если в read переменная с:Char, то Сhr(c) - это масло масляное; в write крышка у unknown^ не нужна (хотя может и прокатит) Что касается, формата записываемого файла - не проще ли в начале записывать длину filename, а то while в поисках конца строки выглядит как-то некрасиво, если не сказать глупо. PS: volodya наверное спит, иначе бы этой темы здесь не было. Helheim & Delphi в одном флаконе - это перебор
А еще нужно очистить свое сознание от предрассудков, которые скрывает от духовного зрения тот простой факт, что filename - это строка, а строки в Delphi 1. начинаются с _первого_ элемента 2. являются полноценными динамическими переменными Поэтому filename:='', несомненно, поможет благородному дону. Короче, вместо того, чтобы лабать изначально дефективный "перевод", есть смысл понять, что эта программа делает (судя по приведенному куску - ничего особо сложного) и написать ее именно на Delphi, а не на языке "Объектный Сикаль".
Просветите чукчу (меня) - что, в самом деле существует язык программирования Delphi ? leo > IMHO не проще. В таком случае при копировании строки будет использоваться счётчик (регистр), а в случае с "посимвольным копированием до завершающего нуля" код будет более эффективным. А выглядит некрасиво, потому что следовало бы делать так: Код (Text): while(char c=fgetc(f)) filename+=c;
S_T_A_S_ > "в случае с "посимвольным копированием до завершающего нуля" код будет более эффективным" Это в каком смысле более эффективным ? 1) если filename это динамическая строка "произвольного" размера, то наверное понятно что будет, когда мы делаем filename+=c; если не понятно, то поясняю - если при очередном добавлении символа не хватает размера выделенной памяти, то происходит realloc со всеми вытекающими отсюда последствиями. За подробностями обращяться в getmem.inc на дельфах или heap.cpp на С++. 2) чтение\копирование блоков памяти и в оси, и в любых компиляторах производится не по байту, а как минимум по 4 и только остаток 1-3 байта по одному. Если размер строки известен, то на дельфах сразу выделяется строка нужного размера и грузится из файла целиком через ReadFile: Код (Text): setlength(filename,L); blockread(f,filename[1],L); //тут можно прочитать и завержающий 0, если еспользовать L+1 PS: В дельфах blockread это просто обертка для ReadFilе с проверкой валидности хэндла, хранещегося в переменной f:TFileRec, и генерацией исключений при GetLastError <> 0.
leo 1) Разве виндос поддерживает имена файлов "произвольного" размера ? 2) Может лучше тогда уж MMX + prefetchnta использовать. Для копирования имени файла =)) > Откуда он будет известен? Его так же придётся загружать из файла - это одна операция чтения. Далее, либо цикл по этому счётчику (который где-то нужно хранить), либо - 2я операция чтения. Если же мы будем просто брать по 1му байту (из буфера, куда fgetc прочитает сразу 4K данных, из отображённого файла, из ...) пока не встретим 0, то потребуется одна операция чтения. Счётчик цикла не нужен. В результате имеем более компактный и простой код.
Действительно "попробовал" Работает наполовину, пытался кучу всего исправить, так и не смог. Теперь попробую по новым добавленным советам портировать эти функции в Дельфи. Спасибо вам, программеры. Если есть еще варианты как это написать в Дельфи, пишите.
S_T_A_S_ Твои рассуждения насчет преимущества fgetc перед ReadFile ИМХО ошибочны. Во-первых, по твоему получается, что fgetc это что-то типа инлайнового mov al,byte [Buf]. На самом деле это далеко не так. Вот минимум того, что должна делать fgetc (на примере дельфийской internal функции _readchar): 1) проверить валидность операции (чтобы с дуру не выдать мусор и не нарваться на исключение) 2) проверить валидность текущей позиции буфера (позиция не вышла за пределы буфера и не соответствет концу файла EOF) если все ОК, то 3) MOV символ в AL 4) инкремент позиции буфера 5) ret 6) здесь разные варианты если после 1 или 2 не ОК Умножаем все эти излишества на число символов filename (с учетом latency call и ret) и радуемся "простоте и компактности кода". Во-вторых, а чем принципиально отличается ReadFile от fgetc ? Если отбросить экзотический вариант с FILE_FLAG_NO_BUFFERING, то ИМХО ReadFile работает примерно также как и fgetc, т.е. даные читаются с диска в системный буфер (файловый кэш) теми же 4K, откуда затем и копируются в наш буфер с теми же проверками, что и в fgetc. Единственное тут могут добавляться системные проверки, связанные с синхронизацией IO при ShareMode <> 0 или FILE_FLAG_OVERLAPPED. Но зато мы вызываем ReadFile для чтения строки только один раз и дополнительно экономим время на пересылке байтов в строку. > "Разве виндос поддерживает имена файлов "произвольного" размера ?" Под "произвольной" длиной filename я конечно имел ввиду не "бесконечную", а от "нескольких" символов до многих десятков (ну к примеру "C:\Program Files\Microsoft Office\Шаблоны\Дизайны презентаций\Высокое напряжение.pot"). > "в самом деле существует язык программирования Delphi" Прикалываешься ? В Delphi используется язык прогаммирования Object Pascal. Но когда мы используем библиотечные функции, то возникает интересный вопрос, а "чьи" это функции - некоего стандарта языка Pascal или системы программирования Delphi ? Чтобы не забивать себе голову этими вопросами, проще говорить, что программа написана не на Object Pascal, а на Delphi такой-то версии, и возможно ее не удастся без проблем скомпилировать в младших версиях или каких-то других компиляторах.
leo > Обязательно ли делать "подмену ценностей" ? Моё мнение было о разнице в применении zero-terminated и паскалевских строк в данном конкретном случае. > Я не теоретик. Откомпилировал болванку и пробежался отладчиком - это даже быстрее, чем рыться в сорцах > А вот это мне нравицца =) Если , то fgetc - .....? Это та же самая обёртка! Но нет, при "модификации" моих рассуждений о строчках, откуда ни возьмись, берётся fgetc vs ReadFile (даже не blockread . Естесственно, любой человек, находясь в трезвом уме и здравой памяти, скажет, что ReadFile - это более быстрый путь получить данные из файла. Но утверждал ли я обратное ?
S_T_A_S_ Извини, но ты меня запутал окончательно. Из последнего поста я-тупой не понял ничего. Я рассуждаю просто - и blockread и fgetc\_readchar кроме чтения\копирования данных выполняют вспомогательные операции. Поэтому 2 операции "чтения" размера строки и самой строки должны занимать меньшее время, чем N чтений по одному байту. Плюс к этому "ускоренное" копирование строки по 4 байта (или фиг знает по сколько). Минус проблемы с неизвестной зарезервированной длиной filename (резервировать MaxPath на все случаи жизни ?). Если ты считаешь иначе, то хотелось бы услышать вразумительно-доходчивые аргументы. Только про счетчик не вспоминай, т.к. я его в явном виде никогда и не предлагал использовать.
Как говорят теоретики, 90% кода программы выполняется в течении 10% времени её работы. IMHO они не правы. Правильное соотношение - как минимум 80% / 20%. Потомучто эти 90% нужно оптимизировать по размеру ! Использование одной операции чтения вместо 2х, и/или отказ от счётчика ведёт к уменьшению размера кода. Ну зачем это оптимизировать по скорости ? (и можно ли это сделать вообще
Понял. Оптимизация по скорости здесь конечно под большим вопросом (впрочем как и по коду). Так что давай "замнем" на этом для ясности.
Итак, эти функции я переконвертил в дельфи, теперь есть еще вопрос насчет считывания и записи поблочно. В Си это делается через векторы, а в дельфи помедленнее у меня выходит. В общем, есть процедура: Код (Text): Procedure UnpackPBO(FName:string); const N = 63579; var FromF, ToF: file; NumRead, NumWritten, i, j, CountFiles: Integer; BufName: array [1..1] of Char; Buf: array [1..1] of Char; BufUnknown: array[1..16] of Char; BufSize: array [1..4] of byte; rec:^Trec; str,preFname,dir:string[255]; begin GetMem(rec, N*sizeof(PboEntry)); AssignFile(FromF, FName); Reset(FromF, 1); { Record size = 1 } j:=0; Repeat j:=j+1; str:=''; Repeat BlockRead(FromF, BufName, SizeOf(BufName), NumRead); If Char(BufName)<>chr(0) then str:=str+Char(BufName); Until Char(BufName)=chr(0); rec^[j].FPN:=str; BlockRead(FromF, BufUnknown, SizeOf(BufUnknown), NumRead); BlockRead(FromF, BufSize, SizeOf(BufSize), NumRead); str:=''; For i:=4 downto 1 do str:=str+IntToHex(BufSize[i],2); rec^[j].size:=HexToInt(str); Until rec^[j].size=0; CountFiles:=j-1; preFname:=''; For i:=1 to Length(FName)-4 do preFname:=preFname+FName[i]; For j:=1 to CountFiles do begin dir:=preFname+'\'+ExtractFilePath(rec^[j].FPN); if ForceDirectories(dir) then begin Assign(ToF,preFname+'\'+rec^[j].FPN); Rewrite(ToF,1); For i:=1 to rec^[j].size do begin BlockRead(FromF, Buf, SizeOf(Buf), NumRead); BlockWrite(ToF, Buf, NumRead, NumWritten); end; CloseFile(ToF); end else showmessage('Cann''t create '+dir); end; CloseFile(FromF); {$IFDEF VER80} FreeMem(rec, N*sizeof(PboEntry)); {$ELSE} FreeMem(rec); {$ENDIF} end; Так вот, можно ли как то во время работы программы изменять длину(размер) буфера Buf? Чтобы считывать не по одному символу, а сразу помногу? Пробовал Buf как динамический массив, но не работает, выдает ошибку.
Не, ну я уже злиться начинаю... Сказано же в правилах - дельфи - ересь. Это тебе что, форум по дельфям, блин?