2016-01-02 5 views
0

Я пытаюсь прочитать .MEM file с помощью Delphi. Это файлы переменной памяти FoxPro. Я попытался прочитать с помощью TFileStream и загрузить в TStringList. Но он возвращает только первое слово.Как читать файлы с переменной памятью FoxPro (.MEM) с Delphi

F := TFileStream.Create(sFile, fmOpenRead); 
L := TStringList.Create; 
try 
    F.Position := 0; 
    L.LoadFromStream(F); 
    ShowMessage(L.Text); 
finally 
    F.Free; 
    L.Free; 
end; 

Причина в том, что я хочу перенести некоторые полезные значения .MEM из старой программы в мою новую программу. Спасибо за любую помощь.

+3

Ваш код предполагает, что .mem-файл - это текст, которого нет. Это файл двоичного формата. Они похожи на формат файла Clipper .mem; вы можете найти этот формат [здесь] (http://www.zelczak.com/clipp_en.htm), которого может быть достаточно, чтобы начать работу по правильному пути. –

+0

Чтение файлов достаточно просто. Как вы предлагаете разобрать файл? –

+0

Хотя список строк неверен для двоичных данных, обратите внимание, что вы можете использовать LoadFromFile и избегать создания объекта потока. Также обратите внимание, что TFile.ReadAllText избегает необходимости в списке строк. –

ответ

0

Чтение двоичных файлов .mem не является правильным способом продолжения. Правильное решение - заставить VFP экспортировать данные. Он знает, как читать. Получите VFP для экспорта в известный формат и прочитайте это. Это стандартный подход к миграции данных.

+0

Вы предполагаете, что OP имеет доступ к соответствующей версии Fox (IDE), которая не обязательно предоставляется. Файлы MEM были особенно популярны в пред-VFP (например, FPD, FPW), в то время как VFP часто присутствует в MEM, кроме ЕВРОПЕЙСКОГО.МЕМ, который поставляется с VFP. – DarthGizka

+0

@ Darth Это разумное предположение. –

+0

Нет, это не так. Кроме того: VFP (** любой ** VFP) может легко прочитать тестовый файл с помощью простого теста «ОТДЫХ ОТ», что, безусловно, является самым быстрым способом его проверки. Тем не менее, сброс содержимого .MEM в формате, импортируемом Delphi, - это другое дело, особенно с марками double и datetime (которые неизбежно теряют точность при преобразовании в текст). Fox не имеет средств перечислить переменные памяти, кроме синтаксического анализа вывода 'DISP MEMO' или' LIST MEMO'; имена переменных должны быть извлечены из этого, sysvars удален, значения запрашиваются и преобразуются. – DarthGizka

0

Если это разовое дело, и у вас есть доступ к установке VFP, то есть к IDE, а не только к времени исполнения, то предложение Дэвида Хеффернана, безусловно, является самым разумным способом. В этом случае вы можете загрузить и проверить .mem через

release all extended && to clear away all existing memvars 
restore from foo  && assuming the file in question is named FOO.MEM 
activate window Locals && inspect the variables... 
list memory to foo  && or list them to FOO.TXT 
modify file foo.txt 

Однако LIST MEMORYDISPLAY MEMORY) также включают в себя все системные переменные - вещи, которые начинаются с символа подчеркивания - которые должны были бы быть разобраны прочь.

Если это постоянное дело - требуется повторный импорт, и вы знаете, какие переменные вам нужны, тогда есть два довольно чистых и простых способа.

Первый действует только в том случае, если на компьютере, на котором должна запускаться программа Delphi, установлена ​​VFP IDE. В этом случае вы можете создать экземпляр VFP из Delphi (оставить его невидимым), он читал в .mem, а затем опрашивать отдельные переменные:

procedure fetch_variables_from_MEM (mem_filename: string; var_list: CFoos); 
var 
    fox: Variant; 
    foo: CFoo; 
begin 
    fox := CreateOleObject('VisualFoxpro.Application.9'); 
    try 
     fox.DoCmd('release all extended'); 
     fox.DoCmd('restore from ' + mem_filename); 
     for foo in var_list do 
     foo.Value := fox.Eval('m.' + foo.Name); 
    finally 
     fox.Quit; // AutoQuit not supported 
    end; 
end; 

я приукрасил некоторые детали, как, что CoInitialize() должен быть вызван на резьбе где-то перед вызовом этого, и я принял подходящие определения для списка переменных (список/коллекция гипотетических объектов CFoo), но набросанный контур работает - даже в 64-битном Delphi.

Преимущество в том, что такие вещи, как значения даты и времени, поступают как TDateTime благодаря инфраструктуре COM и использованию вариантов.

Второй простой способ применим, если IDE не доступна на компьютере, где программа Delphi будет использоваться, но у вас есть доступ к IDE где-то, так что вы можете построить небольшой COM сервер:

define class FoxWrapper as custom olepublic 
    function Eval (cExpression as string) as variant 
     return evaluate(m.cExpression) 
    procedure DoCmd (cCommand as string) 
     &cCommand 
enddefine 

Это может быть использовано вместо «VisualFoxPro.Application.9» в приведенном выше примере. Примечание. Для 64-разрядного Delphi вам необходимо построить это как сервер вне процесса (то есть EXE). Кроме того, это может противоречить условиям лицензии VFP.

