【问题标题】:How to extract the first instance of unique strings如何提取唯一字符串的第一个实例
【发布时间】:2019-04-13 13:36:43
【问题描述】:

我需要从 12 年的一致的计算机生成的每天一个文本文件中提取独特项目的列表。文件名仅因包含日期而异,因此很容易在代码中生成所需的名称。它们包含在给定日期内我当地机场的所有飞机起降的列表,按时间顺序排列。自然,同一架飞机来来去去很多次,目的是循环文件,挑选出每架飞机出现的第一个实例(首次访问或FV)将其复制到列表中,然后从那时起忽略它在。结果应该是按日期顺序列出的所有首次访问的列表。应该很简单,但是...我的程序很小,所以我包含了整个实现代码。

procedure TForm1.FormCreate(Sender: TObject);
begin
  FileDate := StrToDate('01/01/2007');
  FName := 'E:LGW Reports/SBSLGW2007-01-01.txt'; //1st file to be read
  FDStr := copy(FName, 21, 10);
  TempList := TStringList.Create; //temp holder for file contents
  FVCheckList := TStringList.Create; //holds unique identifier (UID)
  FVCheckList.Sorted := TRUE;
  FVCheckList.Duplicates := dupIgnore;
  FVList:= TStringList.Create;  //the main output
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
begin
  Memo1.Lines.Append('Started');
  Repeat
    TempList.Clear;
    TempList.LoadFromFile(FName);
    for i := 1 to TempList.Count-1 do
    begin
      Line := TempList.Strings[i];
      //create a //create a Unique identifier (UID) from elements in Line          
      Serial := Trim(Copy(Line, 22, 9)); 
      MsnPos1 := Pos('[', Line) + 1;
      MsnPos2 := Pos(']', Line);
      Msn := copy(Line, MsnPos1, (MsnPos2 - MsnPos1));
      UID := Serial + '/' + Msn;
      //          
      if (FVCheckList.IndexOf(UID) < 0) then
      begin
        FVCheckList.Append(UID);
      //Add date of file to Line, otherwise it gives no clue when FV was
        FVList.Append(FormatDateTime('YYYY-MM-DD', FileDate) + ' ' + Line);
        FileDate := IncDay(FileDate, 1);
        FName := 'E:LGW Reports/SBSLGW' + FormatDateTime('YYYY-MM-DD', FileDate) + '.txt';
      end;
    end;
  Until FileExists(FName) = FALSE;
  FVCheckList.SaveToFile('E:LGW Reports/First Visit Checklist.txt');
  FVList.SaveToFile('E:LGW Reports/First Visits.txt');
  Memo1.Lines.Append('Finished');
  Memo1.Lines.SaveToFile('E:LGW Reports/Files parsed.txt');
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  TempList.Free;
  FVCheckList.Free;
  FVList.Free;
end;

没有编译器错误,它在几秒钟内运行完成并生成指定的两个文本文件,格式正确。最大的问题是 FVList 中实际列出的行并不总是飞机的第一次访问,它们可能是第一次、最近一次或介于两者之间。关于为什么会出现错误实例,我看不出任何明显的线索:如果我的代码是正确的,那么 TStringList FVCheckList 的功能就有问题。错误更可能是我忽略的东西,或者我对 .dupIgnore 工作原理的理解,或者我的循环没有按应有的方式工作。

我应该非常感谢任何实际的帮助。非常感谢。

【问题讨论】:

  • 如果我是你,我会调试程序。你有吗?
  • 你不觉得我一直在努力吗?调试器没有发现任何问题。它正在做代码告诉它做的事情,完美无缺,仅此而已。了解实际情况的唯一方法是根据原始文件手动分析数据输出,10 年内每天大约 600 行需要检查。
  • 调试时,将问题减少到可管理的范围内。
  • 在这种情况下比听起来更难。创建一个可能产生有意义结果的测试数据集需要数天的反复试验。我尝试在程序循环时向 TMemo 输出各种变量,但无法理解结果,如果这给了我一个线索,我可能已经创建了一些相关文件,但是......

