вопрос о переменных области delphi - PullRequest
1 голос
/ 07 апреля 2011

Я заполняю раздел, читаемый из файла, для итерации по парам ключ-значение. итерация была решена в Delphi словаре итерация .

проблема в том, что значения в dict не сохраняются, вероятно, проблема в области видимости с переменными. я больше привык к Java ... значения существуют непосредственно после назначения их в словарь в процедуре parsetextfile, а затем теряются:

program parsefile;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes, StrUtils, Dialogs, Generics.collections;

var key : string;
    dict: TDictionary<String, TStringlist>;
    KeysList, Valuename: TStringList;
    KeyName: string;
    i: integer;

function DeleteSpaces(str: string): string;
var
 i: Integer;
begin
 i:=0;
while i<=Length(str) do
  if str[i]=' ' then Delete(str, i, 1)
  else Inc(i);
  Result:=str;
end;

procedure HandleOneKey(KeyIndex:Integer; PrevKeys:string);
var L:TStringList;
    i:Integer;
    Part: string;
    KeyName: string;
begin
  KeyName := KeysList[KeyIndex];
  L := dict[KeyName];
  for i:=0 to L.Count-1 do
  begin
    writeln(L[i]);
    Part := KeyName + '=' + L[i];
    if KeyIndex = (KeysList.Count-1) then
      WriteLn(PrevKeys + ' ' + Part)
    else
      HandleOneKey(KeyIndex+1, PrevKeys + ' ' + Part);
  end;
end;

procedure Split(const Delimiter: Char;Input: string;const Strings: TStrings);
begin
   Strings.Clear;
   Strings.Delimiter := Delimiter;
   Strings.DelimitedText := Input;
end;

procedure parsetestfile;
  var testfile: Textfile;
      text: string;
      splitarray: TStringList;
      subsplit1, subsplit2: TStringList;
begin
  splitarray := TStringList.Create;
  subsplit1:= TStringList.Create;
  subsplit2:= TStringList.Create;
  AssignFile(testfile, 'g:\testfile.txt') ;
  Reset(testfile);

  while not Eof(testfile) do
  begin
    ReadLn(testfile, text);
    if AnsiContainsStr(text, '=') then
    begin
      Split('=', text, splitarray);
      splitarray[0] := trim(splitarray[0]);
      splitarray[1] := DeleteSpaces(splitarray[1]);
      if AnsiStartsStr('data', splitarray[0]) then
      begin
        split(' ', splitarray[0], subsplit1);
        splitarray[0]:=subsplit1[1];
        split(',', splitarray[1], subsplit2);
        dict.Add(splitarray[0], subsplit2);
        for ValueName in dict.Values do
        begin
          for i := 0 to Valuename.Count - 1 do
          write('Values are : '+ Valuename[i]);
        writeln;
        end;//for
      end;//end-data-check
    end;//end-=-check
  end;//while
  CloseFile(testfile);
  splitarray.Free;
  subsplit1.Free;
  subsplit2.Free;
end;

begin
  dict := TDictionary<String, TStringlist>.Create;
  parsetestfile;
  KeysList := TStringList.Create;

  for KeyName in dict.Keys do
    KeysList.Add(KeyName);
  for i := 0 to Keyslist.Count - 1 do
  begin
    writeln('Keylist Items: ' + Keyslist[i]);
  end;
  if KeysList.Count > 0 then
  begin
    HandleOneKey(0, '');
  end;
  dict.Destroy;
  Keyslist.Free;
  WriteLn('Press ENTER to make the window go away');
  ReadLn;
end.

1 Ответ

1 голос
/ 07 апреля 2011

Top Edit

Теперь я понял, что вы более привыкли к Java, и это объясняет вашу проблему.Java использует сборщик мусора: если у вас есть ссылка на что-то, то одна вещь действительная .Delphi не использует GC, вы отвечаете за освобождение всей выделенной памяти.Это приводит ко второй проблеме: вы можете освободить память, на которую ссылаетесь, ничто не мешает вам сделать это.В своей процедуре parsetestfile вы добавляете subsplit2 в словарь, поэтому сохраняете копию этой ссылки.Позже в той же процедуре вы освобождаете subsplit2, поэтому ваш словарь теперь содержит ссылку на то, что Delphi считает «свободной памятью»!

