Определение типа файла по содержимому

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Сообщение Trezub » 28.11.2005 00:26:22

Проблема в следующем. Необходимо определять тип файла по его содержимому. Файлы Microsoft Word и Microsoft Excel. И это все усложняет. Вариант с просмотром файла и поиском строк "Microsoft Word" и аналогично "Microsoft Excel" не подходит, потому как иногда срабатывает неверно (например, при вставках Ворда в Ексель и наоборот).

Определение типа файла возможно по его сигнатуре. Но проблема в том, что начальные биты файлов Ворда и Екселя одинаковые (во всяком случае так "считываю" я).

В *nix'е есть программа file, которая определяет тип. Существует два портированных вариана под Windows, так вот программа filetype, определяет файлы и Ворд'а и Ексель'а как file Microsoft Office, без уточнения :(

А вторая прога не запускается... просит библиотеки pcre.dll и zlib1.dll (их я еще не скачал:). И потом прийдеться мучаться с подключением сишного кода к freepascal'у.

Мне то, всего и нужно - определять три типа - Ворд, Ексель и другие. Не хочется использовать код монстра, который определяет все типы (сомневаюсь, что смогу вытащить нужный мне код "под себя")

Что подскажете?
Trezub
новенький
 
Сообщения: 52
Зарегистрирован: 17.09.2005 21:23:04

Сообщение alexs » 28.11.2005 02:55:40

a ShellExecute (стандартная виндовая функция) не подойдёт? (это если для запуска соответсвующей программы)
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4053
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Сообщение Trezub » 28.11.2005 10:59:42

alexs писал(а): a ShellExecute (стандартная виндовая функция) не подойдёт? (это если для запуска соответсвующей программы)

хм, мне кажеться, что ShellExecute документ "blabla" без расширения не запустит, даже если он будет Word'овским. Разве нет?...

Но в любом случае мне это не подойдет, потому что нужно только определить тип - без запуска соответ. программы.
Trezub
новенький
 
Сообщения: 52
Зарегистрирован: 17.09.2005 21:23:04

Сообщение bw » 28.11.2005 11:15:11

Смотри StgOpenStorageEx.
Правда в winapi для fp нет описания этой функции, а так же необходимых интерфейсов. Постараюсь сегодня все что необходимо перенести из delphi. Исходник выложу здесь. Если нужно.

..bw
bw
 

Сообщение Trezub » 28.11.2005 13:39:20

bw писал(а): Постараюсь сегодня все что необходимо перенести из delphi. Исходник выложу здесь. Если нужно.

Очень (подчеркиваю красным) нужно. А если сегодня - то это просто великолепно. :)

а пока смотрю в инете что это вобще такое - StgOpenStorageEx.
Trezub
новенький
 
Сообщения: 52
Зарегистрирован: 17.09.2005 21:23:04

Сообщение noch » 29.11.2005 15:08:28

А все-таки человек советующий shellexecute не имел в виду запускать документы ;)
Он имел в виду что лучше использовать вызов для запуска программы file а я бы посовтеовал использовать для этого popen ;)
Открываешь pipe и читаешь из него ответ программы file, а что казается zlib.dll разве это проблема?
Аватара пользователя
noch
постоялец
 
Сообщения: 145
Зарегистрирован: 07.06.2005 09:45:49
Откуда: Armenia

Сообщение Trezub » 29.11.2005 17:25:18

noch, аааа... :)

Кто подскажет, программа file под *nix как определяет файлы Оффиса? Отличает Ворд и Ексель? Ибо прога под винду - на все документы Оффиса говорит - "документ Майкрософт Оффис". И разделения не делает на Ворд и Ексель..
Trezub
новенький
 
Сообщения: 52
Зарегистрирован: 17.09.2005 21:23:04

Сообщение bw » 29.11.2005 18:48:09

Код: Выделить всё
{$apptype console}
{$mode delphi}

uses
 SysUtils, Windows, ActiveX;

