1

我填写一个从文件中读取的字典,以迭代键值对。迭代在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.
4

1 回答 1

2

顶部编辑

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

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

*推荐


这是您的代码,其中修复了很多东西。请阅读评论,我在任何更改的地方都插入了评论。

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

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.
于 2011-04-07T12:32:17.967 回答