Вторник
17.10.2017
07:01


Форма входа

Приветствую Вас Гость | RSS
Borland Delphi 7
Главная Регистрация Вход
Главная » 2009 » Август » 14 » Работа с текстовыми файлами
Работа с текстовыми файлами
14:05
TStringList
Практически любая программа взаимодействует с какими-либо типами файлов. Delphi позволяет нам работать с файлами различных типов. Текстовый файл – одна из этих разновидностей. В этих файлах информация расположена не сплошным блоком данных, а в виде строк, которые заканчиваются символом конца строки и перевода каретки. Можно работать с текстовыми файлами общим способом, то есть, побайтно считывать и записывать данные. Однако это не всегда удобно. Рассмотрим пример:

Привет!
Как жизнь?
Если мы считаем эти 2 строки сплошным блоком, то получим строку:

'Привет!' + #13 + #10 + 'Как жизнь?'
Это очень неудобно, так как для обработки строк нам каждый раз придется искать эти два символа конца строки. А если строк много, например 100? И если нужно получить доступ к 75-й строке?

Тут на помощь приходит объект TStrings, который мы использовали при обработке Memo, и который является простым контейнером для хранения строк. Однако использовать объект TStrings напрямую нельзя, так как этот объект является шаблоном для других объектов, работающих со строками. Вспомните, в компоненте Memo и ListBox мы тоже не использовали этот объект напрямую, мы использовали его "потомков" - Lines и Items. Поэтому лучше использовать более продвинутый вариант этого объекта – TStringList. Этот вариант наследует все возможности объекта TStrings и добавляет еще новые. Это, пожалуй, самый простой способ работы с текстовыми файлами.

Чтобы воспользоваться этим объектом, требуется вначале объявить переменную этого типа, затем проинициализировать ее, а в конце закрыть. В простейшем варианте это будет выглядеть так:

var
  f : TStringList; //объявили переменную - объект
begin
  f := TStringList.Create(); //проинициализировали
  f.Free; //освободили (уничтожили)
end;
Здесь мы создаем объект и тут же его уничтожаем, не работая с ним. Когда мы объявили переменную типа объект TStringList, мы указали компилятору, сколько оперативной памяти потребуется выделить в дальнейшем для этой переменной. Когда мы проинициализировали объект методом Create, мы выделили под него оперативную память. В дальнейшем, с этим объектом можно работать, записывать в него текст, считывать текст, пользоваться свойствами, методами и событиями этого объекта. Метод Free разрушает объект, уничтожает его в оперативной памяти. После этого к переменной f типа TStringList обращаться уже нельзя. Все, что должен был делать этот объект, должно быть описано до метода Free.

Чтение и запись с таким объектом происходит знакомым нам способом. Допустим, нам нужно прочитать текстовый файл и изменить в нем 10-ю строку:

  var
  f : TStringList;
begin
  f := TStringList.Create();
  //читаем текст из файла:
  f.LoadFromFile('c:\myfile.txt');
  //меняем текст 10-й строки (строки начинаются с 0):
  f[9] := 'Новая строка';
  //сохраняем измененный текст обратно в файл:
  f.SaveToFile('c:\myfile.txt');
  f.Free;
end;
Обратите внимание, что мы обратились к 9-му индексу, так как нумерация индексов начинается с нуля, и f[9] содержит 10-ю строку.

Свойство Count этого объекта возвращает общее количество строк, начиная с первой. Давайте напишем пример, в котором мы будем среди всех строк искать строку "Привет, Москва!". Если такая строка будет найдена, то программа выведет номер искомой строки, иначе выведет сообщение, что такой строки нет.

Вначале воспользуйтесь собственным редактором текстов и создайте текстовый файл, куда запишите 10-15 строк. Одну из строк сделайте "Привет, Москва!".

Затем делаем новое приложение, устанавливаем кнопку "Выполнить" и диалог OpenDialog. Затем создаем обработчик событий для кнопки, куда записываем следующий код:

procedure TForm1.Button1Click(Sender: TObject);
var
  f : TStringList;
  i : Integer;
begin
  //если ничего не открыли, выходим из процедуры:
  if not OpenDialog1.Execute then Exit;

  //читаем из файла:
  f := TStringList.Create();
  f.LoadFromFile(OpenDialog1.FileName);

  //ищем строку:
  for i := 0 to f.Count-1 do begin
  if f[i] = 'Привет, Москва!' then begin
  ShowMessage('Строка найдена под № ' + IntToStr(i+1));
  Break;
  end; //if
  end; //for

  //закрываем файл:
  f.Free;