标签: delphi-2010 tstringlist


【解决方案1】:
Repeat
  ...
Until FileExists(FName) = FALSE;

应该是

While FileExists(FName) = TRUE do
Begin
End;

如果第一个 2007-01-01 文件不存在,您的代码将在第一个 LoadFromFile() 处崩溃,因为您在加载文件之前不检查文件是否存在,这与后续文件不同。

否则,我建议坚持使用repeat,但在每次循环迭代的顶部分配FName,而不是在循环外对其进行初始化,然后在每次迭代的底部重新分配。无需重复努力。

如果您手动检查IndexOf(),则根本不需要使用SorteddupIgnore。这是你在这种情况下应该做的。当dupIgnore 忽略一个新字符串时,Append() 不会告诉您该字符串已被忽略。为此,您必须检查Count 是否实际增加了。

在外部循环内部,FileDateFName 的重新分配应该在内部for 循环之外,而不是在for 循环内部。

试试这个:

procedure TForm1.FormCreate(Sender: TObject);
begin
  FileDate := EncodeDate(2007,1,1);
  FDStr := FormatDateTime('YYYY-MM-DD', FileDate);
  TempList := TStringList.Create; //temp holder for file contents
  FVCheckList := TStringList.Create; //holds unique identifier (UID)
  FVList := TStringList.Create; //the main output
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
begin
  Memo1.Lines.Append('Started');
  Repeat
    FName := 'E:LGW Reports/SBSLGW' + FormatDateTime('YYYY-MM-DD', FileDate) + '.txt';
    if not FileExists(FName) then Break;
    Memo1.Lines.Append(FName)
    TempList.LoadFromFile(FName);
    for i := 1 to TempList.Count-1 do
    begin
      Line := TempList.Strings[i];
      //create a Unique identifier (UID) from elements in Line
      Serial := Trim(Copy(Line, 22, 9));
      MsnPos1 := Pos('[', Line) + 1;
      MsnPos2 := PosEx(']', Line, MsnPos1);
      Msn := copy(Line, MsnPos1, (MsnPos2 - MsnPos1));
      UID := Serial + '/' + Msn;
      if FVCheckList.IndexOf(UID) = -1 then
      begin
        FVCheckList.Append(UID);
        //Add date of file to Line, otherwise it gives no clue when FV was
        FVList.Append(FormatDateTime('YYYY-MM-DD', FileDate) + ' ' + Line);
      end;
    end;
    FileDate := IncDay(FileDate, 1);
  end;
  FVCheckList.SaveToFile('E:LGW Reports/First Visit Checklist.txt');
  FVList.SaveToFile('E:LGW Reports/First Visits.txt');
  Memo1.Lines.Append('Finished');
  Memo1.Lines.SaveToFile('E:LGW Reports/Files parsed.txt');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  TempList.Free;
  FVCheckList.Free;
  FVList.Free;
end;

【讨论】:

  • 谢谢雷米,我确定就是这样,因为我自己发现了它,修复并测试了程序,然后冲回另一台电脑,希望我可以删除问题而不会浪费任何人的时间。输出现在更有意义了。由于这是我写了 10 多年的第一个 Delphi 程序,而且年龄和老年痴呆症让生活变得困难,我希望你能原谅我。我想我添加了 IndexOf() 用法,因为没有它它似乎根本无法工作,但现在它可能会。再次感谢。
  • 我决定硬编码 2007 年 1 月 1 日作为第一个文件的日期,因为它很简单就是第一个文件的日期,而且我知道它存在于文件夹中。这是一个一次性的非常具体的任务的一次性程序。将来,我将修改生成文件以识别新的首次访问并将它们添加到现有列表的程序 - 主要是因为我一直忘记手动执行此操作,并且知道什么是或不是首次访问似乎是对我们当地的飞机爱好者社区至关重要:)。您刚刚帮助过的一些代码可以重复使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-10
  • 1970-01-01
  • 1970-01-01
  • 2010-09-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多