Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Управление чтением неструктурированных данных в файлах с маршрутами для Delphi и Pascal

Delphi , Файловая система , Файлы

Чтение неструктурированных файлов с маршрутами в Delphi и Pascal

Введение

При работе с текстовыми файлами разработчики часто сталкиваются с проблемой неструктурированных или "хаотичных" данных. В этой статье мы рассмотрим практический пример обработки файла с маршрутами, где данные организованы без четких разделителей между секциями. Мы разберем несколько подходов к решению этой задачи на языке Object Pascal (Delphi).

Постановка проблемы

Исходные данные представляют собой текстовый файл с информацией о маршрутах, где каждая секция содержит:

  1. Название пункта назначения (без заголовка)
  2. Трейлер (без заголовка)
  3. Место отправления (с заголовком "Location:")
  4. Время в пути (с заголовком "Time:")
  5. Опциональную остановку (с заголовком "Stop:")
  6. Подробные инструкции по маршруту (с заголовком "Directions:")

Основная сложность заключается в том, что между секциями нет явных разделителей, а первые две строки следующей секции могут быть ошибочно приняты за продолжение инструкций предыдущей.

Решение с использованием конечного автомата

Один из наиболее надежных подходов - реализация конечного автомата (state machine). Рассмотрим пример кода:

type
  TDirs = record
    Dest: string;
    Trail: string;
    Loc: string;
    Time: Integer;
    Stop: string;
    Dirs: TStringList;
  end;

var
  DirArray: array of TDirs;

procedure ParseRouteFile(const FileName: string);
var
  sl: TStringList;
  i: Integer;
  CurrentState: Integer;
  CurrentDir: TDirs;
begin
  sl := TStringList.Create;
  try
    sl.LoadFromFile(FileName);
    SetLength(DirArray, 0);

    CurrentState := 0; // 0 - ожидаем начало новой секции
    i := 0;

    while i < sl.Count do
    begin
      case CurrentState of
        0: // Поиск начала новой секции
          begin
            if Trim(sl[i]) = '' then
            begin
              Inc(i);
              Continue;
            end;

            // Инициализация новой записи
            SetLength(DirArray, Length(DirArray) + 1);
            CurrentDir := DirArray[High(DirArray)];
            CurrentDir.Dirs := TStringList.Create;

            // Первые две строки без заголовков
            CurrentDir.Dest := Trim(sl[i]);
            Inc(i);
            if i >= sl.Count then Break;

            CurrentDir.Trail := Trim(sl[i]);
            Inc(i);
            if i >= sl.Count then Break;

            CurrentState := 1; // Переходим к ожиданию Location
          end;

        1: // Ожидаем Location
          begin
            if Pos('Location:', sl[i]) > 0 then
            begin
              CurrentDir.Loc := Trim(Copy(sl[i], Pos(':', sl[i]) + 1, MaxInt));
              Inc(i);
              if i >= sl.Count then Break;
              CurrentState := 2; // Переходим к ожиданию Time
            end
            else
            begin
              // Ошибка формата
              raise Exception.Create('Ожидался заголовок Location в строке ' + IntToStr(i+1));
            end;
          end;

        2: // Ожидаем Time
          begin
            if Pos('Time:', sl[i]) > 0 then
            begin
              CurrentDir.Time := StrToIntDef(Trim(Copy(sl[i], Pos(':', sl[i]) + 1, MaxInt)), 0);
              Inc(i);
              if i >= sl.Count then Break;
              CurrentState := 3; // Переходим к ожиданию Stop или Directions
            end
            else
            begin
              raise Exception.Create('Ожидался заголовок Time в строке ' + IntToStr(i+1));
            end;
          end;

        3: // Ожидаем Stop или Directions
          begin
            if Pos('Stop:', sl[i]) > 0 then
            begin
              CurrentDir.Stop := Trim(Copy(sl[i], Pos(':', sl[i]) + 1, MaxInt));
              Inc(i);
              if i >= sl.Count then Break;

              // После Stop должен идти Directions
              if Pos('Directions:', sl[i]) = 0 then
                raise Exception.Create('Ожидался заголовок Directions после Stop в строке ' + IntToStr(i+1));
            end;

            if Pos('Directions:', sl[i]) > 0 then
            begin
              Inc(i);
              if i >= sl.Count then Break;
              CurrentState := 4; // Переходим к чтению Directions
            end
            else
            begin
              raise Exception.Create('Ожидался заголовок Stop или Directions в строке ' + IntToStr(i+1));
            end;
          end;

        4: // Чтение Directions
          begin
            // Читаем до следующего Location или конца файла
            while (i < sl.Count) and (Pos('Location:', sl[i]) = 0) do
            begin
              CurrentDir.Dirs.Add(Trim(sl[i]));
              Inc(i);
            end;

            // Удаляем последние две строки (они принадлежат следующей секции)
            if CurrentDir.Dirs.Count > 2 then
            begin
              CurrentDir.Dirs.Delete(CurrentDir.Dirs.Count - 1);
              CurrentDir.Dirs.Delete(CurrentDir.Dirs.Count - 1);
            end;

            // Сохраняем запись
            DirArray[High(DirArray)] := CurrentDir;

            // Возвращаемся к началу
            CurrentState := 0;
            // Откатываем на две строки назад
            if i >= 2 then
              Dec(i, 2)
            else
              i := 0;
          end;
      end;
    end;

    // Сохраняем последнюю запись, если она была в процессе заполнения
    if CurrentState = 4 then
      DirArray[High(DirArray)] := CurrentDir;

  finally
    sl.Free;
  end;
