【问题标题】:Delphi - Split Lines(Program not responding), not spliting file size (TTHREAD)Delphi - 分割线(程序没有响应),不分割文件大小(TTHREAD)
【发布时间】:2015-08-15 03:28:35
【问题描述】:

希望每个人都做得很好。 我只有一个问题,在 Delphi 编程中...... 我有一个备忘录,它为我加载了一个文件 .txt ,它包含大行,比如 50,000Lines....

所以我想将这些行拆分为 5000 行,然后将它们加载到新的备忘录中。 例如第一次拆分,它将在新文件文本上拆分 5000 行,然后将其加载到新备忘录上,加载后(删除 5000 的文件),当然对于大 LINES 将是 45000。

第二次拆分它将从 45,000 拆分 5000 ,大行将是 40,000,新行将是 5000(文件文本)。

我为什么要拆分文件文本上的行然后加载它的原因,因为当我将文件拆分为备忘录时程序没有响应(没有响应)。

procedure TForm1.Button1Click(Sender: TObject);
var count,i ,X,m:integer;
begin
Memo2.Clear;
Label1.Caption:=IntToStr(Memo1.Lines.Count) ;
if Memo1.Lines.Count > 5000 then
X:=5000
else X:= Memo1.Lines.Count ;
for  count:=0 to x do
  begin

  Memo2.lines.add(Memo1.lines.Strings[0]);
  Memo1.Lines.Delete(0);

             Memo2.Text:=Trim(Memo2.text);

end;
end;

这是我用来将小行拆分为其他小行的代码...

在备忘录中,但当你有 100 万行时,程序将停止回答。

我添加了 trim(memo2.text) ;删除末尾的空行。

那么我如何分割行,而不是文件大小(因为它会破坏行)......我如何像我对文件文本所说的那样分割大行,然后加载并删除它,然后当我重新单击一个按钮时,它将对其他行执行相同的操作....

我知道我们必须使用 TThread 类,但我不知道如何用我的代码实现它...谢谢!

谢谢。

