【问题标题】:Problem adding lots of strings to a TStringList向 TStringList 添加大量字符串时出现问题
【发布时间】:2009-12-14 21:46:18
【问题描述】:

我在将字符串添加到 TStringList 时遇到问题。我搜索了其他帖子,但找不到答案。

我想要做的是向 TStringList(超过 14000 个)添加大量字符串,但在此过程中的某个地方我得到了 EAccessViolation。这是我正在使用的代码:

procedure TForm1.FormCreate(Sender: TObject);
begin
    List := TStringList.Create;
    List.Duplicates := dupAccept;
end;

procedure TForm1.ButtonStartClick(Sender: TObject);
begin
    List.Clear;
    List.Add('125-AMPLE');
    List.Add('TCUMSON');
    List.Add('ATLV 4300');
    List.Add('150T-15');
    List.Add('TDL-08ZE');
    List.Add('RT20L');
    List.Add('SIN LINEA');
    List.Add('TIARA');
    List.Add('FL200ZK1');
    List.Add('FL250ZK1');
    List.Add('SIN LINEA');
    List.Add('CENTAURO-70 S.P.');
    List.Add('CORSADO');

{ This list continues to about 14000 strings...}

    List.Add('VOSJOD 2');
    List.Add('Z 125');
    List.Add('ZUMY');
    List.Add('NEW AGE 125');
    List.Add('SIN LINEA');
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    FreeAndNil(List);
end;

¿这段代码有什么问题?该列表包含重复的字符串,因此我将Duplicates 属性设置为dupAccept。我可以使用 LoadFromFile 加载列表,但我不想在我的应用程序之外有一个文本文件。

希望你能帮帮我!!!如果您需要更多信息,请告诉我。

非常感谢。非常感谢您的帮助。

【问题讨论】:

  • 您有 14,000 个字符串并且“某处”有问题。你可以说得更详细点吗?总是在同一个地方吗?如果你使用不同的字符串(更短、更长、不同的顺序)怎么办?
  • 您是否可能从代码中调用 ButtonStartClick,或者当您单击按钮时是否真的发生了错误?如果从代码中,您是否在 FormCreate 事件之前调用 ButtonStartClick(例如从您的构造函数中)?当您单击按钮时发生错误时:甚至调用了 FormCreate 吗?有一个 FormCreate 过程并不意味着它被分配给表单的 FormCreate 事件,也许你不小心在 objectinspector 中清除了该事件。
  • @Rob:你是对的。问题总是发生在同一行(在我发布的示例中,添加 3275 字符串时抛出异常)。当我使用如下较长的字符串时:'INSERT INTO T_ELEMENTOS (IdTablaOpciones, NombreElemento, Codigo, Columna1, Columna2) VALUES (4, ''125-AMPLE (11-0)'', 1, ''TANTA [11]' ', ''125-AMPLE [11]'');'异常发生在第 900 行左右。顺序似乎不是问题,因为我尝试了不同的字符串集。
  • @The_Fox:单击按钮时发生错误。我使用调试器确保在单击按钮之前调用了 FormCreate。
  • 如果您在该行之前创建一个刹车点并查看字符串列表对象是否仍然有效并尝试跨过有问题的行怎么办。这样您就可以查看调试器中发生的情况。

标签: delphi tstringlist


【解决方案1】:

使用外部文件的建议在此处标记。但是,您的帖子表明您希望没有外部文件。然后,我建议您将文件作为资源链接到可执行文件。您可以按照以下步骤轻松完成此操作:

将所有字符串放入名为 stringdata.txt(或您选择的任何名称)的文本文件中。然后创建一个您选择的任何名称的 .rc 文件并将以下内容放入其中(STRING_DATA 可以是您选择的任何标识符):

STRING_DATA RCDATA "stringdata.txt"

从 .rc 创建一个 .res 文件:

BRCC32 <name of rc>.rc

现在从源代码中引用此文件。将以下内容放在单元中的某个位置:

{$R <name of res>.res}

不是从文件流加载,而是从资源流加载:

StringData := TResourceStream.Create(HInstance, 'STRING_DATA', RT_RCDATA);
try
  List.LoadFromStream(StringData);
finally
  StringData.Free;
end;

如果您进行命令行自动构建,我建议您将 .rc 文件置于源代码管理之下,并在构建过程中构建 .res。这样,您还可以将 stringdata.txt 文件置于源代码控制之下,并且在下一次构建时会自动捕获任何编辑,而无需在每次 .txt 文件更改时显式构建 .res 文件。