end;

Альтернативное решение: чтение файла с конца

Интересный альтернативный подход предложил участник форума Zvoni - чтение файла с конца:

procedure ParseBackwards(const FileName: string);
var
  sl: TStringList;
  i: Integer;
  CurrentDir: TDirs;
begin
  sl := TStringList.Create;
  try
    sl.LoadFromFile(FileName);
    SetLength(DirArray, 0);

    i := sl.Count - 1;
    while i >= 0 do
    begin
      if Trim(sl[i]) = '' then
      begin
        Dec(i);
        Continue;
      end;

      // Создаем новую запись
      SetLength(DirArray, Length(DirArray) + 1);
      CurrentDir := DirArray[High(DirArray)];
      CurrentDir.Dirs := TStringList.Create;

      // Directions
      while (i >= 0) and (Pos('Directions:', sl[i]) = 0) do
      begin
        CurrentDir.Dirs.Insert(0, Trim(sl[i]));
        Dec(i);
      end;

      // Пропускаем строку с Directions:
      if i >= 0 then Dec(i);

      // Stop (если есть)
      if (i >= 0) and (Pos('Stop:', sl[i]) > 0) then
      begin
        CurrentDir.Stop := Trim(Copy(sl[i], Pos(':', sl[i]) + 1, MaxInt));
        Dec(i);
      end;

      // Time
      if (i >= 0) and (Pos('Time:', sl[i]) > 0) then
      begin
        CurrentDir.Time := StrToIntDef(Trim(Copy(sl[i], Pos(':', sl[i]) + 1, MaxInt)), 0);
        Dec(i);
      end;

      // Location
      if (i >= 0) and (Pos('Location:', sl[i]) > 0) then
      begin
        CurrentDir.Loc := Trim(Copy(sl[i], Pos(':', sl[i]) + 1, MaxInt));
        Dec(i);
      end;

      // Trail и Dest
      if i >= 1 then
      begin
        CurrentDir.Trail := Trim(sl[i]);
        CurrentDir.Dest := Trim(sl[i-1]);
        Dec(i, 2);
      end;

      // Сохраняем запись
      DirArray[High(DirArray)] := CurrentDir;
    end;
  finally
    sl.Free;
  end;
end;

Обработка ошибок и валидация данных

При работе с неструктурированными данными важно предусмотреть обработку ошибок:

function IsValidRoute(const Route: TDirs): Boolean;
begin
  Result := (Route.Dest <> '') and 
            (Route.Loc <> '') and 
            (Route.Time > 0) and 
            (Route.Dirs.Count > 0);
end;

procedure ValidateRoutes;
var
  i: Integer;
begin
  for i := 0 to High(DirArray) do
  begin
    if not IsValidRoute(DirArray[i]) then
      raise Exception.CreateFmt('Неверные данные в маршруте %d', [i+1]);
  end;
end;

Оптимизация работы с памятью

Для больших файлов можно оптимизировать использование памяти:

  1. Чтение файла построчно без загрузки всего содержимого в память
  2. Использование более эффективных структур данных
  3. Постепенная обработка данных с сохранением промежуточных результатов

Пример построчного чтения:

procedure ParseLineByLine(const FileName: string);
var
  F: TextFile;
  Line: string;
  // ... остальные переменные как в предыдущих примерах
begin
  AssignFile(F, FileName);
  Reset(F);
  try
    while not Eof(F) do
    begin
      ReadLn(F, Line);
      // Обработка строки аналогично предыдущим примерам
    end;
  finally
    CloseFile(F);
  end;
end;

Заключение

Обработка неструктурированных данных требует тщательного анализа формата входных данных и выбора подходящего алгоритма. В статье рассмотрены два основных подхода:

  1. Конечный автомат - последовательная обработка данных с явным выделением состояний
  2. Обратное чтение - оригинальный метод, особенно эффективный для данных с рекурсивной структурой

Для работы с подобными файлами в Delphi и Pascal рекомендуется:

  • Всегда проверять границы массивов и списков
  • Предусматривать обработку ошибок формата
  • Использовать структуры данных, соответствующие характеру обрабатываемой информации
  • Рассмотреть возможность предварительной очистки и нормализации данных

Приведенные примеры кода можно адаптировать для решения схожих задач обработки текстовых данных со сложной структурой.

Создано по материалам из источника по ссылке.

Описание обработки неструктурированных файлов с маршрутами в Delphi и Pascal, включая методы разбора данных, обработку ошибок и оптимизацию.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




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


:: Главная :: Файлы ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-06-04 06:35:44/0.0062239170074463/0