Динамическое создание строк

Динамическое создание строковых переменных
Когда вы описываете строковую переменную, вы задаете количество символов, которое будет хранится в этой переменной. Если вы не знаете на момент описания строки ее размер, но можете узнать его во время выполнения программы, то можно воспользоваться динамическим созданием строки. Т.е. при описании переменной не указывается ее размер. Размер задается при создании строки, в том месте программы, в котором это будет необходимо.

Зачем
Например:
1) Допустим, что у вас есть текстовый файл. Вы хотите загрузить его содержимое в строковую переменную, а потом отображать эту переменную посредством Text-бокса. Заранее вы не знаете какого размера текстовый файл будет открывать пользователь. Следовательно, вы не можете задать размер строки жестко, так как вы обычно это делаете:

varFileContent    STRING(1024) ! переменная размером 1кБ

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

2) Когда вы начнете использовать Windows API функции в своих Кларион программах, то заметите, что при работе со строками используется строки типа CSTRING. В Кларионе же, в основном используются строки типа STRING. Раз вы привыкли работать с типом STRING, то можно динамически создавать CSTRING-строки, приравнивать им значения STRING-строк для использования в API-функциях, а потом уничтожать CSTRING-строки, сохраняя при этом удобство работы.
CSTRING-строка аналогична STRING-строке, в CSTRING-строке добавляется завершающий символ ‘0’. Таким образом, если взять строки, в которых находятся одинаковые значение, то длина CSTRING-строки больше на один символ, чем длина STRING-строки.

Основы
Так как мы решили создавать строку динамически, то при описании переменной не указывается ее размер, более того, сама строка не создается, существует просто ссылка на строку:

MyString    STRING(20) ! описание "обычной" строки
DynString   &STRING    ! описание ссылки на строку

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

DynString &= new STRING(20) ! создание строки размером в 20 символов

После создания строки, необходимо проверить, успешным ли было создание. Я не знаю причины, по которой создание строки будет ошибочным, кроме нехватки оперативной памяти. Также, вы конечно помните, что Windows использует жесткий диск в качестве оперативной памяти при ее нехватке (swap). Поэтому, вероятность ошибки достаточно мала.
При возникновении ошибки, в конечном итоге ваша программа не будет работоспособной, проще говоря, она упадет, а вот как она упадет с GPF-ом или без него, зависит уже от вас 🙂
Как говорил один из гуру: «Или пользователи будут говорить «Программа иногда глючит» или «Компьютер иногда глючит».

Подводя итоги: перед использовании переменных или объектов вы обязаны проверять, что их создание прошло успешно.

Проверка осуществляется следующим образом:

if DynString &= NULL
   ! переменная не была создана, ошибка
else
   ! все хорошо
end

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

DynString = 'Петя Петров'
message(DynString)

И последний, но важный момент: после завершения работы со строкой вы должны уничтожить ее. В противном случае, память которая используется этой строкой не будет очищена, т.е. останется занятой. Что опять таки не хорошо.
За уничтожение отвечает оператор dispose:

dispose(DynString)! Уничтожить строку

После уничтожения строки, ее нельзя использовать. Этот момент достаточно важен, так как зачастую при достаточно сложном коде, вы можете забыть, что уже уничтожили переменную. В этом случае вы получите 100%-ый GPF. Поэтому, немного перефразируя вышесказанное: перед использованием ссылочных переменных или объектов, вы обязаны проверять, что они существуют.

А теперь полностью весь код:

DynString   &STRING    ! описание ссылки на строку

 code

DynString &= new STRING(20)  ! создание строки размером в 20 символов
if NOT DynString &= NULL     ! проверка на ошибку создания
   DynString = 'Петя Петров' ! присвоение значения
   message(DynString)        ! показали
   dispose(DynString)        ! уничтожили
end
:
if NOT DynString &= NULL     ! при дальнейшем использовании
   DynString = 'Петя Петров' ! проверяем, что переменная
End                          ! существует

Как вы понимаете, строковые переменные могут быть использованы и как свойства класса. И конечно же и в классах бывает так, что заранее размер переменной неизвестен, поэтому возникает желание использовать динамическое создание свойства, для того, чтобы объект класса занимал как можно меньше памяти.

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

MyClass CLASS,TYPE
MyData     &STRING,PRIVATE

Destruct   PROCEDURE                 ! деструктор
GetMyData  PROCEDURE(),STRING        ! получить данные
SetMyData  PROCEDURE(STRING NewData) ! сохранить данные
        END

!*****************************************************
MyClass.Destruct    PROCEDURE
 CODE
 IF NOT SELF.MyData &= NULL THEN DISPOSE(SELF.MyData).
!*****************************************************
MyClass.GetMyData   PROCEDURE
 CODE
 IF SELF.MyData &= NULL
    RETURN ''
 ELSE
    RETURN SELF.MyData
 END
!*****************************************************
MyClass.SetMyData   PROCEDURE(STRING NewData)
 CODE
 ! если строка ранее была создана, то уничтожим ее
 IF NOT SELF.MyData &= NULL THEN DISPOSE(SELF.MyData).

 IF LEN(NewData)
    SELF.MyData &= STRING(LEN(NewData)) ! создадим новую строку
    SELF.MyData  = NewData              ! присвоение значения
 END

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

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

Автоматизация процесса создания строк: способ 2
Как минимум с версии Клариона 5.5 существует интерфейс IDynStr, который как раз и служит для цели создания динамических строк. Описание методов интерфейса вы можете найти в директории LibSrc в файле DynStr.inc, в стандартной документации описание отсутствует. Использовать интерфейс достаточно просто:

DS &IDynStr ! описание объекта
 code
 DS &= NewDynStr()             ! Обязательно используется ф-ия NewDynStr
 DS.Cat('динамическая строка') ! создали строку и заполнили ее данными
 message(DS.Str())             ! Показали строку
 DisposeDynStr(DS)             ! уничтожили строку

Полное описание и примеры использования, вы можете скачать ниже.

  • Функции из способа 1 входят в состав xClasses (830). Пример написан на C55H ABC. Для установки читайте readme.1st.
  • DynStr Demo (883) написан на C6.1 build 9033. В пример входит описание интерфейса IDynStr (IDynStr.chm)

© Project Zero, 2005-2006. Все права защищены.