Версия для печати


Работа с файлами Паскаля - Использование текстовых файлов для импорта и экспорта
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=847

Anatoly Podgoretsky
дата публикации 16-09-2003 13:19

урок из цикла: Уроки от АП

Работа с файлами Паскаля - Использование текстовых файлов для импорта и экспорта

Текстовые файлы являются универсальным средство импорта/экспорта, например, Excel может очень легко импортировать текстовые файлы, в одном из распознаваемых им форматов. Допустимы следующие форматы:

Все четыре формата имеют свое назначение, по умолчанию CSV формат считается универсальным форматом, поскольку многие программы и даже некоторые языки программирования поддерживают его. Недостатком является некоторая избыточность. Самые экономные это Tab delimited и Symbol Delimited, поскольку для разделение используется только один символ. Самый не экономный формат Fixed, поскольку для размещения данных всегда используется полная длина, его достоинством является простота обработки файла, можно просто читать фиксированными порциями или даже определить структуру в программе. Многие программы пишут свои логи именно в этом формате.

Какой использовать формат определяется задачей. Но я рассмотрю в примерах все форматы. Данные для экспорта могут находиться где угодно: в базе данных, в TStringList, в другом текстовом файл, поступать из потока. В примерах будет использоваться экспорт из TStringGrid, это позволит нам убить двух зайцев, дополнить возможности TStringGrid и освоить экспорт. В дополнение к примерам по экспорту, я рассмотрю и обратную операцию, загрузку данных в TstringList из ранее сохраненных данных в текстовой файл.

Пример 1, экспорт в файл в формате Comma Separated Value
Основой для экспорта в CSV понимание некоторых вещей:

Будьте осторожнее, обратный экспорт из Экселя работает не так как ожидается, формат далек от CSV, для обратного экспорта правильнее использовать формат Tab Delimited, с ним не ожидается таких сложностей и странностей. Есть еще странности, например, очень отличаются по действию открытие этих файлов из меню и открытие по ассоциации с расширением. Результаты очень удивят. Попробуйте поэкспериментировать с файлами и с Экселем, только сохраняйте в файлы с различными именами. При открытии по ассоциации (двойной щелчок) и расширении CSV получается полностью автоматический импорт.

Умолчания для примера:

var
  F: TextFile;
  FileName: string;
  I: Integer;
  SG: TStringGrid;
  TempStr: string;
  Y, M, D: Word;
begin
  try
    AssignFile(F, Filename);  // связали файл с переменной
    Rewrite(F);               // создаем пустой файл
// если строка с заголовком не нужна, то можно эту строку удалить.
    WriteLn(F,
      '"', SG.Cells<0, 0>, '",',
      '"', SG.Cells<1, 0>, '",',
      '"', SG.Cells<2, 0>, '"');
    for I := 1 to SG.RowCount – 1 do  // проход по всем строкам
    begin
      try
// конвертирование строки из регионального в американский формат
        DePREDate(StrToDate(Trim(SG.Cells<1, I>)), Y, M, D);
        TempStr := IntToStr(M)+'/'+ IntToStr(D)+'/'+IntToStr(Y);
      except
        TempStr := ' ';             // дата не указана или неверная
      end;
      WriteLn(F,
        SG.Cells<0, I>,             // число
        TempStr,                    // конвертированная дата
        '"', SG.Cells<2, I>, '"');  // текст
    end;
  finally
    CloseFile(F);
  end;
end;

Как видим код весьма простой, первым WriteLn выводим заголовки таблицы, а поскольку все заголовки это текст, то обрамляем элементы двойными кавычками и разделяем запятой.

Далее в цикле проходим по всем строкам данных и выводим сами данные, но в отличии от строки заголовка делаем следующее:

Пример 2, экспорт в файл в формате Tab Delimited

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

Этот же пример пригоден и для формата Symbol Delimited, достаточно заменить символ табуляции на любой нужный символ.

Умолчания для примера:

const
  TAB = #9;                  // код символа табуляции
                             // константа для удобства
                             // можно было бы использовать и #9
var
  F: TextFile;
  FileName: string;
  I: Integer;
  SG: TStringGrid;
begin
  try
    AssignFile(F, Filename);  // связали файл с переменной
    Rewrite(F);               // создаем пустой файл

// если строка с заголовком не нужна,
// то начните цикл не с нуля, а с единицы.

    for I := 0 to SG.RowCount – 1 do  // проход по всем строкам
    begin
      WriteLn(F,
        SG.Cells<0, I> + TAB +
        SG.Cells<1, I> + TAB +
        SG.Cells<2, I>);
    end;
  finally
    CloseFile(F);
  end;
end;

Пример 3, экспорт в файл в формате Fixed

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

Умолчания для примера:

var
  F: TextFile;
  FileName: string;
  I: Integer;
  SG: TStringGrid;
begin
  try
    AssignFile(F, Filename);  // связали файл с переменной
    Rewrite(F);               // создаем пустой файл

// если строка с заголовком не нужна,
// то начните цикл не с нуля, а с единицы.

    for I := 0 to SG.RowCount – 1 do  // проход по всем строкам
    begin
      WriteLn(F,
        Format('%-25s %-25s %16s',
        , SG.Cells<0, I>, SG.Cells<0, I> >));
    end;
  finally
    CloseFile(F);
  end;