Для получения доступа непосредственно к данным, вот некоторые быстрые & грязный код Delphi, который я смоделировал после некоторых материалов FoxPro, которые я закодировал давно и обновил для VFP9. Это код с доказательством принципа с упрощенной обработкой массивов и другими компромиссами для изложения; он испытывает недостаток в шуме производственного качества, обусловленном определением языка Delphi и его округлым временем исполнения.

type 
    TMEMVarHeader = packed record 
     var_name: array [0..10] of AnsiChar; 
     mem_type: AnsiChar;    // 0ACDHLNOQYacdhlnoqy 
     big_size: UInt32;     // only if mem_type == 'H' 
     width : Byte;     // special meaning if mem_type == 'H' 
     decimals: Byte; 
     padding : array [0..13] of Byte; // 0 0 0 0 0 0 0 3 0 0 0 0 0 0 
    end; 

    SizeOf_TMEMVarHeader_eq_32 = true .. SizeOf(TMEMVarHeader) = 32; 

    TMEMVarInfo = record 
     header: TMEMVarHeader; 
     null_t: AnsiChar; 
     name : AnsiString; 
     value : Variant; 
     function ReadFromStream (stream: TStream): Boolean; // false if EOF 
    end; 

function TMEMVarInfo.ReadFromStream (stream: TStream): Boolean; 
const 
    DELPHI_EPOCH = 2415019.0; 
var 
    header_bytes_read: Integer; 
    name_length: UInt16; 
    text_length: UInt32; 
    array_dim_1: UInt16; 
    array_dim_2: UInt16; 
    d: TDate;   // 64-bit double 
    l: Boolean; 
    n: Double;   // 64-bit double 
    q: array of Byte; 
    c: AnsiString; 
    t: TDateTime;  // 64-bit double 
    y: Int64; 
    binary: Boolean; 
    i: Cardinal; 
    a: array of Variant; 
    v: TMEMVarInfo; 
begin 
    name := ''; value := Unassigned; 
    header_bytes_read := stream.Read(header, SizeOf(header)); 
    if header_bytes_read <> Sizeof(header) then begin 
     if not ((header_bytes_read = 1) and (header.var_name[0] = #26)) then 
     raise Exception.Create('unexpected MEM file format (problem reading header)'); 
     result := false; // EOF 
     EXIT; 
    end; 
    result := true; 
    // variable name 
    if header.var_name[0] = #0 then begin // long variable name 
     assert(header.mem_type = LoCase(header.mem_type)); 
     stream.ReadBuffer(name_length, Sizeof(name_length)); 
     SetLength(name, name_length); 
     stream.ReadBuffer(name[1], name_length); 
    end else begin 
     assert(header.mem_type = UpCase(header.mem_type)); 
     name := header.var_name; 
    end; 
    // variable value 
    case UpCase(header.mem_type) of 
     'A': 
     begin 
      stream.ReadBuffer(array_dim_1, SizeOf(array_dim_1)); 
      stream.ReadBuffer(array_dim_2, SizeOf(array_dim_2)); 
      if array_dim_2 = 0 then // it's a vector, not an array 
       array_dim_2 := 1; 
      SetLength(a, array_dim_1 * array_dim_2); 
      for i := 0 to array_dim_1 * array_dim_2 - 1 do begin 
       if not v.ReadFromStream(stream) then 
        raise Exception.Create('error reading array element'); 
       a[i] := v.value; 
      end; 
      value := a; 
     end; 
     '0': begin stream.ReadBuffer(null_t, 1); value := Null; end; 
     'C', 'H', 'Q': 
     begin 
      if UpCase(header.mem_type) = 'H' then begin // length > 254 
       binary := header.width <> 0; 
       text_length := header.big_size; 
      end else begin 
       binary := UpCase(header.mem_type) = 'Q'; 
       text_length := header.width; 
      end; 
      if binary then begin 
       SetLength(q, text_length); stream.ReadBuffer(q[0], text_length); value := q; 
      end else begin 
       SetLength(c, text_length); stream.ReadBuffer(c[1], text_length); value := c; 
      end; 
     end; 
     'D': begin stream.ReadBuffer(d, Sizeof(d)); if d > 0 then d := d - DELPHI_EPOCH; VarCast(value, d, varDate); end; 
     'L': begin stream.ReadBuffer(l, Sizeof(l)); value := l; end; 
     'N': begin stream.ReadBuffer(n, Sizeof(n)); value := n; end; 
     'T': begin stream.ReadBuffer(t, Sizeof(t)); if t > 0 then t := t - DELPHI_EPOCH; value := t; end; 
     'Y': begin stream.ReadBuffer(y, Sizeof(y)); VarCast(value, y/10000.0, varCurrency); end; 
    else 
     raise Exception.Create('unexpected type ''' + header.mem_type + ''' in MEM file'); 
    end; 
end; 

для чтения .mem, создать переменную TFileStream и TMEMVarInfo, а затем прочитать переменные один на один, пока var_info.ReadFromStream(stream) возвращается false.

Примечание: байт со смещением 19h (показано как 3 в комментарии структуры) является идентификатором кодовой страницы. Значения такие же, как those found in .DBF headers, то есть 1 для DOS 437, 3 для Windows 1252 и т. Д. Однако, несмотря на то, что VFP сохраняет эти идентификаторы при записи .MEM, все новые версии VFP, которые я тестировал , полностью игнорируют эти метки кодовой страницы при загрузке .MEM. Однако самозаписывающийся импортер мог бы использовать знаки кодовой страницы.