type
 EOleError = class(Exception);
 EOleSysError = class(EOleError)
 private
   FErrorCode: HRESULT;
 public
   constructor Create(const Message: String; ErrorCode: HRESULT; HelpContext: Integer);
   property ErrorCode: HRESULT read FErrorCode write FErrorCode;
 end;

constructor EOleSysError.Create(const Message: String; ErrorCode: HRESULT; HelpContext: Integer);
var
 S: String;
begin
 S:=Message;
 if S='' then
 begin
   S:=SysErrorMessage(ErrorCode);
   if S='' then FmtStr(S,'OLE error %.8x',[ErrorCode]);
 end;
 inherited CreateHelp(S,HelpContext);
 FErrorCode:=ErrorCode;
end;

procedure OleCheck(Result: HResult);
begin
 if Failed(Result) then raise EOleSysError.Create('',Result,0);
end;

{function LCIDToCodePage(ALcid: LongWord): Integer;
const
 CP_ACP = 0;
 LOCALE_IDEFAULTANSICODEPAGE = $00001004;
var
 ResultCode: Integer;
 Buffer: array [0..6] of Char;
begin
 GetLocaleInfo(ALcid, LOCALE_IDEFAULTANSICODEPAGE, Buffer, SizeOf(Buffer));
 Val(Buffer, Result, ResultCode);
 if ResultCode<>0 then Result:=CP_ACP;
end;}

function WCharFromChar(WCharDest: PWideChar; DestChars: Integer; const CharSource: PChar; SrcBytes: Integer): Integer;
begin
 {if GetVersion() and $80000000<>$80000000 then
 begin
   if Lo(GetVersion())>4 then
     DefaultUserCodePage:=3 else
     DefaultUserCodePage:=LCIDToCodePage(GetThreadLocale);
 end
 else
   DefaultUserCodePage := LCIDToCodePage(GetThreadLocale);
 Result:=MultiByteToWideChar(DefaultUserCodePage, 0, CharSource, SrcBytes, WCharDest, DestChars);}
 Result:=MultiByteToWideChar(3,0,CharSource,SrcBytes,WCharDest,DestChars);
end;

function StringToWideChar(const Source: String; Dest: PWideChar; DestSize: Integer): PWideChar;
begin
 Dest[WCharFromChar(Dest,DestSize-1,PChar(Source),Length(Source))]:=#0;
 Result:=Dest;
end;

function StringToOleStr(const Source: String): POleStr;
var
 SourceLen: Integer;
 Buffer: PWideChar;
begin
 SourceLen:=Length(Source);
 Buffer:=CoTaskMemAlloc((SourceLen+1)*SizeOf(WideChar));
 StringToWideChar(Source,Buffer,SourceLen+1);
 Result:=POleStr(Buffer);
end;


