Когда речь идет о работе программы с текстовым файлом, подразумеваются процедуры ввода данных из файла в программу или, наоборот, запись этих данных в файл программой. Для текстового файла допустима простая работа с файлом без особых дополнительных механизмов, которые применяются для работы со специализированными файлами, такими как при загрузке данных из Excel или работе программы с базой данных. Разумеется, Delphi располагает возможностями работать с файлами с использованием компонентов. Но в данной статье рассматривается механизм прямой работы с текстовым файлом без использования дополнительных компонентов.
Итак, в общем виде, работа с файлом заключается в следующих этапах:
1. подключение к файлу – связь с внешним файлом, указание режима подключения;
2. выполнение операций записи в файл или чтения из файла;
3. завершение работы с файлом.
Подключение к файлу
Для связи программы с файлом используется специальная переменная – "Файловая переменная". Объявляется она так же как и любая переменная в Delphi. Тип это переменной может быть File для типизированных (хранящих данные определенного типа) файлов, а можно указать TextFile, что будет означать тип обычного текстового файла. Объявление переменной:
var
f : TextFile;
В исполняемом коде программы выполняется подключение к внешнему файлу:
Команда AssignFile, выполняет связь файловой переменной с внешним файлом. Вторым параметром указывается адрес файла. Он может быть задан относительным или абсолютным. Если указать только имя файла, то программа будет пытаться обнаружить его в той же директории, где она сама и находится. Абсолютный путь указывается от корневого диска:
Использование относительной директории дает возможность не привязываться к конкретным дискам и адресам. Например:
AssignFile(f, ‘datainput.txt’ ); // во вложенной папке относительно директории с программой
AssignFile(f, ‘..input.txt’ ); // на уровень выше директории с программой
AssignFile(f, ‘..datainput.txt’ ); // во вложенной папке на уровень выше директории с программой
После того как выполнено подключение, выполняется процедура, устанавливающая режим работы с файлом. В основном это режим чтения или записи. Эти режимы назначаются процедурами Reset() ( для чтения) и rewrite() (для записи):
* Для команды Rewrite() следует учитывать, что при ее выполнении, она либо создает файл, указанный в файловой переменной, либо если он уже есть перезаписывает файл заново, удаляя старый без какого-то предупреждения.
Любую из указанных команд можно использовать без команды AssignFile(). Для этого достаточно вторым параметром указать путь к файлу. Таким образом, она сразу выполнит привязку файла к файловой переменной и назначит режим работы с этим файлом:
Reset(f, ‘C:myproginput.txt’ ); // чтение
Rewrite(f, ‘C:myproginput.txt’ ); // запись
Операции с файлами
Для чтения из файла, необходимо назначить режим чтения и использовать команду Readln(), которая будет вводить в строковую переменную по одной строке из файла. Затем с этой переменой можно выполнить необходимые действия.
Обычно для загрузки всех строк из файла используется оператор цикла. Для того, чтобы определить, что файл закончился используется функция EOF() (End Of File). Таким образом получается цикл, в котором последовательно в строковую переменную вводятся все строки файла и завершающийся после окончания фала:
while ( not EOF(f)) do begin
Readln(f, s);
end ;
Для записи, назначение режим записи в файл и командой Writeln() производится запись по строкам.
Закрытие файла
По завершении работы с файлами, особенно в случае записи в них данных, необходимо корректно завершить работу с файловой переменной. Это делается для того, чтобы сохранить все внесенные в файл изменения.
Примеры работы с текстовыми файлами в Delphi
Чтение в переменную одного значения из файла:
var
f : TextFile;
s : String;
begin
AssignFile(f, ‘input.txt’ );
Reset(f);
Загрузить все строки файла в компонент Memo:
var
f : TextFile;
s : String;
begin
AssignFile(f, ‘input.txt’ );
Reset(f);
while ( not EOF(f)) do begin
Readln(f, s);
myMemo.Lines.Add(s);
end ;
Следует отметить, что для этой задачи проще воспользоваться командой самого компонента Memo LoadFromFile().
Записать строку в файл:
var
f : TextFile;
begin
AssignFile(f, ‘input.txt’ );
Rewrite(f);
Writeln(f, ‘My text’ );
Записать в текстовый файл все строки из компонента Memo:
var
f : TextFile;
i : Integer;
begin
AssignFile(f, ‘input.txt’ );
Rewrite(f);
for i := 0 to myMemo.Lines.Count — 1 do
Writeln(f, myMemo.Lines[i]);
CloseFile(f);
end ;
Как и для чтения из файла в Memo, так и здесь, имеется специальная команда:
Текстовый файл отличается тем что он разбит на разные по длине строки, отделенные символами #13#10. Есть 2 основных метода работы с текстовыми файлами — старый паскалевский способ и через файловые потоки. У обоих есть преимущества и недостатки. Через потоки способ проще поэтому начнем с него.
Итак у всех потомков класса TStrings (TStringList, memo.Lines и т.п. ) есть методы записи и чтения в файл — SaveToFile, LoadFromFile. Преимущество — простота использования и довольно высокая скорость, недостаток — читать и писать файл можно только целиком.
1. Загрузка текста из файла в Memo:
2. Сохранение в файл:
3. А вот так можно прочитать весь файл в строку:
Паскалевский метод доступа
Для более тонких операций над текстовыми файлами придется освоить очень древний паскалевский способ.
Итак, для доступа к текстовым файлам используется переменная типа TextFile. До сих пор не совсем понимаю что это такое физически — что-то типа "внутреннего" паскалевского Handle на файл.
Итак чтобы ассоциировать файл на диске с переменной надо проделать следующие опрерации:
1) Определяем файловую переменную:
2) Ассоциируем ее:
3) Теперь надо этот файл открыть, есть 3 варианта:
- файла нет или он должен быть перезаписан, открытие для записи: Rewrite(f)
- файл есть и его надо открыть для чтения (с первой строки): Reset(f)
- файл есть и его надо открыть для дописывания строк в конец: Append(f)
Как видите не хватает очень полезных функций таких как открытия файла для чтения с произвольной строки и для записи в файл произвольной строки. Но надо учесть, что так как длины строк разные, не существует никакого способа узнать физическое место начала например 1000 строки, не прочитав всю тысячу строк. Для записи ситуация еще сложнее — вставить строку означает перезаписать всю информацию после этой строки заново. Таким образом варианты только следующие:
- Перезаписать весть файл
- Читать с первой строки
- Дописать что-то в конец
- Читать и писать файл целиком (см. выше работу через TStrings)
В конце работы открытый файл нужно закрыть:
Теперь пусть у нас есть строковая переменная s для чтения строки из файла
Чтение предварительно открытого файла:
ReadLn(f, s) — будет прочитана текущая строка и позиция чтения переведена на следующую позицию.
А как прочитать весь файл?
Хорошо, а если файл несколько метров есть ли способ поставить какой-нибудь ProgressBar или Gauge чтобы показывал сколько считано? Есть, но не совсем прямой — не забыли, сколько строк в файле заранее мы не знаем, узнать можно только прочитав его весь, но показометер мы все-таки сделаем:
Теперь комментарии к коду.
- Функцию GetFileSize я рассмотрю после, она немного по другому подходит к чтению файла (кстати я знаю еще по крайней мере 3 способа ее реализации, поэтому не нужно указывать что это можно сделать легче, быстрее или просто по другому — просто давайте разберем это позже)
- Переменная i — все время указывает на количество байт которое мы считали — мы определяем длину каждой строки и прибавляем 2 (символы конца строки). Зная длину файла в байтах и сколько байт прочитано можно оценить и прогресс, но
- Если ставить изменение прогресса после каждой строки, то это очень сильно тормознет процесс. Поэтому вводим переменную j и обновляем прогресс например 1 раз на 1000 прочитанных строк
- Переменная Canceled — глобальная переменная. Поставьте на форму кнопку, в обработчике нажатия поставьте Canceled:=True; и нажатие кнопки прервет чтение файла.
Теперь как писать в текстовый файл:
Запись целой строки:
Запись кусочка строки(те следующая операция записи будет произведена в ту же строку):
Если переменная s содержит больше 255 символов (т.е. является длинной строкой), то таким способом ни фига не запишется, в файл вместо строки попадут 4 байта указателя на нее. Надо делать так:
Работа через WinAPI
Раздел написан Podval (примеры к сожалению на С++)
Любителям WinAPI посвящается. Функции FileOpen, FileSeek, FileRead. Возьмем форму, положим на нее кнопку, грид и Опен диалог бокс. Это для Билдера, но какая нам в данном случае разница?
Потренируемся еще. Функции FileExists, RenameFile, FileCreate, FileWrite, FileClose. Бросим на форму Save dialog box.
(с) Оба примера взяты из хелпа по Borland C++ Builder 5.
Первоисточник тот же.
Может быть, такая функция полезна будет, хотя о программном поиске файлов и так много наговорено. Но на всякий случай, как говорится.
В этом примере идет поиск в текущем каталоге и каталоге Windows
В дополнение к Дате/Времени
Для конвертации возвращаемого значения в TDateTime:
Теперь разберем типизированные файлы. Типизированный файл — это файл в котором записанны идентичные структуры. Например любой файл можно считать файлом байтов — т.е. можно его читать байт за байтом, можно перейти сразу к любому байту по его номеру, можно сразу узнать сколько байт в файле, можно заменить любой байт на другой не перезаписывая файл. Теперь все это в примерах:
Объявляем файл байтов:
Ассоциируем файловую переменную с физическим файлом:
Теперь мы можем либо перезаписать/создать файл:
Либо открыть существующий для чтения и записи:
Обратите внимание, что функция Reset хотя и имеет такой же формат как и для текстовых файлов, но открытый ей файл можно и читать и писать, в отличие от текстовых.
Теперь функции работы с файлом:
Все эти функции не работают с файлами большими 2 Gb.
После работы файл надо закрыть:
Приведенные выше механизмы будут работать с любым файлом, так как любой файл можно считать файлом байтов. Теперь где это можно использовать? В принципе везде, но в подавляющем большинстве случаев это будет очень неудобно, ведь скорость считывания при чтении по байтам будет на порядки более низкой чем другими способами. Однако в некоторых случаях этот способ может быть очень полезен. Например в программе вам надо заменить 100й байт файла на другой, или прочитать 100й байт файла, например во всяких читерских программах, при взломе и т.п. Здесь такой доступ будет весьма удобен. Гораздо более интересным представляется дальнейшее развитие технологии типизированных файлов (их еще лет 15 назад называли "Файлы прямого доступа"). Представим себе, что файл состоит не из байт а из более сложных структур. Например мы имеем некоторую информацию в виде:
Обратите внимание, что все элементы записи точно типизированны, нет ни длинных строк, ни открытых массивов, ни объектов, другими словами, заранее точно известно сколько именно байт будет занимать переменная этого типа. Объявим переменную этого типа:
и файл этого типа:
Теперь мы можем читать и писать сразу целую структуру, абсолютно так же как и если бы это был один байт:
Все остальные функции приведенные в предыдущей статье будут работать так же, только одно отличие — Seek и Size оперируют не с количеством байт, а с количеством записей.
Идем дальше. Есть такое понятие как нетипизированный файл. Это такой файл который содержит разнородные элементы. Например файл EXE — вначале он имеет заголовок, затем двоичный код, в конце какие-то ресурсы. Все части файла имеют разную длину и разную структуру. Тут уже обратится к произвольному элементу сложно, обычно надо вначале узнать где этот элемент находится, подчас это записано в предыдущем куске информации. Работа с такими файлами достаточно сложна и требует вручную разработки алгоритмов его чтения, но в связи гибкостью структуры и компактностью такие файлы составляют большинство. Для работы с нетипизированными файлами используют процедуры BlockRead и BlockWrite, которые позволяют читать/писать произвольное количество байт. Привожу пример пользования этими функциями из справки по Дельфи:
Этот код копирует из одного файла в другой. Замечания по поводу этого метода работы с файлами — плюсы — очень высокая скорость, особенно если размер буфера увеличить до 64kb-512kb, что позволит считывать файл достаточно большими кусками, чтобы обеспечить отсутствие простоев винчестера, к тому же обеспечивается очень высокая гибкость в работе. Минусы — сложность разработки, необходимость вручную писать все детали механизма чтения/записи и интерпретации данных.
Пожалуй на этом можно было бы и завершить описание работы с файлами средствами Паскаля и файловых переменных, но заглянув в Help Дельфей я обнаружил еще несколько функций достойных упоминания.
Теперь разберем возможности работы потомка TStream — TFileStream — файловый поток. Этот класс был специально введен для работы с файлами. Для работы с файловым потоком Вам надо записать в Uses модули classes, Sysutils (classes — включает в себя собственно определение класса, Sysutils — некоторые константы необходимые для работы).
Вот пример записи/перезаписи файла:
Теперь небольшой разбор:
TFileStream.create — конструктор класса, его вызов требует указания имени файла и опций его открытия, следующие опции определены:
Теперь метод Write — этим методом в файл пишется любая информация из буфера любого типа, Вам надо указать только буффер и количество записываемых байтов. В данном случае используется переменная типа String в качестве буффера, но так как для длинных строк она представляет собой лишь указатель, то конструкция "pointer(s)^" заставляет обращаться именно к ее содержимому.
А вот этот код демонстрирует чтение файла с использованием файлового потока:
И пояснения к коду:
- Никаких проверок длину файла и его наличие здесь не делается — это демонстрационный код, а не готовая процедура чтения.
- Файл мы считываем в буффер типа PChar (с тем же успехом можно использовать массив или любой другой контейнер). Для тех кто не помнит — процедуры GetMem(p, 255) и FreeMem(p) — распределение памяти для строки и освобождение памяти.
- Метод потока Seek позволяет установить текущую позицию считывания/записи файла. Первый параметер — номер байта, второй — это от чего считать этот байт (у нас считать от начала файла), возможны варианты:
- soFromBeginning — от начала файла
- soFromCurrent — от текущей позиции считывания
- soFromEnd — от конца файла (в этом случае номер байта должен быть отрицательным или равным нулю)
- Собственно считывание из потока осуществляется методом read, в котором указывается в качестве параметров буфер в который мы читаем и желаемое количество байт для чтения. Метод read является функцией, которая возвращает количество байт реально прочитанных из потока.
Заканчивая о файловых потоках хочу упомянуть о методе
CopyFrom который позволяет перекачивать информацию из одного потока в другой и о свойствах:
Size — размер файла
Position — текущая позиция чтения/записи потока
Работа с файловыми потоками весьма быстра, этот класс, являясь классом VCL, в то же время базируется на низкоуровневых функциях Windows, что обеспечивает очень высокую скорость работы и стабильность операций. К тому же многие компоненты и классы VCL поддерживаю прямое чтение и запись с файловыми потоками, что значительно упрощает работу — например TStringList, TBlobField, TMemoField и другие.
Файловые потоки могут быть рекомендованы к использованию в большинстве случаев для чтения и записи файлов (за исключением специфических ситуаций, требующих каких-то других подходов), другими словами если вам надо просто записать или считать файл, используйте файловые потоки.
Работа через Handle
Еще один способ работы с файлами — это открытие Handle на файл и работу через него. Тут есть 2 варианта — можно использовать функции Дельфи или использовать WinAPI напрямую.
При использовании функций Дельфи можно применять следующие функции:
FileOpen(FileName, fmOpenWrite or fmShareDenyNone) — функция открывает файл и возвращает целое цисло — Handle на файл. Параметры функции — имя файла и тип доступа (все типы доступа я перечислил ранее). Если файл успешно открыт то Handle должен быть положительным цислом, отрицательное число — это код ошибки.
Во всех остальных функциях используется именно значение Handle, возвращаемое этой функцией.
FileClose(Handle: Integer) — закрывает файл
FileRead(Handle: Integer; var Buffer; Count: Integer): Integer;
FileWrite(Handle: Integer; const Buffer; Count: Integer): Integer;
Эти функции для чтения/записи файла, где Buffer любая переменная достаточного размера для чтения/записи куска информации (обычно типа PChar или массив), Count-количество байт, которое Вы желаете записать/прочитать. Функции возвращают количество байт которые реально были прочитанны или записаны.
Этот тип доступа к файлам применяется весьма редко. Дело в том что он практически дублирует соответствующие функции WinAPI и к тому же обычно работает несколько медленнее, чем например потоки. И все же использование функций FileOpen и FileClose не лишено привлекательности. Наряду с тем что эти функции намного легче в использовании соответствующих функций WinAPI (можете сравнить — FileOpen имеет 2 параметра, cooтветствующая функция WinAPI — CreateFile имеет 7 параметров, большая часть из которых реально требуется лишь в ограниченном числе случаев) этот путь доступа открывает возможность прямого использования всех функций WinAPI при работе с файлами, которые требуют Handle на открытый файл.
Дельфи предоставляет довольно широкие возможности по файловым операциям без использования механизмов открытия/закрытия файлов.
Вот список наиболее употребляемых функций, большинство из которых в качестве параметров нуждаются только в имени файла:
ChDir(NewCurrentPath: string); — изменяет текущий каталог (в среде Windows сие конечно не так актуально как в ДОС, но все же), прочитать же текущий каталог можно функцией GetCurrentDir, а текущий каталог для определенного драйва — GetDir.
CreateDir(const Dir: string): Boolean; — создает каталог. При этом предыдущий уровень должен присутствовать. Если вы хотите сразу создать всю вложенность каталогов используйте функцию ForceDirectories(Dir: string): Boolean; Обе функции возвращают True если каталог создан
DiskFree(Drive: Byte): Int64; — дает свободное место на диске. Параметр — номер диска 0 = текущий, 1 = A, 2 = B, и так далее
DiskSize(Drive: Byte): Int64; — размер винта. Обратите внимание на то что для результата этой и предыдущей функций абсолютно необходимо использовать переменную типа Int64, иначе максимум того что вы сможете прочитать правильно будет ограничен 2Gb
FileExists(const FileName: string) — применяется для проверки наличия файла
FileGetAttr(const FileName: string): Integer;
FileSetAttr(const FileName: string; Attr: Integer): Integer; — функции для работы с атрибутами файлов. Вот список возможных атрибутов:
RemoveDir(const Dir: string): Boolean; — удаляет папку(пустую)
DeleteFile(const FileName: string): Boolean; — удаляет файл
RenameFile(const OldName, NewName: string) — переименовывает файл
Информация о файле
Привожу пример функции которая собирает довольно большое количество информации о выбранном файле:
Скорее всего эта функция как есть вряд ли понадобится, так как наверняка бОльшее количество определяемых параметров избыточно, тем ни менее может кому пригодится как пример выяснения информации о файле.
Теперь поговорим о поиске файлов. Для этой цели могут использоваться процедуры FindFirst, FindNext, FindClose, при участии переменной типа TSearchRec которая хранит информацию о текущем статусе поиска и характеристики последнего найденного файла.
Пример иллюстрирующий поиск всех файлов и каталогов в определенном каталоге:
Примечания по приведенному коду:
- Первыми в список могут попадать файлы с именами "." и ".." — это ДОСовские имена для переходов на "родительский уровень", иногда нужна обработка для их игнорирования.
- FindFirst в качестве первого параметра принимает шаблон для поиска, так как он был принят для ДОС. Если шаблон не включает путь то файлы будут искаться в текущем каталоге.
- FindFirst требует задания атрибута для файла — здесь мы искали все файлы, если надо какие-то определенные (например только скрытые, или только каталоги) то надо это указать, список всех атрибутов я уже приводил выше.
- SearchRec переменная связывает во едино FindFirst и FindNext, но требует ресурсов для своей работы, поэтому желательно ее освободить после поиска процедурой FindClose(SearchRec) — на самом деле утечки памяти небольшие, но если программа работает в цикле и долгое время пожирание ресурсов будет значительным.
- FindFirst/FindNext — работают не открывая файлы, поэтому они корректно находят даже Swap файлы Windows.