end;

Для выравнивание ширины колонок использована функция Format, вместо встроенного выравнивания WriteLn, поскольку последняя добавляет пробелы слева, а нам нужны пробелы справа. Вместо функции Format можно использовать свою функцию, или функции из других библиотек, или из Дельфи. Не важно, что использовать, важно чтобы строки были дополнены справа пробелами до нужной длины.

Разберем форматную строку

%-25.25s – Символ % это признак спецификатора формата, символ «-» означает выравнивание влево, 25 означает, что длина будет дополняться до 25 символов, а .25 означает максимальное количество символов в строке будет 25, остальные символы будут отбрасываться, символ s указывает не тип данных, в данном случае это означает, что в функцию передается строковое значение.

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

Пример 4, импорт и экспорт данных для TStringList

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

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

Умолчания для примера:

const
  TAB = #9;                  // код символа табуляции
                             // константа для удобства
                             // можно было бы использовать и #9
procedure Export(const FileName: string;  SG: TStringGrid);
var
  F: TextFile;
  I: Integer;
  J: Integer;
begin
  try
    AssignFile(F, Filename);  // связали файл с переменной
    Rewrite(F);               // создаем пустой файл
    for I := 0 to SG.RowCount – 1 do  // проход по всем строкам
    begin
      for J := 0 to SG.ColCount – 1 do  // проход по всем колонкам

      begin
        Write(F,SG.Cells);  // пишем отдельную ячейку
        if J < SG.ColCount – 1
        then
          Write(F, TAB)      // тогда пишем разделитель
        else
          WriteLn(F);         // иначе закрываем строку
      end;
    end;
  finally
    CloseFile(F);
  end;
end;

procedure Import(const FileName: string; var SG: TStringGrid);
var
  F: TextFile;
  S: string;
begin
  try
    AssignFile(F, Filename);  // связали файл с переменной
    Reset(F);                 // открываем файд с данными
    SG.ColCount := 1;         // начальные значения
    SG.RowCount := 1;         // количества колонок и строк
    while not EOF(F) do       // проход по всем строкам
    begin
      ReadLn(F, S);           // читаем строку данных
      SG.Col := 0;            // проход всегда начинается с нуля
      while Pos(TAB, S) > 0 do
      begin                              // вычленение колонок
        SG.Cells := Copy(S, 1, Pos(TAB, S) - 1);
        Delete(S, 1, Pos(TAB, S));
        if SG.ColCount - SG.Col = 1
        then
        begin
          SG.ColCount := SG.ColCount + 1;// нужна новая колонка
        end;
        SG.Col := SG.Col + 1;            // следующая колонка
      end;
      SG.Cells := S;     // последняя колонка
      SG.RowCount := SG.RowCount + 1;    // добавим еще одну строку
      SG.Row := SG.Row + 1;              // следующая строка
    end;
    SG.RowCount := SG.RowCount - 1;      // лишняя строка
  finally
    SG.FixedCols := 1;                   // восстанавливаем
    SG.FixedRows := 1;                   // значение по умолчанию
    CloseFile(F);
  end;
end;

Рекомендация: Если необходимо сохранить ширину колонок, количество фиксированных строк и колонок или другие характеристики, то перед импортом сохраните эти значения, а после восстановите их, или установите в нужное значения. После импортирования эти параметры устанавливаются в значение по умолчанию.

В дополнение к обычной работе с файлами, можно отметить еще и следующее. Все ранее изученные нами методы пригодны для создания стандартных консольных приложений для динамических ВЕБ страниц. Для создания достаточно использования процедур ReadLn и WriteLn, если конечно этот сервер работает под управлением Windows. Это так называемые консольные CGI приложения (Standalone CGI Application).

Вот выдержка из книги доктора Боба «Интернет решения от доктора Боба», которую можно найти на моем сайте http://podgoretsky.com/ftp/Docs/Delphi/DX/drbobinetrus.doc

Для начала посмотрим на стандартное "hello world" CGI приложение. Единственное, что оно должно сделать, это вернуть HTML страницу со строкой "hello, world". Перед тем как мы начнем делать это - обратим внимание на следующее: CGI приложение должно сообщить миру какой (MIME) формат оно выдает. В нашем случае это "text/html", которое мы должны указать как: content-type: text/html, и затем одну пустую строку.

Вот код нашего первого "Hello, world!" CGI приложения:

program CGI1;
{$APPTYPE CONSOLE}
begin
  writeln('content-type: text/html');
  writeln;
  writeln('<HTML');
  writeln('<BODY');
  writeln('Hello, world!');
  writeln('</BODY');
  writeln('</HTML')
end.

Если вы откомпилируете данную программу в Дельфи 2 или 3 и затем запустите ее из web браузера подключенного к web серверу, где оно записано в исполнимом виде в исполняемом каталоге таком как cgi-bin, то вы увидите текст "Hello, world!" на странице.

Заключение

Ну вот теперь вы знаете про файлы Паскаля все, ну или почти все :), остальное в ваших руках.