{procedure WriteStorage(Storage: IStorage);
var
 Enum: IEnumSTATSTG;
 Elem: TSTATSTG;
 n: Integer;
 s: String;
begin
 OleCheck(Storage.EnumElements(0,nil,0,Enum));
 repeat
   Enum.Next(1,Elem,@n);
   if n>0 then
   begin
     case Elem.dwType of
       STGTY_STORAGE  : s:='Storage';
       STGTY_STREAM   : s:='Stream';
       STGTY_LOCKBYTES: s:='LockBytes';
       STGTY_PROPERTY : s:='Property';
       else s:='';
     end;
     if s<>'' then s:=#09'- '+s;
     WriteLn(WideCharToString(Elem.pwcsName)+s);
   end;
 until n=0;
end;

procedure WriteOffice(FileName: PWideChar);
var
 Storage: IStorage;
 Stream : IStream;
 Mode: DWORD;
begin
 Mode:=STGM_READ or STGM_SHARE_EXCLUSIVE;
 OleCheck(StgOpenStorage(FileName,nil,Mode,nil,0,Storage));
 WriteStorage(Storage);
 OleCheck(Storage.OpenStream(#05'SummaryInformation',nil,Mode,0,Stream));
end;}

function GetTypeDocument(FileName: String): String;
var
 Storage: IStorage;
 OleStr: POleStr;
 Mode: DWORD;
 hr: HRESULT;

 function ExistName(Name: PWideChar): Boolean;
 var
   Stream: IStream;
 begin
   hr:=Storage.OpenStream(Name,nil,Mode,0,Stream);
   Result:=Succeeded(hr);
   if not Result and (hr<>HRESULT($80030002)) then OleCheck(hr);
 end;

begin
 Result:='unk';
 Mode:=STGM_READ or STGM_SHARE_EXCLUSIVE;
 OleStr:=StringToOleStr(FileName);
 try
   hr:=StgOpenStorage(OleStr,nil,Mode,nil,0,Storage);
   if Succeeded(hr) then
   begin
     if ExistName('Workbook')     then Result:='xls' else
     if ExistName('WordDocument') then Result:='doc';
   end else
     if hr<>HRESULT($80030050) then OleCheck(hr);
 finally
   CoTaskMemFree(OleStr);
 end;
end;


var
 Handle: THandle;
 Search: TSearchRec;
begin
 Handle:=FindFirst('*',faAnyFile and not (faDirectory or faVolumeId or faHidden),Search);
 while Handle=S_OK do
 begin
   WriteLn(Format('%s'#09' - %s',[Search.Name,GetTypeDocument(Search.Name)]));
   Handle:=FindNext(Search);
 end;
 FindClose(Handle);
end.


..bw
bw
 

Сообщение Trezub » 30.11.2005 11:07:32

bw, огромнейшее спасибо. Это восхитетельно, потому как уже над проблемой бьюсь давно.

Буду разбираться в коде, при первом запуске выдало ошибку:

"project raised exception class "EoleSyserror"
with message "недопустимое имя %1".


копошусь в коде. Может научусь умным вещам.
Trezub
новенький
 
Сообщения: 52
Зарегистрирован: 17.09.2005 21:23:04

Сообщение bw » 30.11.2005 14:25:57

У меня fpc 2.0.0, ОСь w2kpro sp4, если что. Этот код только для win32. И (уверен) что под win9x и winMe он не пойдет.

..bw
bw
 

Сообщение Trezub » 30.11.2005 15:32:18

bw, и схема работы проги для 98-ого не подходит? Нет возможности под 98 использовать?
Trezub
новенький
 
Сообщения: 52
Зарегистрирован: 17.09.2005 21:23:04

Сообщение bw » 30.11.2005 16:59:23

Х.з. надо пробовать. Ты под win98 запускал? Это под ней ошибка вылезла? Это же все COM, а под win9x COM был очень слабенький и во многом не совместимый с win2k и далее. А зачем win98, пора уже его выбросить. Я года три как выбросил. Можно (и не сложно на самом деле) и без COM структуру "хранилищ разрулить", но у меня сейчас на это нет времени.

..bw
bw
 

Сообщение Trezub » 30.11.2005 17:12:47

bw, пробывал по 98. У нас на работе везде 98-ой, поэтому вариант "онли с 2к" не сильно подходит.. Хотя можно сделать чтобы на 98-ом ругался, а на 2к работал.. посмотрим...
Trezub
новенький
 
Сообщения: 52
Зарегистрирован: 17.09.2005 21:23:04

Сообщение Alexander » 04.12.2005 13:40:44

Странно. А не проще считать первые два байта из файла ? Для ворда
они такие: D0 CF.
Alexander
 

Сообщение bw » 05.12.2005 05:06:26

Они и у Excel'я такие и еще у сотни других форматов посторенных на Mu$tDiE'вских контейнерах.
Сам Office, насколько я понял, не может распознать какой документ был ему предоставлен. Я определяю это по структуре контейнера. Хотя, наверное, правельныы это делать по ключевым свойствам. Но что это за свойства и какие значения они должны содержать мне не известно.
По структуре, это видно из моего кода формат определяется достаточно элементарно. Если в главном хранилище существует запись Workbook, то это Excel, если WordDocument, то это Word.

..bw
Аватара пользователя
bw
постоялец
 
Сообщения: 359
Зарегистрирован: 01.12.2005 11:36:23
Откуда: Усть-Илимск

След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 96

Рейтинг@Mail.ru