Работа с текстовыми файлами в Delphi
Здесь — конец строки и — перевод каретки на новую строку. Таким образом, чтобы найти конец первой строки мы должны сканировать весь текст с целью поиска признака конца строки и перевода каретки ( и ). Это очень неудобно. А если у вас файл имеет размер в 100 строк и вам нужно получить доступ к 75-й строке? Как вы думаете, долго мы будем искать нужную строку? Нет, точнее сказать, что не долго, а неудобно.
Тут на помощь приходит объект TStrings, который является простым контейнером (хранилищем) для строк. Можно еще пользоваться более продвинутым вариантом этого объекта TStringList. Если посмотреть на иерархию объекта TStringList (рис. 10.3), то мы увидим, что он происходит от TStrings. Это значит, что TStringList наследует себе все возможности объекта TStrings и добавляет в него новые.
Для работы с объектом надо объявлять переменную типа объект:
f:TStringList; //Переменная типа объект TStringList.
TP ersi stent l
Иерархия объекта TStringList (снимок из файла помощи Delphi):

Инициализируется эта переменная как всегда методом create. Никаких параметров не надо. Чтобы освободить память объекта и уничтожить его, применяется метод Free. Метод использования объекта показан в листинге:
f:TStringList; //Переменная типа объект TStringList. begin
f:= TStringList.Create(); // здесь можно использовать класс f.Free; end;
В этом примере только создан новый объект и сразу произведено его уничтожение, без использования.
Давайте снова ВСПОМНИМ, ЧТО TStringList происходит ОТ TStrings. Использовать TStrings напрямую нельзя, потому что это абстрактный объект. Абстрактный объект— объект, который представляет собой пустой шаблон. Он может даже ничего не уметь делать, а только описывать какой-то вид или шаблон, на основе которого можно выводить полноценные объекты. Вот так TStringList добавляет в TStrings свои функции так, что он становится полноценным объектом.
В результате получается, что мы не можем объявлять переменные типа TStrings и использовать этот объект, потому что это всего лишь шаблон. Это и так и не так. Переменную мы можем объявлять, но использовать сам объект не можем. Зато мы можем объявить переменную типа TStrings, но использовать эту переменную как объект TStringList, потому что он происходит от первого. Это значит, что код, представленный в листинге 10.9, идентичен предыдущему.
f:TStrings; //Переменная типа объект TStringList. begin
f:= TStringList.Create(); f.Free; end;
В этом примере мы объявили переменную типа TStrings, но при создании про- инициализировали ее объектом TStringList. Это вполне законная запись, потому что объект TStringList происходит от TStrings. Новая переменная f будет работать как объект TStringList, хотя и объявлена как TStrings. Главное— каким объектом переменная проинициализирована.
Такие трюки можно проводить только с родственными объектами. Они не будут рассматриваться в этой книге, хотя знать их нужно, потому что вам может встретиться что-то подобное в чужом коде.
В разд. 7.6 мы уже познакомились с классом TStrings и его основными функциями, а сейчас оказывается, что это абстрактный класс и его нельзя создавать. Как же так получается? На самом деле, большинство компонентов используют именно TStringList.
Итак, давайте рассмотрим, как можно работать с помощью TStringList, с целью обработки текстовых файлов. Все очень просто. У него есть метод LoadFromFile, для которого нужно указать имя текстового файла. После этого через свойство strings можно получить доступ к любой строке, а в свойстве count находится число, указывающее на количество строк в файле. В листинге 10.10 показан пример загрузки файла и работа с первыми двумя строками. Обратите внимание, что строки нумеруются с нуля.
f:TStrings; //Переменная типа объект TStringList. begin
f.LoadFromFile(‘с:filename.txt’);// Загрузка текстового файла f.Strings[0]; II Здесь находится первая строка файла f.Strings[1]; // Здесь находится вторая строка файла
Давайте напишем пример, который будет искать в файле строку "Привет, Вася". Программный код, реализующий этот пример, приведен в листинге:
f:TStrings; //Переменная типа объект TStringList. i: Integer; // Счетчик begin
f.LoadFromFile(‘c:filename.txt’); // Загружаем текстовый файл
for i:=0 to f.Count-1 do // Запускаем цикл begin // Начало для цикла
if f.Strings = ‘Привет, Вася1 then //Если i-я строка равна нужной, то
Application.MessageBox(‘Строка найдена’, ‘Поиск закончен’, MB_OKCANCEL)
end; 11 Конец для цикла f.Free; end;
Точно так же можно изменять данные в файле. Например, если вам надо изменить 5-ю строку на "Прощай, станция Мир", то следует внести изменения, как показано в листинге:
f:TStrings; //Переменная типа объект TStringList. i: Integer; // Счетчик begin
f.LoadFromFile(г с:filename.txt’); II Загружаем текстовый файл
if f.Count>=5 then 11 Если в файле есть 5 строк, то изменить f.Strings[5] = ‘Прощай, станция Мир’;
f.Add(‘Прощай’);// Добавляем новую строку
f.SaveToFile(*с:filename.txt’); // Сохраняю результат f.Free; end;
В приведенном в листинге коде, прежде чем изменить 5-ю строку, происходит проверка, есть ли в файле эти пять строк. Если окажется меньше пяти, то при попытке изменения данных произойдет ошибка, ведь у объекта просто не выделено достаточно памяти.
В этом же примере в конец файла добавляется новая строка с помощью вызова метода Add. После этого результат сохраняется в том же файле с помощью метода SaveToFile. Если не вызывать метод сохранения, то все изменения пропадут, потому что данные изменяются в объекте, а не в файле. Именно поэтому объект надо сохранять обратно в файл.
На этом можно закончить рассмотрение. Помните, все, что мы говорили про TStrings ранее, В равной степени ОТНОСИТСЯ И К TStringList.