【问题讨论】:

  • 线程不是解决方案,因为如果不与主线程同步,它们就无法与 GUI 控件交互(这违背了使用线程的目的)。为什么需要使用备忘录来拆分内容?用户显然不能同时看到所有的备忘录控件。您的代码的意图是什么?显示与拆分为文本文件无关。
  • 我会再试一次。目前尚不清楚您是否需要帖子中的备忘录(视觉控件)。目前尚不清楚您是在尝试向用户显示内容,还是只是(并且错误地)尝试将备忘录用于错误的事情。所以我会再问一次:你的实际目标是什么?您想向用户显示备忘录中的内容吗?你想玩瓷砖的内容吗?你真的能读懂我一直问你而你似乎一直忽略的问题吗? 你到底想做什么?如果你懒得解释,你的问题不属于这里。
  • 澄清您的问题以表明您的目标是否是*向用户显示备忘录的内容(正确使用(,或者您是否正在使用备忘录(错误地)作为执行其他任务的工具。到目前为止,您的帖子和 cmets 并没有说明您是否只是正确使用了 UI 控件,或者您是否在做一些完全错误的事情,用户在此过程中使用了 UI 控件,并且反复尝试要求您澄清似乎失败了。*您想要做什么?它们是独立且不同的事情,所以只有一个选择。哪一个你选吗?
  • 线程不是答案。备忘录不是答案。
  • 如果您停止玩游戏并明确您的要求,我们或许可以提供帮助。我通常会处理大于 1GB 的文件,并允许用户浏览内容。如果您简单地说明您的要求,正如我之前多次问过的那样,我敢肯定这里有人可以提供帮助。如果您仍然固执己见,那么让我们结束这个问题并转向真正需要我们帮助的人

标签: delphi


【解决方案1】:

线程不是问题的答案。您不应该需要线程来执行此任务。问题出在这里:

Memo1.Lines.Delete(0);

循环执行此操作对于备忘录而言非常昂贵。文本的第一部分被删除,其余部分向上移动。

最好的办法是:

  1. 将整个源移动到TStringList
  2. 将该字符串列表的前 5000 行处理为单个字符串。
  3. 使用SelText := ... 将整个字符串一次性添加到备忘录中。

完成后,一键清除原始备忘录。

【讨论】:

  • 这不是这个网站的真正目的。对不起。
  • 那是你学习的时候了。本网站旨在供程序员使用。如果您不知道如何编写代码和学习,那么它不适合您。在这种情况下,您需要聘请一名程序员。
  • @JohnGOLFoil - 请停止骚扰大卫,要求他提供代码。他给了你一个答案,你来执行它。我们都是这里的志愿者。很高兴他花时间以任何方式回答你。这些 cmets 现在需要停止。
  • 他开始在 Facebook 上向我发送垃圾邮件。他至少 10 次称我为种族主义者,现在他无法理解我不会成为他的朋友。
  • @Jens 很难理解。可能需要治疗。
【解决方案2】:

如前所述

使用 TStringList 加载和处理您的数据,使用 TMemo 显示它。

请记住:如果你做不到,那就假装它!

而且您无法在合理的时间内将 50000 行加载到标准 VCL 控件中。

所以我为您制作了一个小型示例应用程序,向您展示如何伪造它:

你需要在演示中添加错误处理,我只是采取了最短和最乐观的路径。

首先我需要一些数据,所以我生成了一个包含随机数据的文件:

procedure TForm25.GenereateTextFile(const aLines: Integer);
var
  Buffer: TStringlist;
  Line: string;
  i, j: Integer;
begin
  Buffer := TStringlist.Create;
  try
    for i := 1 to aLines do
    begin
      SetLength(Line, Random(200));
      for j := 1 to Length(Line) do
        Line[j] := Chr(ord('a') + Random(26));
      Buffer.Add(Line);
    end;

    Buffer.SaveToFile('dummy.txt');
  finally
    Buffer.Free;
  end;
end;

我称之为 FormCreate:

procedure TForm25.FormCreate(Sender: TObject);
begin
  GenereateTextFile(50000);
end;

然后我们需要生成一些GUI来显示文件:

在表单上放置一个 TTimer 并将它的 Interval 设置为 50,并为其分配一个 OnTimer 事件

过程 TForm25.Timer1Timer(Sender: TObject); 开始 Timer1.Enabled := 假; GenerateGUI(Self, 'dummy.txt'); 结束;

剩下的就是生成实际的 GUI:

procedure TForm25.GenerateGUI(const aForm: TForm; const aFileName: TFilename; aLinesPerPage: Integer = 5000);
var
  Buffer: TStringlist;
  Tmp: TStringlist;
  i, j: Integer;
  PageControl: TPageControl;
  TabSheet: TTabSheet;
  Memo: TMemo;
begin
  Buffer := TStringlist.Create;
  Buffer.LoadFromFile(aFileName);
  Buffer.BeginUpdate;

  PageControl := TPageControl.Create(aForm);
  PageControl.Parent := aForm;
  PageControl.Align := alClient;
  i := 0;

  for i := 0 to (Buffer.Count div aLinesPerPage) - 1 do
  begin
    TabSheet := TTabSheet.Create(PageControl);
    TabSheet.PageControl := PageControl;
    TabSheet.Caption := Format('Lines %d to %d', [i * aLinesPerPage, (i + 1) * aLinesPerPage - 1]);

    Memo := TMemo.Create(TabSheet);
    Memo.Parent := TabSheet;
    Memo.Align := alClient;

    j := 0;

    Tmp := TStringlist.Create;
    Tmp.BeginUpdate;
    Application.ProcessMessages;

    while j < aLinesPerPage do
    begin
      Tmp.Add(Buffer[0]);
      Buffer.Delete(0);
      inc(j);
    end;

    Memo.Lines.Assign(Tmp);
    Tmp.Free;
    Application.ProcessMessages;
  end;
  FreeAndNil(Buffer);
end;

您会看到页面一一显示并显示数据。

它一个一个出现的原因是因为我打电话给Application.ProcessMessages;,它让一点点机器人的速度变慢了很多,你告诉你的用户程序是活着的。

【讨论】:

  • 我不明白?如何从按钮调用程序? ,你的意思是当我点击一个按钮时我启用了计时器?
  • 在表单上放置一个按钮并单击它两次,然后编写以下代码:GenerateGUI(Self, 'dummy.txt');
  • dummy.txt 有什么用,我可以带我的 50,000 行吗?或者我必须做第一个代码来生成这些随机行?因为我不需要随机线...
  • 将 dummy.txt 替换为您的文件的真实名称。
  • @Jens 请重新考虑。请不要喂吸血鬼。
【解决方案3】:

我不确定使用 TMemo 对您的应用程序有多重要,但如果您只想将一个文本文件拆分为多个文件,其中的行数相同(可能除了最后一个一个,当然),您根本不需要 TMemo,也不需要任何 Delphi 容器类,如 TStringList 或 TStream。关键是,你已经有了一个完美的容器,即文件系统。

以下是一个 Delphi 控制台应用程序,它使用自 Delphi 之前在 Object Pascal 中的文件访问方法(我可以听到一些 Delphi 粉丝对我以这种方式向您展示的啧啧啧啧,但 imo 找到其他方法并没有什么坏处,完全有效的做事方式,尤其是避免进入 Delphi 容器类的语义和用法)。

希望它的工作原理是不言而喻的。创建测试文件后,它会逐行读取它并将其输出到按顺序编号的输出文件,直到其中包含您想要的行数,或者没有更多行可读取。在我的便携式设备上,从开始到结束需要 0.078 秒。

代码:

program FileSplit;

{$APPTYPE CONSOLE}

uses
  SysUtils;

const
  MaxLines = 100000;
  LinesPerFile = 5000;
  BufferSize = 32768;

var
  MainFileName : String;

procedure CreateFile(AFileName : String);
var
  AFile : Text;
  Line : Integer;
  Buffer : Array[1..BufferSize] of Char;
  S : String;
begin
  AssignFile(AFile, AFileName);
  SetTextBuf(AFile, Buffer, SizeOf(Buffer));
  Rewrite(AFile);

  Line := 1;
  while Line <= MaxLines do begin
    S := Format('This is line: %d', [Line]);
    writeln(AFile, S);
    Inc(Line);
  end;
  Close(AFile);
end;

procedure SplitFile(AFileName : String);
var
  AFile : Text;
  SubFileNumber : Integer;
  Buffer : Array[1..BufferSize] of Char;

  procedure CreateSubFile(SubFileNumber : Integer);
  var
    OutFile : Text;
    OutBuffer : Array[1..BufferSize] of Char;
    SubFileName : String;
    S : String;
    Line : Integer;
  begin
    SubFileName := ExtractFilePath(AFileName) + IntToStr(SubFileNumber) + '.Txt';
    writeln(SubFileName);

    Assign(OutFile, SubFileName);
    SetTextBuf(OutFile, OutBuffer, SizeOf(OutBuffer));
    Rewrite(OutFile);

    Line := 1;

    // the following reads the input file a line at a time
    // and outputs it to the current output file
    // until there's nothing left to read or the lines
    // per output file is reached

    while (Line <= LinesPerFile) and not Eof(AFile) do begin
      readln(AFile, S);
      writeln(OutFile, S);
      Inc(Line);
    end;

    Close(OutFile);
  end;

begin
  Assign(AFile, AFileName);
  SetTextBuf(AFile, Buffer, SizeOf(Buffer));
  Reset(AFile);

  SubFileNumber := 1;
  while not Eof(AFile) do begin
    CreateSubFile(SubFileNumber);
    Inc(SubFileNumber);
  end;
  Close(AFile);
end;

begin
  MainFileName:= GetEnvironmentVariable('Temp');
  MainFileName := IncludeTrailingPathDelimiter(MainFileName);
  MainFileName := MainFileName + 'Main.Txt';

  CreateFile(MainFileName);
  writeln(Format('File %s created', [MainFileName]));

  SplitFile(MainFileName);
  readln;

end.

【讨论】:

  • NICE CODE ,只需要小改动...我想拆分 LINES,而不是文件 SIZE,因为它是不同的术语,当您拆分行时,您从源中获取行,当您拆分文件大小时文件和文件之间是不同的,你会毁掉你的行......所以我想从 50,000 个源中“剪切”5000 行。当我点击一个按钮时。当我重新点击时,它会从 45,000 中取出 5000,当我第三次点击时,它会花费 5000,它会休息 35,000 等等
  • 我不确定您要在“行”和“大小”之间做出什么区分 - 无论单行的长度如何,我的代码都可以工作。但无论如何......
  • 当您将大文件 SIZE 拆分为小文件时,在文件和文件之间您将破坏最后一行。但是,当您每次单击按钮时从 50,000 条中提取 5000 条线时,它会非常好(没有损坏的线)。所以你的代码做这项工作? (例如从 50,000 行中删除 5000 行)?
  • 实际上,我不确定您是否了解发生了什么。该代码根本不会破坏任何东西,它只是将行从一个文件复制到其他文件。顺便说一句,这让我想起了(我想知道为什么?)上周这里的一个线程是关于“加入”备忘录的。在那种情况下使用备忘录也是完全没有必要的。无论如何,我已经完成了。祝你好运!
【解决方案4】:

您可能会达到 tmemo 的限制(取决于您使用的 Delphi 版本)。当然,删除 tmemo 中的第一行需要时间,尤其是 5000 次。

您是否考虑在前 5000 行的备忘录 2 中使用 readln 和 writeln 并在其余部分使用备忘录 2(如果存在)?

【讨论】:

  • 没有TMemo的限制。从 Windows 2000 开始,没有限制。
  • 是的,删除第一行确实需要时间,这就是为什么我们必须使用一些 tthread 类,意思是拆分成文件文本,而不是备忘录,因为它正在使用内存。
  • 是的,对 TMemo 没有限制,只要你从 Tstrings 添加行,对于负载没有限制
  • 我对我的代码所做的,首先是在 MEMO1 中加载文件文本。所以大约有 50,000 行......(并且在加载大文件时编程也开始阻塞),所以在 MEMO2 中,我从 MEMO1 中拆分了 5000 行。所以 MEMO2 上有 5000 行,Memo1 上有 45,000 行现在!!!!!!....但是从 50,000 行中提取这 5000 行需要很多时间...我只是想让它更快!所以这就是我所说的TThread
  • @John 在 GUI 控件中处理文本非常缓慢。从备忘录顶部删除文本很慢。不要那样做。
猜你喜欢
  • 2021-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 2018-12-18
相关资源
最近更新 更多