С Delphi вам нужно быть очень осторожным и обдумывать жизненный циклуправление.В этом случае вы, очевидно, не можете освободить subsplit2 в самой процедуре parsetestfile, но вам нужно освободить его позже.Вам нужно будет освободить его, когда вы освободите Dict, посмотрите мой первоначальный код, чтобы узнать, как это сделать.

* Рекомендовано


Вот ваш код с множеством вещейфиксированный.Пожалуйста, прочитайте комментарии, я вставлял комментарии везде, где я что-то изменил.

Он компилируется и значения выдерживают процедуру разбора, но я не уверен, чего вы хотите достичь, и вы забыли предоставить образец текстового файла: Iпришлось "сделать один".

program Project23;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes, StrUtils, Dialogs, Generics.collections;

var deviceid, key, topmodule : string;
    dict: TDictionary<String, TStringlist>;
    KeysList: TStringList;
    KeyName: string;
    i: integer;

function DeleteSpaces(str: string): string;
var
 i: Integer;
begin
 i:=0;
while i<=Length(str) do
  if str[i]=' ' then Delete(str, i, 1)
  else Inc(i);
  Result:=str;
end;

procedure HandleOneKey(KeyIndex:Integer; PrevKeys:string);
var L:TStringList;
    i:Integer;
    Part: string;
    KeyName: string;
begin
  KeyName := KeysList[KeyIndex];
  L := dict[KeyName];
  for i:=0 to L.Count-1 do
  begin
    writeln(L[i]);
    Part := KeyName + '=' + L[i];
    if KeyIndex = (KeysList.Count-1) then
      WriteLn(PrevKeys + ' ' + Part)
    else
      HandleOneKey(KeyIndex+1, PrevKeys + ' ' + Part);
  end;
end;

procedure Split(const Delimiter: Char;Input: string;const Strings: TStrings);
begin
   Strings.Clear;
   Strings.Delimiter := Delimiter;
   Strings.DelimitedText := Input;
end;

procedure parsetestfile;
  var testfile: Textfile;
      text: string;
      splitarray: TStringList;
      subsplit1, subsplit2: TStringList;
      ValueName:TStringList; // Never Ever ignore compiler warnings!
      i: Integer; // Never Ever ignore compiler warnings!
begin
  splitarray := TStringList.Create;
  subsplit1:= TStringList.Create;      
  AssignFile(testfile, 'c:\temp\testfile.txt') ;
  Reset(testfile);

  while not Eof(testfile) do
  begin
    ReadLn(testfile, text);
    if AnsiContainsStr(text, '=') then
    begin
      Split('=', text, splitarray);
      splitarray[0] := trim(splitarray[0]);
      splitarray[1] := DeleteSpaces(splitarray[1]);
      if AnsiStartsStr('data', splitarray[0]) then
      begin

        subsplit2:= TStringList.Create; // Moved the creation of subsplit2 over here, because you need one fresh list for every line of text you read.

        split(' ', splitarray[0], subsplit1); // can't split on SPACE because the previous split allready broke the text at "=" and at SPACE. That's how DelimitedText works!
        // splitarray[0]:=subsplit1[1]; // splitarray[0] already contains the stuff before "="; And you should check the nubmer of lines in subsplit1!
        split(',', splitarray[1], subsplit2);
        dict.Add(splitarray[0], subsplit2);
        for ValueName in dict.Values do
        begin
          for i := 0 to Valuename.Count - 1 do
          writeLN('Values are : '+ Valuename[i]); // Only use Write when you intend to write the line terminator later
        writeln;
        end;//for
      end;//end-data-check
    end;//end-=-check
  end;//while
  CloseFile(testfile);
  splitarray.Free;
  subsplit1.Free;
  // subsplit2.Free; // Ooops! You're freeing Subsplit2, after you added it as a value in the dict.
end;

begin
  dict := TDictionary<String, TStringlist>.Create;
  parsetestfile;
  KeysList := TStringList.Create;

  for KeyName in dict.Keys do
    KeysList.Add(KeyName);
  for i := 0 to Keyslist.Count - 1 do
  begin
    writeln('Keylist Items: ' + Keyslist[i]);
  end;
  if KeysList.Count > 0 then
  begin
    HandleOneKey(0, '');
  end;
  dict.Free; // dict.Destroy; // never call "Destroy" directly, call .Free.
  Keyslist.Free;
  WriteLn('Press ENTER to make the window go away');
  ReadLn;
end.
...