Кубанский государственный университет
Опубликован: 24.12.2013 | Доступ: свободный | Студентов: 682 / 9 | Длительность: 24:28:00
Лекция 3:

Иерархические модели данных. Деревья в СУБД Cache

3.2.6 Строки с разделителями

Элементы строк с разделителями называют полями.

Символ-разделитель нельзя употреблять в самих полях. Одной строкой можно записать, например, список фамилий работников отдела "Ива-нов^Петров^Сидоров". В качестве разделителя можно использовать любой печатаемый символ, в том числе и пробел. Естественно, в этом случае поле не должно содержать пробел. Поля также могут быть строками с разделителями, но разделитель внутри поля должен быть другим. Можно и эти поля представить в виде переменных с разделителями и т.д.

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

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

  • определение количества полей в строке;
  • выделение поля;
  • вставка значения в нужное поле, вставка дополнительных полей.

В Cache ObjectScript для решения этих задач используются функции $LENGTH, $FIND, $EXTRACT, $PIECE.

Итак, выбираем в качестве разделителя печатаемый символ, который не может использоваться в тексте полей, скажем: """, "+" и т. д. Строка с разделителями создаётся обычным присваиванием (команда "SET"). Создадим две строки str1="A~77~BD" и str2="A+B~A/B~A B" (листинг 3.23),

определим их длины (не в символах, а в полях) и выделим поля со второго по третье в строке str1.

USER>S str1="A^77^BD"
USER>S str2="A+B^A/B^A B"
USER>W "Длина str1=",$L(str1,"^") 
Длина str1=3
USER>W "Длина str2=",$L(str2,"^")
Длина str2=3
USER>W $P(str1,"^",2,3)
77^BD
Пример 3.23. Строки с разделителями

Количество полей определяется уже известной функцией $L[ENGTH] , в которой во втором аргументе записывается знак разделителя. Выделяется нужное поле функцией $PIECE, имеющей формат:

$P[IECE](строка, разделитель, первое_поле,последнее\_поле)

Например, функция $P(str,"^",1) выделяет первое поле, а $P(str, "^",1,3) — поля с первого по третье. Функция $PIECE относится к так называемым левым функциям. Это означает, что она может стоять в левой части команды присваивания. Вставим текст "QQ" на место второго поля, добавим четвёртое поле и седьмое поля (листинг 3.24).

USER>S str1="A^77^BD"

USER>S $P(str1,"^",2)="QQ" w str1 
A^QQ^BD
USER>s $P(str1,"^",4)="PP" w str1 
A^QQ^BD^PP
USER>s $P(str1,"^",7)="PP" w str1 
A^QQ^BD^PP^^^PP
Пример 3.24. Левая функция $PIECE

Заметим, что вставка в седьмое поле привела к созданию пустых пятого и шестого полей "A^QQ^BD^PP^^^PP", что и вызвало появление в записи строки двух дополнительных циркумплексов.

3.2.7 Списки

Одна из основных структур данных — список — создаётся специальной функцией $LISTBUILD (сокращенно $LB). Её формат:

$LISTBUILD(элeмeнт\_спискa [,элемент_списка ...])

Создадим список из двух элементов и с помощью команды ZZDUMP разберёмся с его структурой (листинг 3.25).

USER>S x=$LB("Red","Blue")

USER>ZZDUMP x
0000:  05 01 52 65 64 06 01 42 6C 75 65
Пример 3.25. Структура списка

В левой части строки коды символов, а в правой отпечатанная строка. Точками обозначены непечатаемые символы. Оказывается, элементы списка разделяются парами непечатаемых символов. Первый символ такой пары — это число в шестнадцатиричной системе счисления, равное количеству символов в следующем за разделителем элементе списка плюс 2. Вторым идёт символ с кодом 01. Разделители в конце списка не ставятся. Зададим список из одного пустого элемента и список из единственного элемента, представляющего пустую строку (листинг 3.26). Пустой список эквивалентен пустой строке

USER>S y=$LB(),   z=$LB("")  ZZDUMP x,y

0000: 01 
0000: 02 01
Пример 3.26. Пустой элемент и пустая строка —не одно и то же

При вставке в список значения не определённой переменной ошибка не появляется, а вставляется NULL (листинг 3.27).

USER>K a S list1=$LB(1,a,3)

USER>F i=1:1:3 W "i=",i,?10,$LISTGET(list1,i),!
i=1 1
i=2
i=3 3

Пример 3.27. Вставка значения не определённой переменной

Двухаргументная функция $LISTGET была использована для извлечения из списка указанного первым аргументом элемента с номером, указанным вторым аргументом.

Результат конкатенации двух списков есть список. Для работы со списками кроме их создания необходимо определять правильность списков (функция $LISTVALID) и их длину (функция $LISTLENGTH), сравнивать списки ($LISTSAME). Навигация по спискам производится с использованием функций $LISTDATA, $LISTFIND, $LISTGET, $LISTNEXT. Предусмотрены преобразования строк с разделителями в списки (функция $LISTFROMSTRING) и обратное преобразование ($LISTTOSTRING).

Функция $LISTVALID

Булева функция $LISTVALID проверяет правильность списка. Для правильных списков возвращает 1, а для неправильных 0. Формат:

\$LISTVALID(вырaжeниe)

или

\$LV(вырaжeниe)

Проверяемый список должен быть создан с помощью функций $LIST-BUILD и $LISTFROMSTRING, либо извлечён из существующего списка с помощью $LIST. В листинге 3.28 приведено несколько примеров, достаточных, чтобы разобраться с деталями.

USER>S r="A",s=33,t="",u=$LB("A","B"),v=$LB(1) 
USER>W $LV(r),?10,$LV(s),?20,$LV(t)
0  0  1
USER>W $LV(u),?40,$LV(v)
1  1
USER>S y=$LB(NULL)    w $LV(y)
1
Пример 3.28. Функция $LISTVALID

Вы видите, что пустой элемент t есть правильный список.

Функция $LISTLENGTH

Возвращает длину списка. Неопределённые элементы засчитываются. Вложенные списки учитываются как один элемент.

\$LISTLENGTH(список)

или

\$LL(список1)

Функция $LISTSAME

Проверяет совпадение списков. Формат:

\$LISTSAME(список1, список2)

или

\$LS(список1, список2)

Примеры употребления приведены в листинге 3.29.

USER>s x=$LB("A","B"),   y=$LB("B","A"),   z=$LB("A","B"),  v=$LB(), w=$LB("")
USER>w "$LS(x,y)="_$LS(x,y),!,"$LS(x,z)="_$LS(x,z),!,"$LS(v,w)="_$LS(v,w),!
$LS(x,y)=0
$LS(x,z)=1
$LS(v,w)=0
Пример 3.29. Функция $LISTSAME

Обратите внимание на то, что пустая строка (в примере в листинге 3.28) и список из одного пустого элемента (x) оба допустимые списки, но они не равны между собой.

Функция $LISTDATA

Формат:

\$LISTDATA(список, позиция)

или

\$LD(список, позиция)

Функция $LISTDATA проверяет указанный элемент списка и возвращает единицу, если элемент в такой позиции существует и имеет значение. Если нет такой позиции или значение элемента не определено, то вернётся ноль (листинг 3.30)

USER>k s x=$LB("Один",,y,"","Пять")
USER>s pos="Позиция "
USER>f i=0:1:6 w pos,i,?15,"$LD(x,",i,")=",$LD(x,i),!
Позиция 0 $LD(x,0)=0
Позиция 1 $LD(x,1)=1
Позиция 2 $LD(x,2)=0
Позиция 3 $LD(x,3)=0
Позиция 4 $LD(x,4)=1
Позиция 5 $LD(x,5)=1
Позиция 6 $LD(x,6)=0

Пример 3.30. Функция $LISTDATA

В $LISTDATA, как в остальных функциях для работы со списками, "список" —это выражение, дающее имя существующего списка. Если такого списка нет, возникает ошибка UNDEFINED.

Функция $LISTFIND

Функция $LISTFIND это наша старая знакомая $FIND приспособленная для работы со списками. Она ищет первое вхождение указанного значения в качестве элемента списка. Совпадение должно быть точным. Формат:

\$LISTFIND(список,  значение, начальная\_позиция)

или

\$LISTFIND(список, значение)

Если элемент найден, возвращается его номер, а если не найден, вернётся 0 (листинг 3.31).

USER>S x=$LB("A","BB","B","C")
USER>W "$LF(x,""A"")=",$LF(x,"A")
$LF(x,"A")=1
USER>W "$LF(x,""B"")=",$LF(x,"B")
$LF(x,"B")=3
USER>W "$LF(x,""BB"")=",$LF(x,"BB")
$LF(x,"BB")=2
USER>W "$LF(x,""D"")=",$LF(x,"D")
$LF(x,"D")=0

Пример 3.31. Функция $LISTFIND

Прикрепление пустого элемента, как к голове, так и к хвосту списка, не изменяет список (листинг 3.32).

USER>ZZDUMP x
0000: 03 01 41 04 01 42 42 03 01 42 03 01 43  ..A..BB..B..C
USER>ZZDUMP ""_x
0000: 03 01 41 04 01 42 42 03 01 42 03 01 43  ..A..BB..B..C
USER>ZZDUMP x_""
0000:  03 01 41 04 01 42 42 03 01 42 03 01 43  ..A..BB..B..C

Пример 3.32. Подключение пустого элемента в голову или хвост не меняет список

Необходимо помнить, что сравнение выполняется после приведения данных к так называемой канонической форме. Для списка созданного командой

S y=$LB("1.0","+2","003","2*2")

все следующие функции возвращают 0 потому, что числа не приводятся к текстовым константам: $LF(y,1.0), $LF(y,+2), $LF(y,003), $LF(y,4). А для списка созданного командой S y=$LB(7,6) обе следующие функции возвращают 1 потому, что вторые аргументы этих функций согласуются с элементами списка: $LF(y,7.000), $LF(y, 006).

Функция $LISTGET

Возвращает элемент списка в указанной позиции или значение по умолчанию, если этот элемент не определён. Формат:

\$LISTGET(список, позиция [, значение\_по\_умолчанию])

или

\$LG(список, позиция [, значение\_по\_умолчанию])

В листинге 3.33 показан пример использования функции $LISTGET и сравнение её с функцией $LIST, которая выдаёт ошибку при появлении неопределённого элемента.

USER>S y=$LB("A",,"B")
USER>F i=1:1:3 W "Элемент ",i,?12,$LG(y,i,"null"),! 
Элемент 1 A 
Элемент 2 null 
Элемент 3 B
USER>F i=1:1:3 W "Элемент ",i,?12,$LI(y,i),! 
Элемент 1 A Элемент 2
F i=1:1:3 W "Элемент ",i,?12,$LI(y,i),!
^
<NULL VALUE>
USER>
Пример 3.33. Сравнение функций $LISTGET и $LIST
Функция $LISTNEXT

Извлекает элементы списка последовательно. Работает очень быстро. Формат:

\$LISTNEXT(список,  указатель, значение)

Локальная переменная "указатель" перед первым вызовом функции должна быть установлена в 0. Тогда список будет анализироваться с начала.

Локальная переменная "значение" не может быть массивом. Инициализировать её не следует. Функция будет помещать в неё значения элементов списка (листинг 3.34).

USER>S y=$LB("A","B","C"), p=0
USER>F i=1:1:10 W "i=",i,?6,"Ф=",$LISTNEXT(y,p,v),?10,"p=",p,?20,"v=",v,!
i=1 Ф=1 p=589827 v=A
i=2 Ф=1 p=589830 v=B
i=3 Ф=1 p=589833 v=C
i=4 Ф=0 p=0 v=C
i=5 Ф=1 p=589827 v=A
i=6 Ф=1 p=589830 v=B
i=7 Ф=1 p=589833 v=C
i=8 Ф=0 p=0 v=C
i=9 Ф=1 p=589827 v=A
i=10 Ф=1 p=589830 v=B
Пример 3.34. Функция $LISTNEXT

Когда $LISTNEXT достигнет конца списка, она вернёт 0, переустановит указатель в 0 и оставит предыдущее извлечённое "значение". Продолжится анализ списка с начала. Элементы вложенных списков не выделяются. Удобно использовать функцию с циклом WHILE. Проверьте командную строку: S p=0 WHILE $LISTNEXT(y,p,v) {W !,v}.

Функция $LISTFROMSTRING

Создаёт список из строки с разделителями. Формат:

\$LISTFROMSTRING(строкa, ограничитель)

или

\$LFS(строкa, ограничитель)

Пример приведен в листинге 3.35.

USER>S string="A^B^C", list=$LFS(string,"^")
USER>ZZDUMP list,string
0000: 03 01 41 03 01 42 03 01 43 ..A..B..C
0000: 41 5E 42 5E 43 A^B^C
USER>
Пример 3.35. Преобразование строки в список
Функция $LISTTOSTRING

Преобразует список в строку с разделителями. Если разделитель не указан, выбирается запятая. Отсутствие флага или нулевое его значение определяет появление ошибки <NULL VALUE>. Формат:

\$LISTTOSTRING(список[,огрaничитeль][,флaг])

или

\$LTS(список[,огрaничитeль][,флaг])

Обратите внимание на то, что функция $LTS не проверяет наличие в исходном списке разделителя, который будет использован в преобразовании. Поэтому в последнем примере в листинге 3.36 ошибочно получена строка из трех элементов с разделителем "пробел".

USER>S x=$LB("A","B"), y=$LTS(x,"разделитель")
USER>ZZDUMP y
0000: 0041 0440 0430 0437 0434 0435 043B 0438 Aраздели
0008: 0442 0435 043B 044C 0042 тельB
USER>S z=$LB("A B","C")
USER>S y=$LTS(z," ")
USER>ZZDUMP y
0000: 41 20 42 20 43 A B C
USER>
Пример 3.36. Функция $LISTTOSTRING