Как извлечь первый экземпляр уникальной строки - PullRequest
0 голосов
/ 13 апреля 2019

Мне нужно извлечь список уникальных предметов из 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, или, возможно, мой цикл не работает должным образом.

Буду очень благодарен за любую практическую помощь.Большое спасибо заранее.

1 Ответ

0 голосов
/ 13 апреля 2019
Repeat
  ...
Until FileExists(FName) = FALSE;

Должно быть

While FileExists(FName) = TRUE do
Begin
End;

Если первый файл 2007-01-01 не существует, ваш код будет зависать при первом LoadFromFile(), поскольку вы не проверяете существование файла перед его загрузкой, в отличие от последующих файлов.

В противном случае я бы предложил придерживаться repeat, но назначить FName в верхней части каждой итерации цикла, вместо того, чтобы инициализировать его вне цикла, а затем переназначать в нижней части каждой итерации. Не нужно дублировать усилия.

Если вы проверите IndexOf() вручную, вам вообще не нужно использовать Sorted или dupIgnore. Это то, что вы должны делать в этой ситуации. Когда dupIgnore игнорирует новую строку, Append() не говорит вам, что строка была проигнорирована. Для этого вам нужно проверить, действительно ли Count был увеличен или нет.

Внутри внешнего цикла переназначение FileDate и FName должно быть вне внутреннего цикла 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;
...