【问题标题】:delphi scope of variables questiondelphi变量范围问题
【发布时间】:2011-04-07 12:14:56
【问题描述】:

我填充一个 tdictionary ,从文件中读取,以迭代键值对。迭代已在delphi dictionary iterating 中解决。

问题是 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.

【问题讨论】:

  • 首先请不要dict.Destroydict.Free 是正确的。

标签: delphi variables scope tdictionary


【解决方案1】:

顶部编辑

我现在看到您更习惯 Java,这可以解释您的问题。 Java 使用垃圾收集器:如果您有对某物的引用,则该物有效。 Delphi 不使用 GC,您负责释放您分配的所有内存。这导致了第二个问题:你可以释放你持有引用的内存,没有什么能阻止你这样做。在您的parsetestfile 过程中,您将subsplit2 添加到字典中,因此您保留了该参考的副本。稍后在同一过程中,您将释放 subsplit2,因此您的字典现在包含对 Delphi 认为是“空闲内存”的引用!

使用 Delphi,您需要非常小心谨慎地进行生命周期管理。在这种情况下,您显然无法在parsetestfile 过程本身中释放subsplit2,但您确实需要稍后释放它。释放Dict 时需要释放它,请查看我的初始代码以了解如何执行此操作。

*推荐


这是您修复了很多问题的代码。请阅读 cmets,我在任何更改的地方都插入了 cmets。

它可以编译并且值在解析过程中仍然存在,但我不确定您想要实现什么并且您忘记提供示例文本文件:我必须“弥补”。

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.

【讨论】:

  • 非常感谢您的建议。该文件如下:“data test”="1,2,3",例如,“data temp”="a,b,c"。这就是为什么我想在“”处拆分。我想摆脱前面的“数据”
  • 只剩下一个问题...如果 value-stringlist 小于前一个,它将无法正确运行。例如,如果您有两行包含 3 个值,然后一行包含两个值,则只会打印两个值。如果您首先放置第 2 行的值,它会起作用。
  • 现在说得通了,我使用的是data a=1,2,3,没有引号,所以第一次拆分是在= 和空间上拆分。
  • 非常感谢。只有值字符串大小的最后一个错误是奇怪的。我使用引号是因为我读到了一个错误;)
  • 感谢您对 java 的内存处理差异的解释!
猜你喜欢
  • 2016-08-30
  • 2011-11-21
  • 2011-04-13
  • 2011-10-20
  • 2016-06-21
  • 2011-04-10
  • 2011-08-11
  • 2010-11-22
  • 1970-01-01
相关资源
最近更新 更多