Среда, 24.04.2024, 13:44
Prise Clan! Главная страницаРегистрацияВход
Приветствую Вас Гость
Меню сайта
Наш опрос
Чего не хватает на сайте
Всего ответов: 42
 paskal 
продолжаем

Нетипизированные файлы Третий тип файлов Паскаля, это нетипизированные файлы, этот тип характеризуется тем, что данные имеют не регулярную структуру, например записи различных типов или записи переменной длины, или это просто двоичные данные. После появления поддержки файлов в VCL, на уровне потоков, а также прямой доступ к файловой системе, через функции АПИ, его ценность стала низкой и почти во всех случаях правильнее использовать потоковые методы из VCL, и только тогда когда требуется небольшой размер программы, стоит использовать их или Win API. Хотя предполагается работа с двоичными данными неопределенного типа, все равно ведется работа с понятиями запись (блок), размер которой задается при открытии файла, по умолчанию размер записи равен 128 байт. Размер файла должен быть кратным размеру блока, иначе при чтении последней записи возникнет ошибка. Новых понятий немного, это понятие размер блока, режим открытия и вместо процедур Read/Write используются процедуры BlockRead/BlockWrite. Посмотрим на изменения по отношению к текстовым и типизированным файлам. Объявление файла делается так: var FileVar: file; Сразу бросается в глаза отсутствие типа, вместо file of тип, просто file, то есть файл данных любого типа. Процедуры открытия файла Reset и Rewrite имеют дополнительный параметр, который указывает размер записи, если этот параметр не указан, то используется значение по умолчанию в 128 байт, кстати, это часто является одной из причин для возникновения ошибок, забываем указать этот размер, а при работе считаем, что работаем с байтом. Что бы работать с файлом, как с байтом, надо или установить размер записи в 1 или использовать типизированный файл следующего типа - file of byte. При получении размера файла, результат выдается так же в записях, и если опять же нужно получить размер файла в байтах, также надо устанавливать размер записи в единицу или умножить количество записей на размер записи, в этом кстати и состоит различие между файлами Паскаля и файлами в АПИ или VCL, те не оперируют понятиями запись, а только понятиями байт. Другая ошибка у начинающих состоит в том, что длина файла должна быть кратна длине записи, частичные записи не допустимы. Различие между Reset и Rewrite такое же, как и у нетипизированных файлов, первый открывает файл без уничтожения старого файла, а второй создает новый файл, режим открытия файлов задается отдельно. Примеры открытия файла с размером записи в 1 байт Reset(F, 1); // открытие с сохранением файла, файл должен существовать Reset(F, 1); // открытие с созданием нового файла, или с удалением старого Для управления режимом открытия файлов существует глобальная переменная FileMode. Назначение этой переменной одного из значений влияет на режим последующего открытия файлов, все последующие файлы открываются в соответствии с ее значением, режим ранее открытых файлов не изменяется. Данная переменная не применима для текстовых файлов, которые открываются в соответствии с типом функции, для Reset это режим только чтение, для Rewrite и Append это режим записи. В модуль Sysutils находится определение констант для потоков, часть констант совпадает с нужными нам. Для полноценного управления режимами доступа надо использовать класс TFileStream. fmOpenRead = 0 открытие только в режиме чтения fmOpenWrite = 1 открытие только в режиме записи fmOpenReadWrite = 2 открытие в режиме чтения/записи Примечание: Переменная FileMode не является потоко безопасной. Теперь можно приступить к примерам и поскольку трудно придумать практический пример, то я приведу по три примера использования данного типа и с использованием TFileStream. Это позволит оценить оба метода. Пример 1 - абстрактные данные (file) Использование с двоичными данными абстрактного типа. Просто набор байт. Для демонстрации возьмем простой набор строк, скажем из TStringList. Запись в файл будем производить в следующем формате - длина, строка. Особого практического применения нет, но данная техника часто используется в потоках. Когда надо передавать данные переменной длины, записывающий поток передает, принимающий поток сначала считывает длину, а затем считывает уже известное количество данных. Умолчания для примера: SL создан и содержит строки; Переменная FileName инициализирована и содержит имя файла; Обработка ошибок не ведется, кроме необходимых случаев. var SL: TStringList; I: Integer; F: file; FileName: string; begin try AssignFile(F, Filename); // связали файл с переменной FileMode := fmOpenWrite; // только запись Rewrite(F, 1); // размер записи один байт for I := 0 to Sl.Count –1 do // проход по всем строкам begin BlockWrite(F, Length(Sl.Strings[I]), SizeOf(LongInt)); BlockWrite(F, Sl.Strings[I], Length(Sl.Strings[I]); end; finally CloseFile(F); end; end. Пример 2 - абстрактные данные (TFileStream) var SL: TStringList; I: Integer; FS: TFileStream; FileName: string; I: Integer; begin FS := TFileStream.Create(Filename, fmOpenWrite or fmShareExclusive); try for I := 0 to Sl.Count –1 do // проход по всем строкам begin FS.Write(Length(Sl.Strings[I]), SizeOf(LongInt)); FS.Write(Sl.Strings[I], Length(Sl.Strings[I])); end; finally FS.Free; end; end. Преимущество состоит в использовании расширенных режимов открытия файлов и нет нужды объявлять размер записи, поскольку самих записей не существует, при записи в файл просто указывается длина данных. Пример 3 - записи фиксированной длины (file) Использование записей, но разного типа. Обратите внимание, что в записях используется не Integer, а LongInt, это связано с тем, что Integer не является фундаментальным типом и его размер зависит от версии компилятора, в то же время LongInt всегда 4 байта. Также что размер string[3] совпадает с размером LongInt, этим обеспечивается одинаковый размер записи. Вторым параметром, влияющим на размер записи - являет выравнивание элементов записи, на какую либо границу 2, 4, 8 байт, это также предмет для изменений в различных версиях компилятора или его настроек. Использование ключевого слова packed позволяет избежать этой неприятности, в этом случае запись занимает ровно столько место, сколько требуется и не байтом больше. Это обеспечит нам переносимость. Настоятельно рекомендую обратить особое внимания на эти замечания, поскольку это распространенная ошибка при написании программ. Умолчания для примера: Массив DevArray создан и содержит данные; Переменная FileName инициализирована и содержит имя файла; Обработка ошибок не ведется, кроме необходимых случаев. type TCmd: string[3]; // команда устройству, аббревиатура из 3 символов TRecType = (rtNone, rtCmd, rtData); THdr = packed record TypeID: TRecType; // идентификатор записи, // общая часть во всех остальных типах. end; TCmd = packed record Hdr: THdr; // идентификатор записи DevCmd: TCmd; // команда устройству, аббревиатура из 3 символов end; TData = packed record Hdr: THdr; // идентификатор записи DevData: LongInt; // данные для устройства или из устройства end; TDevEntry = packed record Cmd: TCmd; Data: LongInt; end; var Cmd: TCmd; Data: Tdata; DevArray: array[1..100] of TDevEntry; F: file; FileName: string; I: Integer; begin try AssignFile(F, Filename); // связали файл с переменной FileMode := fmOpenWrite; // только запись Rewrite(F, SizeOf(TCmd)); // TData имеет тот же размер for I := Low(DevArray) to High(DevArray) do // проход по массиву begin Cmd.Hdr.TypeID := rtCmd; Cmd.DevCmd := DevArray[I].Cmd; BlockWrite(F, Cmd, SizeOf(TCmd)); Data.Hdr.TypeID := rtData; Data.DevData := DevArray[I].Data; BlockWrite(F, Data, SizeOf(TData)); end; finally CloseFile(F); end; end. Пример 4 - записи фиксированной длины (TFileStream) Объявления типов прежние. var DevArray: array[1..100] of TDevEntry; FS: TFileStream; I: Integer; begin FS := TFileStream.Create(Filename, fmOpenWrite or fmShareExclusive); try for I := Low(DevArray) to High(DevArray) do // проход по массиву begin FS.Write(rtCmd, SizeOf(THdr.TypeID)); FS.Write(DevArray[I].Cmd, SizeOf(TCmd.DevCmd)); FS.Write(rtData, SizeOf(THdr.TypeID)); FS.Write(DevArray[I].Data, SizeOf(TData.DevData)); end; finally FS.Free; end; end. Как только код стал сложнне, так сразу стало видно, что использование TFileStream проще и прозрачнее, код более четкий. Отпала необходимости в копировании данных во временные переменные. Пример 5 - записи переменной длины (file) Использование записей переменной длины для организации сложных структур. Записи состоят из двух частей, фиксированной с информацией о дальнейших записях и переменной 0 сами записи. Возможно построение сложных иерархических структур, когда одна запись содержит в себе другие вложенные данные, наглядным примером являются объектовые (.obj) и исполнимые файлы (.exe). Умолчания для примера: Массив DevArray создан и содержит данные; Переменная FileName инициализирована и содержит имя файла; Обработка ошибок не ведется, кроме необходимых случаев. В качестве основы определим следующие типы: type THdr = packed record RecID: TRecType; // идентификатор записи, необязательная часть,type // зависит от задачи, // но очень полезная в сложных структурах RecLg: Integer; // длин RecID: TRecType; // идентификатор записи, необязательная часть,type // зависит от задачи, // но очень полезная в сложных структурах RecLg: Integer; // длина данных, следуют сразу за заговоком // данные могут быть простыми, но также и сложными // то есть включать другие структуры // со своими заголовками end; TCmd = string[6]; TPacked = packed record DevCmd: TCmd; // команда устройству, аббревиатура из 3 символов DevData: string; // переменная длина end; TDevEntry = packed record Cmd: TCmd; Data: string; end; В файл будем писать данные в следующим формате Заголовок типа THdr, В качестве RecID будем использовать порядковый номер записи начиная с 1. RecLg будет включать полную длину последующего пакета, размер которого переменный. Данные в формате TPacked, где DevCmd аббревиатура команды из 6 символов (string[6]), фиксированной длины и строковые данные переменной длины. Общая длина пакета отражается в заголовке записи, в поле RecLg. var Hdr: THdr; DevArray: array[1..100] of TDevEntry; F: file; FileName: string; I: Integer; begin try AssignFile(F, Filename); // связали файл с переменной FileMode := fmOpenWrite; // только запись Rewrite(F, 1); // так как записи переменной длины, // то размер записи 1 байт for I := Low(DevArray) to High(DevArray) do // проход по массиву begin Hdr.RecId := I; Hdr.RecLg := SizeOf(TCmd) + Length(DevArray[I].Data); BlockWrite(F, Hdr, SizeOf(THdr)); // записали заголовок BlockWrite(F, DevArray[I].Cmd, SizeOf(TCmd)); BlockWrite(F, DevArray[I].Data[0], Length(DevArray[I].Data); end; finally CloseFile(F); end; end. В примере происходит следующее: Во временную переменную записывается номер записи Рассчитывается длина переменного блока Заголовок пишется в файл Затем в файл пишется фиксированная часть блока DevArray[I].Cmd И затем пишется переменная часть блока DevArray[I].Data[0] Цикл повторяется по всему массиву, по окончанию файл закрывается, теперь реализуем это пример с помощью TFileStream. Пример 6 - записи переменной длины (TFileStream) var DevArray: array[1..100] of TDevEntry; FS: TFileStream; I: Integer; begin FS := TFileStream.Create(Filename, fmOpenWrite or fmShareExclusive); try for I := Low(DevArray) to High(DevArray) do // проход по массиву begin FS.Write(I, SizeOf(THdr.RecID)); FS.Write(SizeOf(TCmd)+Length(DevArray[I].Data),SizeOf(THdr.RecLg)); FS.Write(DevArray[I].Cmd, SizeOf(TCmd)); FS.Write(DevArray[I].Data[0], Length(DevArray[I].Data); end; finally FS.Free; end; end. Опять видим, что код стал проще и прозрачнее. Отпала необходимость во временных переменных. На этом данный урок закончен, вы принципе Вы уже должны представлять основные методы работы с тремя базовыми файлами и начальные сведенья по TFileStream, что позволит сделать выбор в сторону правильного для задачи метода. Расширенная работа с тестовыми файлами Кроме работы со строками в текстовых файлах, Паскаль поддерживает и более расширенные методы, можно оперировать и данными в строке. Можно читать данные из строки в одну или более переменных. Паскаль сам обеспечивает разбор строки на составляющие части. Полный синтаксис процедур следующий procedure Readln([ var F: TextFile; ] V1 [, V2, ...,Vn ]); procedure Writeln([ var F: TextFile; ] P1 [, P2, ...,Pn ] ); F - файловая переменная типа TextFile, это означает, что процедуры Readln и Writeln могут работать с текстовыми файлами и не примени для других типов файлов. Если эта переменная опущена, то в качестве текстового файла используется консоль, это также означает, что вывод возможен на консоль и не применим для GUI приложений. На самом деле эта конструкция равносильна следующему вызову Readln([Input) или Writeln(Output). Просто это значение по умолчанию и компилятор сам подставляет эти файловые переменные, который уже описаны в модуле System. Если создано консольное приложение, то Дельфи автоматически ассоциирует эти файловые переменные с окном консоли приложения. Vn – это одна или несколько переменных строкового, символьного, целочисленного или плавающего типа и также логические переменные. Возможно это не полный список типов, но можете попробовать проверить сами. Не поддержанные типа можно выводить с помощью функций преобразования в строку, например DateTimeToStr(Now). Pn – это один или более параметров функции, которые могут являться строкой, символом, целым числом или числом с плавающей запятой. В справке по процедуре Write ошибочно указано, что в качестве параметров могут использоваться только переменные, на самом деле это могут быть как переменные, так и константные выражения. Кроме того, справка полностью умалчивает о формате вывода данных в файл, о возможности форматирования данных непосредственно в параметрах процедуры. Форматирования вывода осуществляется в следующем виде X [:Width [:Decimals]], где Width общая длина вывода, а Decimals это количество знаков после десятичной точки. Для получения нужной длины вывода слева добавляется нужное количество пробелов, если результат не помещается в указанную длину, то тогда длина увеличивается до должного количества знаков. Спецификатор Decimals применим только к числам с плавающей запятой. При этом при необходимости производится округление числа. Если спецификаторы не указаны, то для вывода используется научный формат, то есть #.##############E+#### Для целочисленных чисел, строк и символов, без указания длины используется столько символов, что бы значение полностью вывелось. Значения выводятся без разделителей между ними, поэтому надо или использовать длину на один символ больше нужной или вставлять в список параметров пробел, например, так Writeln(F, A,' ',B,' ',C); Теперь по поводу чтения обратно в переменные. Все аналогично выводу, за исключением одного неприятного отличия. В случае указания в списке переменных, переменной строкового типа, в нее считывает все до окончания строки. Вроде в старом Паскале такого не было, там вроде бы считывалось слово до первого разделителя, но так ли это было уже не помню, но это и не важно, важно знать, как это работает сейчас и учитывать это при чтении данных. Поэтому если надо считывать элементы строки в переменные, то можно использовать только один элемент и он должен быть последним в строке. Приведу пример неправильной логики. WriteLn(F, 1, ' ', 2.5:3:1, ' ', ‘string’, ' ', 3); ReadLn(F, Int1, Real1, Str1, Int2); В файле будет следующая информация "1 2.5 string 3" После считывания значения переменных следующие: Int1 = 1 Real1 = 2.5 Str1 = string 3 Int2 = 0 Правильно написать так WriteLn(F, 1, ' ', 2.5:3:1, ' ', 3, ' ', 'string'); ReadLn(F, Int1, Real1, Int2, Str1); Теперь в переменных правильные значения: Int1 = 1 Real1 = 2.5 Int2 = 3 Str1 = string Ну и напоследок напишем простой пример чтения и записи двухмерного массива в файл. var Column: Integer; F: TextFile; IntArray: array[1..10, 1..3] of Integer; Row: Integer; S: string; TmpInt: Integer; begin // Инициализация for Row := 1 to 10 do begin for Column := 1 to 3 do begin IntArray[Row, Column] := Row*100 + Column; end; end; AssignFile(F, 'Test.txt'); try Rewrite(F); // открытие для записи for Row := 1 to 10 do begin for Column := 1 to 3 do begin Write(F, IntArray[Row, Column]:11); end; WriteLn(F, ' Строка: ', Row); end; finally CloseFile(F); end; // Чтение try WriteLn('Test Reading'); Reset(F); // открытие для чтения for Row := 1 to 10 do begin for Column := 1 to 3 do begin Read(F, TmpInt); Write(TmpInt:11); end; ReadLn(F, S); WriteLn(S); end; finally CloseFile(F); end; Readln; // Закрытие окна по ENTER end. После проверки, можете открыть файл Блокнотом и убедиться, что он действительно текстовый. Примечание: при выводе текстовых сообщений учтите, что на консоль надо выводить в OEM кодировке, поэтому если надо вывести текст на национальном языке, то предварительно преобразуйте его из ANSI в OEM, см. главу «Текстовые файлы». То есть, любой текст надо преобразовывать функцией CharToOem. Это же касается и текстовых констант в коде программы. Использование текстовых файлов для импорта и экспорта Текстовые файлы являются универсальным средство импорта/экспорта, например, в Экселе может очень легко импортировать текстовые файлы, в одном из распознаваемых им форматов. Допустимы следующие форматы: Comma Separated Value (CSV), данные разделенные запятой; Tab Delimited (TXT), данные разделенные символом табуляции, легко распознается Экселем; Symbol Delimited (TXT), данные разделенные указанным определенным символом, частный случай - это Tab delimited, но его достоинство состоит в том, что в Экселе можно определить множество символов разделения, например одновременно разделителями могут быть ЗАПЯТАЯ, ТАБЛУЛЯЦИЯ и ТОЧКА С ЗАПЯТОЙ, Эксель разделит правильно; Fixed (TXT), данные имеют фиксированную длину колонок. Все четыре формата имеют свое назначение, по умолчанию CSV формат считается универсальным форматом, поскольку многие программы и даже некоторые языки программирования поддерживают его. Недостатком является некоторая избыточность. Самые экономные это Tab delimited и Symbol Delimited, поскольку для разделение используется только один символ. Самый не экономный формат Fixed, поскольку для размещения данных всегда используется полная длина, его достоинством является простота обработки файла, можно просто читать фиксированными порциями или даже определить структуру в программе. Многие программы пишут свои логи именно в этом формате. Какой использовать формат определяется задачей. Но я рассмотрю в примерах все форматы. Данные для экспорта могут находиться где угодно: в базе данных, в TStringGrid, в другом текстовом файл, поступать из потока. В примерах будет использоваться экспорт из TStringGrid, это позволит нам убить двух зайцев, дополнить возможности TStringGrid и освоить экспорт. В дополнение к примерам по экспорту, я рассмотрю и обратную операцию, загрузку данных в TStringGrid из ранее сохраненных данных в текстовой файл. Пример 1, экспорт в файл в формате Comma Separated Value Основой для экспорта в CSV является понимание некоторых вещей: Первая строка должна быть строкой заголовков колонок; Данные разделяются запятой; Числовые данные пишутся, как есть; Строковые данные заключаются в двойные кавычки; Даты распознаются если они в формате MM/DD/YYYY, заключать в кавычки не надо; Расширение файла должно быть CSV. Будьте осторожнее, обратный экспорт из Экселя работает не так как ожидается, формат далек от CSV, для обратного экспорта правильнее использовать формат Tab Delimited, с ним не ожидается таких сложностей и странностей. Есть еще странности, например, очень отличаются по действию открытие этих файлов из меню и открытие по ассоциации с расширением. Результаты очень удивят. Попробуйте поэкспериментировать с файлами и с Экселем, только сохраняйте в файлы с различными именами. При открытии по ассоциации (двойной щелчок) и расширении CSV получается полностью автоматический импорт. Умолчания для примера: StringGrid создан и содержит некоторое количество колонок и строк, количество определяется при экспорте; Информация о типах данных в колонках StringGrid, то будем считать, что первая колонка целое число, вторая это дата в региональном формате и третья колонка это текст, больше колонок у нас нет. Количество строк зависит от наполнения. Нулевая строка как обычно содержит заголовки колонок. Переменная FileName инициализирована и содержит имя файла, с должным расширением; Обработка ошибок не ведется, кроме необходимых случаев. 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 // конвертирование строки из регионального в американский формат DecodeDate(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 выводим заголовки таблицы, а поскольку все заголовки это текст, то обрамляем элементы двойными кавычками и разделяем запятой. Далее в цикле проходим по всем строкам данных и выводим сами данные, но в отличии от строки заголовка делаем следующее: Первая колонка у нас число, поэтому выводим, как есть; Вторая колонка у нас дата, приводим ее к формату MM/DD/YYYY, но также без кавычек; Третья колонка у нас строка, ее выводим в кавычках. Если дата опущена или неверная, то экспортируем пустое значение. Пример 2, экспорт в файл в формате Tab Delimited При выводе в данном формате преобразования не нужны, наша задача состоит в том, чтобы вставить символ табуляции между колонками данных, поэтому код будет еще проще. Заголовки и данные выводятся в едином цикле и разделяются символом табуляции. Этот же пример пригоден и для формата Symbol Delimited, достаточно заменить символ табуляции на любой нужный символ. Умолчания для примера: StringGrid создан и содержит некоторое количество колонок и строк, количество определяется при экспорте; Так как отсутствует информация о типах данных в колонках StringGrid, то будем считать, что первая колонка целое число, вторая это дата в региональном формате и третья колонка это текст, больше колонок у нас нет. Но для экспорта данная информация не нужна. Количество строк зависит от наполнения. Нулевая строка как обычно содержит заголовки колонок. Переменная FileName инициализирована и содержит имя файла, с должным расширением; Обработка ошибок не ведется, кроме необходимых случаев. 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 При выводе в данном формате преобразования не нужны, наша задача состоит в том, чтобы сделать данные колонок одинаковой ширины, нам даже не нужны разделители для колонок, но удобнее будет их сделать в виде одного пробела, что бы можно было обрабатывать файл любым текстовым редактором. Заголовки и данные выводятся в едином цикле и как договорились будем их разделять пробелом. Умолчания для примера: StringGrid создан и содержит некоторое количество колонок и строк, количество определяется при экспорте; Так как отсутствует информация о типах данных в колонках StringGrid, то будем считать, что первая колонка целое число, вторая это дата в региональном формате и третья колонка это текст, больше колонок у нас нет. Но для экспорта данная информация не нужна. Количество строк зависит от наполнения. Нулевая строка как обычно содержит заголовки колонок. Переменная FileName инициализирована и содержит имя файла, с должным расширением; Обработка ошибок не ведется, кроме необходимых случаев. 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], SG.Cells[0, I] ])); end; finally CloseFile(F); end; end; Для выравнивание ширины колонок использована функция Format, вместо встроенного выравнивания WriteLn, поскольку последняя добавляет пробелы слева, а нам нужны пробелы справа. Вместо функции Format можно использовать свою функцию, или функции из других библиотек, или из Дельфи, если подобная функция появилась в последних версиях. Не важно, что использовать, важно чтобы строки были дополнены справа пробелами до нужной длины. Разберем форматную строку %-25.25s – Символ % это признак спецификатора формата, символ «-» означает выравнивание влево, 25 означает, что длина будет дополняться до 25 символов, а .25 означает максимальное количество символов в строке будет 25, остальные символы будут отбрасываться, символ s указывает не тип данных, в данном случае это означает, что в функцию передается строковое значение. Количество спецификаторов не ограничено, в нашем случае их три и мы обязаны передать именно три параметра, что и делается, значения передаются как открытый массив. Символы пробелов, между спецификаторами, у нас выполняют роль разделителей колонок. На первый взгляд это сложно, но после того, как Вы освоите синтаксис этой функции, Вы сможете оценить всю ее мощь. Пример 4, импорт и экспорт данных для TStringGrid Остался еще один, последний пример, обеспечения импорта и экспорта данных из таблицы TStringGrid и обратно. Для этого выберем формат Tab Delimited, как очень экономичный и гибкий для нашей цели. Нам не придется бороться с их количеством, поскольку эти значения будут восстановлены автоматически. Нулевая строка будет содержать всю необходимую информацию о таблице. Единственно, что требуется обеспечить, чтобы все колонки данных в таблице имели свой заголовок. Наша задача состоит в том, чтобы вставить символ табуляции между колонками данных. Заголовки и данные выводятся в едином цикле и разделяются символом табуляции, при импорте заголовки будут играть важную роль, по ним будет определяться количество колонок. Умолчания для примера: StringGrid создан и содержит некоторое количество колонок и строк, количество определяется во время выполнения; Информация о типах данных в колонках StringGrid отсутствует, но она нам и не нужна, мы должны уметь экспортировать любую информацию. Количество строк зависит от наполнения. Нулевая строка как обычно содержит заголовки колонок. Переменная FileName инициализирована и содержит имя файла, с должным расширением; Обработка ошибок не ведется, кроме необходимых случаев. 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[J, I]); // пишем отдельную ячейку 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[SG.Col, SG.Row] := 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[SG.Col, SG.Row] := 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(''); writeln(''); writeln('Hello, world!'); writeln(''); writeln('') end. Если вы откомпилируете данную программу в Дельфи 2 или 3 и затем запустите ее из web браузера подключенного к web серверу, где оно записано в исполнимом виде в исполняемом каталоге, таком как cgi-bin, то вы увидите текст "Hello, world!" на странице. Заключение Ну вот теперь вы знаете про файлы Паскаля все, ну или почти все :-), остальное в ваших руках. .
Форма входа
Календарь новостей
«  Апрель 2024  »
ПнВтСрЧтПтСбВс
1234567
891011121314
15161718192021
22232425262728
2930
Поиск по новостям
Друзья сайта
Prise-clan! © 2007