Если это разовое дело, и у вас есть доступ к установке 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 MEMORY
(и DISPLAY 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. Однако самозаписывающийся импортер мог бы использовать знаки кодовой страницы.
Ваш код предполагает, что .mem-файл - это текст, которого нет. Это файл двоичного формата. Они похожи на формат файла Clipper .mem; вы можете найти этот формат [здесь] (http://www.zelczak.com/clipp_en.htm), которого может быть достаточно, чтобы начать работу по правильному пути. –
Чтение файлов достаточно просто. Как вы предлагаете разобрать файл? –
Хотя список строк неверен для двоичных данных, обратите внимание, что вы можете использовать LoadFromFile и избегать создания объекта потока. Также обратите внимание, что TFile.ReadAllText избегает необходимости в списке строк. –