【讨论】:

  • 感谢您发布此信息,我试图弄清楚如何在 DLL 中嵌入字典文件,但我无法从网络或帮助文件中弄清楚。
  • 艾伦:我会尝试你的建议。我会告诉大家它是否有效。
  • 艾伦:效果很好!一个非常优雅和简单的解决方案。非常感谢您的所有反馈!
【解决方案2】:

您使用的是什么 Delphi 版本?一些旧版本的内存管理器存在错误,当尝试将数组重新分配为太大的大小时,可能会导致访问冲突。

尝试将FastMM4 添加到您的项目中以替换旧的内存管理器,看看是否有帮助。

另外,您最好将列表保存在外部文件中。是的,它是另一个文件,但这也意味着您可以更改列表而无需重新编译整个程序。这也使得创建(和分发!)更新更容易。

【讨论】:

  • 问题是,14,000 个元素——54.6 KB——并不是一个真正的大数组。此外,TStrings 在 LoadFromFile 中使用的分配策略与对 Add 的一堆调用使用的分配策略相同。还有其他事情发生。
  • @Rob,这是一个超过 14,000 行的程序...对于单个程序来说相当大且不寻常。最后,生成的代码可能与 LoadFromfile 不同。
  • François,Mason 并没有暗示 code 的长度会成为问题。他建议 array 的大小会产生问题。但是,假设 14,000 行的过程正确运行,则无论哪种情况,数组的分配方式都完全相同。 LoadFromFile 调用 LoadFromStream,然后调用 SetTextStr,它只会在循环中调用 Add 14,000 次。对 Add 的调用次数相同,因此数组大小调整相同。因此我得出结论,数组的大小无关紧要。
【解决方案3】:

对于 AV 的原因,Mason 可能是正确的;这是一个相当大的数组。
附带说明一下,在对 StringList 进行如此长时间的处理时,建议用 BeginUpdate/EndUpdate 将其包围以避免触发任何更新事件。
即使您现在没有,也可能稍后添加它们,您会遇到问题。

【讨论】:

  • François:我使用了上面相同的代码,包括 BeginUpdate 和 EndUpdate 语句,得到了相同的结果...
  • TheSandman:我不认为 François 以任何方式暗示 Begin/End Update 会解决您当前的问题。他只是建议将其作为一种良好的一般做法,因为如果 TStringList 的特定实例具有处理 OnChangeOnChanging 事件的代码,那么您不希望这些事件在每个单独的更改时触发,因为它会对性能产生负面影响。
【解决方案4】:

在创建列表后立即将 list.capacity 设置为您计划添加的项目数。或者,将列表放在一个 RC 文件中(命名与您的项目名称不同)并将其添加到您的项目中。这会编译到您的应用程序中,但不涉及创建列表的可执行代码。

【讨论】:

    【解决方案5】:

    我还会担心 14,000 行过程的编译器完整性。人们发现了超出合理范围的其他情况会以各种方式破坏编译器。

    【讨论】:

      【解决方案6】:

      您可能还想尝试 THashedStringList,可以看到速度提升(虽然不在此函数中),但我不确定 add 方法是否有很大不同。

      【讨论】:

      • 他可能有重复,如代码所示。哈希在这里无济于事。
      • 原则上是这样(虽然速度损失会因重复频率而减轻),但 THashedStringList 将接受重复。
      • 值可以重复,但是键不能。哈希是如何工作的。哈希表中只能存在一个相同的键。然而,两个不同的键可以在内部散列到相同的值。如果 THashedStringList 接受重复的键/名称,则它已损坏。无论如何,哈希只有在他稍后搜索项目时才会有所帮助。简单地将项目添加到哈希实际上比字符串列表慢,因为必须对每个键进行哈希处理。
      • 不过,不管这个主意好不好,它都可以解决他的问题。
      【解决方案7】:

      尝试使用以下代码而不是您的代码将字符串添加到 StringList

      变量
      字符串:字符串;
      开始
      Str := '125-AMPLE' + #13#10;
      力量 := 力量 + 'TCUMSON' + #13#10;
      力量 := 力量 + 'ATLV 4300' + #13#10;
      力量 := 力量 + '150T-15' + #13#10;
      ......

      List.Text := 字符串;

      结束;

      【讨论】:

      • 由于字符串连接,这会很慢。我不会推荐这个代码。
      猜你喜欢
      • 1970-01-01
      • 2015-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-29
      相关资源
      最近更新 更多