Версия для печати
Работа с файлами Паскаля - Использование текстовых файлов для импорта и экспорта
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=847Anatoly Podgoretsky
урок из цикла: Уроки от АП
дата публикации 16-09-2003 13:19Работа с файлами Паскаля - Использование текстовых файлов для импорта и экспорта Текстовые файлы являются универсальным средство импорта/экспорта, например, Excel может очень легко импортировать текстовые файлы, в одном из распознаваемых им форматов. Допустимы следующие форматы:
- Comma Separated Value (CSV), данные разделенные запятой;
- Tab Delimited (TXT), данные разделенные символом табуляции, легко распознается Экселем;
- Symbol Delimited (TXT), данные разделенные указанным определенным символом, частный случай это Tab delimited, но его достоинство состоит в том, что в Экселе можно определить множество символов разделения, например одновременно разделителями могут быть ЗАПЯТАЯ, ТАБУЛЯЦИЯ и ТОЧКА С ЗАПЯТОЙ, Эксель разделит правильно;
- Fixed (TXT), данные имеют фиксированную длину колонок.
Все четыре формата имеют свое назначение, по умолчанию CSV формат считается универсальным форматом, поскольку многие программы и даже некоторые языки программирования поддерживают его. Недостатком является некоторая избыточность. Самые экономные это Tab delimited и Symbol Delimited, поскольку для разделение используется только один символ. Самый не экономный формат Fixed, поскольку для размещения данных всегда используется полная длина, его достоинством является простота обработки файла, можно просто читать фиксированными порциями или даже определить структуру в программе. Многие программы пишут свои логи именно в этом формате.
Какой использовать формат определяется задачей. Но я рассмотрю в примерах все форматы. Данные для экспорта могут находиться где угодно: в базе данных, в TStringList, в другом текстовом файл, поступать из потока. В примерах будет использоваться экспорт из TStringGrid, это позволит нам убить двух зайцев, дополнить возможности TStringGrid и освоить экспорт. В дополнение к примерам по экспорту, я рассмотрю и обратную операцию, загрузку данных в TstringList из ранее сохраненных данных в текстовой файл.
Пример 1, экспорт в файл в формате Comma Separated Value
Основой для экспорта в CSV понимание некоторых вещей:
- 1. Первая строка должна быть строкой заголовков колонок;
- 2. Данные разделяются запятой;
- 3. Числовые данные пишутся, как есть;
- 4. Строковые данные заключаются в двойные кавычки;
- 5. Даты распознаются если они в формате MM/DD/YYYY, заключать в кавычки не надо;
- 6. Расширение файла должно быть CSV.
Будьте осторожнее, обратный экспорт из Экселя работает не так как ожидается, формат далек от CSV, для обратного экспорта правильнее использовать формат Tab Delimited, с ним не ожидается таких сложностей и странностей. Есть еще странности, например, очень отличаются по действию открытие этих файлов из меню и открытие по ассоциации с расширением. Результаты очень удивят. Попробуйте поэкспериментировать с файлами и с Экселем, только сохраняйте в файлы с различными именами. При открытии по ассоциации (двойной щелчок) и расширении CSV получается полностью автоматический импорт.
Умолчания для примера:
- 1. StringGrid создан и содержит некоторое количество колонок и строк, количество определяется при экспорте;
- 2. Информация о типах данных в колонках StringGrid, то будем считать, что первая колонка целое число, вторая это дата в региональном формате и третья колонка это текст, больше колонок у нас нет.
- 3. Количество строк зависит от наполнения.
- 4. Нулевая строка как обычно содержит заголовки колонок.
- 5. Переменная FileName инициализирована и содержит имя файла, с должным расширением;
- 6. Обработка ошибок не ведется, кроме необходимых случаев.
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
- Первая колонка у нас число, поэтому выводим, как есть;
- Вторая колонка у нас дата, приводим ее к формату MM/DD/YYYY, но также без кавычек;
- Третья колонка у нас строка, ее выводим в кавычках. Если дата опущена или неверная, то экспортируем пустое значение.
При выводе в данном формате преобразования не нужны, наша задача состоит в том, чтобы вставить символ табуляции между колонками данных, поэтому код будет еще проще. Заголовки и данные выводятся в едином цикле и разделяются символом табуляции.
Этот же пример пригоден и для формата Symbol Delimited, достаточно заменить символ табуляции на любой нужный символ.
Умолчания для примера:
- 1. StringGrid создан и содержит некоторое количество колонок и строк, количество определяется при экспорте;
- 2. Так как отсутствует информация о типах данных в колонках StringGrid, то будем считать, что первая колонка целое число, вторая это дата в региональном формате и третья колонка это текст, больше колонок у нас нет. Но для экспорта данная информация не нужна.
- 3. Количество строк зависит от наполнения.
- 4. Нулевая строка как обычно содержит заголовки колонок.
- 5. Переменная FileName инициализирована и содержит имя файла, с должным расширением;
- 6. Обработка ошибок не ведется, кроме необходимых случаев.
Пример 3, экспорт в файл в формате Fixed
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;При выводе в данном формате преобразования не нужны, наша задача состоит в том, чтобы сделать данные колонок одинаковой ширины, нам даже не нужны разделители для колонок, но удобнее будет их сделать в виде одного пробела, что бы можно было обрабатывать файл любым текстовым редактором. Заголовки и данные выводятся в едином цикле и как договорились будем их разделять пробелом.
Умолчания для примера:
- 1. StringGrid создан и содержит некоторое количество колонок и строк, количество определяется при экспорте;
- 2. Так как отсутствует информация о типах данных в колонках StringGrid, то будем считать, что первая колонка целое число, вторая это дата в региональном формате и третья колонка это текст, больше колонок у нас нет. Но для экспорта данная информация не нужна.
- 3. Количество строк зависит от наполнения.
- 4. Нулевая строка как обычно содержит заголовки колонок.
- 5. Переменная FileName инициализирована и содержит имя файла, с должным расширением;
- 6. Обработка ошибок не ведется, кроме необходимых случаев.
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, как очень экономичный и гибкий для нашей цели. Нам не придется бороться с их количеством, поскольку эти значения будут восстановлены автоматически. Нулевая строка будет содержать всю необходимую информацию о таблице. Единственно, что требуется обеспечить, чтобы все колонки данных в таблице имели свой заголовок.
Наша задача состоит в том, чтобы вставить символ табуляции между колонками данных. Заголовки и данные выводятся в едином цикле и разделяются символом табуляции, при импорте заголовки будут играть важную роль, по ним будет определяться количество колонок.
Умолчания для примера:
- 1. StringGrid создан и содержит некоторое количество колонок и строк, количество определяется во время выполнения;
- 2. Информация о типах данных в колонках StringGrid отсутствует, но она нам и не нужна, мы должны уметь экспортировать любую информацию.
- 3. Количество строк зависит от наполнения.
- 4. Нулевая строка как обычно содержит заголовки колонок.
- 5. Переменная FileName инициализирована и содержит имя файла, с должным расширением;
- 6. Обработка ошибок не ведется, кроме необходимых случаев.
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!" на странице.
ЗаключениеНу вот теперь вы знаете про файлы Паскаля все, ну или почти все :), остальное в ваших руках.