end;
Здесь код достаточно прозрачен, однако еще раз обращаю ваше внимание на то, что индексы строк начинаются с нуля, а свойство Count возвращает общее количество строк. То есть, если в тексте лишь 2 строки и Count вернет цифру 2, то индексы этих строк будут 0 и 1, поэтому в цикле мы использовали диапазон от 0 до Count-1, иначе бы произошла ошибка переполнения цикла.

Итак, кроме того, что мы уже рассмотрели, этот объект имеет и другие, известные вам по Memo методы:

•Add – добавить новую строку в конец: 
f.Add('Новая строка');
•Clear – очистить объект (не будет текста): 
f.Clear;
•Insert – вставить строку. Есть 2 параметра – индекс вставляемой строки, и сама строка: 
f.Insert(3, 'Новая 4-я строка, следующие строки сдвинутся вниз');
•Delete – удалить строку. Есть параметр – индекс удаляемой строки. 
f.Delete(f.Count-1); //удалили последнюю строку

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

Memo1.Lines.SaveToFile()
Memo1.Lines.LoadFromFile()
Этот метод удобен и хорош, однако он не позволяет нам контролировать весь процесс записи в файл и чтения из файла. То есть, эти функции весь процесс чтения и записи выполняют скрыто от нас.

Однако бывает, когда программист испытывает необходимость контроля этих процессов, кроме того, не для всех типов эти функции доступны. Например, у нас есть текст в строковой переменной. Мы уже говорили, что строковая переменная может хранить почти неограниченное количество символов, то есть в переменную мы можем записать весь текстовый файл, включая и символы перехода на другую строку и возврата каретки (#13 #10). С функциями SaveToFile() и LoadFromFile() могут работать данные, которые имеют тип TStrings, а простые строки не могут их вызывать. Поэтому приходится делать записи в файл напрямую, и также напрямую их считывать.

Для работы с файлами многие программисты предпочитают использовать функции WinAPI. Несмотря на грозное звучание, ничего особо сложного в этих функциях нет, тем не менее, такие функции имеют один крупный недостаток. Корпорация MicroSoft постоянно вносит какие-то изменения в свои операционные системы. Так, в первых версиях Windows, для чтения файлов использовалась функция WinAPI _lread. Потом появилась функция ReadFile и Microsoft стала рекомендовать использовать в программировании именно ее. А затем появилась функция ReadFileEx, которая позволяет работать с файлами больших размеров. После каждого изменения этих функций очень часто приходится переделывать всю программу, чтобы она могла работать с новой операционной системой. А это отнимает много времени и создает дополнительные неудобства и для программистов, и для пользователей.

Поэтому в Delphi рекомендуется использовать встроенный специализированный объект TFileStream. Если Microsoft введет какие-то новшества, Borland учтет их в объекте, и нам останется лишь перекомпилировать нашу программу, не меняя ее кода. Кроме того, использование TFileStream намного проще, чем функции WinAPI. TFileStream - это объект, и как каждый объект, его нужно вначале объявить, а затем проинициализировать.

Первым делом необходимо создать переменную типа TFileStream:

var
  f : TFileStream;
Таким образом, мы объявили переменную типа объект TFileStream, и в дальнейшем можем работать с этой переменной, как с объектом. То есть, указывать имя этой переменной, а после точки выбирать свойства, методы и события этого объекта. Однако объявить переменную мало, требуется еще проинициализировать ее.

f := TFileStream.Create(параметры);
У метода Create объекта TFileStream может быть три параметра, причем третий параметр необязателен, его можно не указывать. Разберемся с этими параметрами.

Имя файла – этот параметр – простая строка, которая может содержать только имя файла, или полное имя файла, включая и адрес.

Режим открытия. Здесь можно указать один из следующих параметров:

•fmCreate – Создать файл с указанным в первом параметре именем. Если файл уже существует, он откроется в режиме для записи.
•fmOpenRead – Открыть файл только для чтения. Если файл не существует, произойдет ошибка, поэтому прежде требуется выполнить проверку на существование файла. Запись в файл в этом режиме невозможна.
•fmOpenWrite – Открыть файл для записи. При этом текущее содержимое файла уничтожается, и файл перезаписывается.
•fmOpenReadWrite – Открыть файл для редактирования, то есть, и чтения, и записи.
Права, с которыми будет открыт файл. Этот параметр необязателен, его можно не указывать. Он имеет следующие возможные значения:

•fmShareCompat – Другие приложения тоже имеют право работать с открытым файлом.
•fmShareExclusive – Другие приложения не смогут открыть файл.
•fmShareDenyWrite – Другие приложения смогут открыть файл только для чтения, записать в него данные они не смогут.
•fmShareDenyRead – Другие приложения смогут открыть файл только для записи, для чтения они не смогут его открыть.
•fmShareDenyNone – не мешать другим приложениям работать с файлом.
Пример:

f := TFileStream.Create('C:\MyFile.txt', fmOpenReadWrite, fmShareExclusive);
Для чего нужны права доступа к файлу? Например, текстовый файл может быть открыт стандартной программой "Блокнот", этот же файл мы можем открыть из нашего собственного текстового редактора. Редактор менеджера файлов FAR также может открыть этот текстовый файл. И программа MS Word тоже может его открыть! Теперь предположим, что наша программа делает запись в файл. В это же самое время какая-то другая программа также сохраняет изменения в этом файле. Наша программа сделала изменения и закрыла файл, а другая программа даже не подозревает об этих изменениях, и просто перезаписывает файл со своими изменениями. Таким образом, наши изменения просто теряются!

Тоже самое может произойти, если ваша программа выполнена в сетевом варианте. Например, есть какой-то специальный файл, в который сотрудники отдела записывают свои изменения. Этот файл может находиться на каком-то сетевом диске, и доступ к этому файлу имеет каждый сотрудник. И у каждого сотрудника установлена ваша программа. Таким образом, если одновременно двое сотрудников будут производить изменения в файле, то данные одного из них потеряются, так как программа другого перезапишет файл без учета этих изменений. Если вы пишете однопользовательскую программу, и доступ к файлу будет иметь только она, то про третий параметр можете вовсе забыть.

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

f.Free;
Теперь осталось разобраться со структурой файла, и методами чтения из него, и записи в него. Начнем со структуры.

Когда вы только открыли файл, позиция курсора устанавливается на начало, и любая попытка чтения или записи будет применена к этой позиции курсора. Чтобы прочитать (записать) в другую позицию, требуется передвинуть курсор. Для этого используют метод Seek, который имеет два параметра:

•Число, указывающее количество байт (символов), на которые требуется передвинуть курсор.
•Откуда нужно двигаться. Тут может быть три варианта: 
◦SoFromBeginning – двигаться на указанное количество байт от начала файла.
◦SoFromCurrent – двигаться от текущей позиции курсора.
◦SoFromEnd – двигаться на указанное количество байт от конца файла к его началу.
Итак, чтобы передвинуть курсор от начала файла на 10 байт, нужно выполнить команду:

f.Seek(10, soFromBeginning);
Метод Seek – это функция, она всегда возвращает количество байт смещения от начала файла. Этим свойством можно воспользоваться, чтобы узнать общее количество байт в файле:

Размер_Файла := f.Seek(0, soFromEnd);
Правда, если нам требуется просто посмотреть размер файла, то этот трюк не совсем подходящий: нам придется открыть файл, переместиться в его конец, и затем закрыть его. Как узнать размер файла более подходящим способом, узнаем позже.

Для чтения из файла нужно использовать метод Read, у которого тоже есть два параметра:

•Переменная, в которую будет записан прочитанный текст.
•Количество байт, которые следует прочитать.
Разберем пример чтения из файла 10 символов, начиная с 15-й позиции:

var
  f : TFileStream; //объявляем переменную
  buf : array [0..10] of Char; //буфер для хранения прочитанных данных
begin
  //открываем файл filename.txt для чтения и записи:
  f := TFileStream.Create('c:\filename.txt', fmOpenReadWrite);
  //перемещаемся на 15 символов вперед:
  f.Seek(15, soFromBeginning);
  //читаем в буфер 10 символов из установленной позиции:
  f.Read(buf, 10);
  Memo1.Lines.Add(buf); //скопировали эти 10 символов в Memo
  //уничтожаем объект и тем самым закрываем файл:
  f.Free;
end;
Метод Read возвращает количество реально прочитанных байт. Оно должно быть равно тому количеству байт, которые мы указали при вызове этого метода. Есть две причины, по которым это количество может быть не равно указанному:

1.При чтении был достигнут конец файла, и функция прочитала меньше байт, чем планировалось.
2.Ошибка на диске или любая другая проблема.
Для записи в файл используется метод Write. Есть два параметра и у него:

1.Переменная, содержимое которой нужно записать.
2.Число байт для записи.
Пользоваться методом записи можно точно также, как и методом чтения.


Категория: Статьи участников клуба | Просмотров: 16895 | Добавил: delphi | Рейтинг: 4.5/2 |
Всего комментариев: 0
Имя *:
Email *:
Код *:
Меню сайта

Разделы новостей
Программные обеспечения [6]
Учебники и справочники Delphi [15]
Лабораторные работы [2]
Статьи участников клуба [36]
Советы и трюки [1]

Календарь новостей
«  Август 2009  »
ПнВтСрЧтПтСбВс
     12
3456789
10111213141516
17181920212223
24252627282930
31

Поиск

Друзья сайта

Мини-чат

Наш опрос
Оцените мой сайт
Всего ответов: 71

Статистика

Copyright MyCorp © 2017