Глава 18. Строки с завершающим нулем

             В Borland Pascal поддерживается класс символьных строк,  ко-

        торые называются строками, завершающимися нулем. Благодаря расши-

        ренному синтаксису Borland Pascal и модулю Strings ваши программы

        (как для DOS,  так и для Windows) могут использовать строки с за-

        вершающим нулем путем задания в операторе uses модуля Strings.

Что такое строка с завершающим нулем?

             В Borland  Pascal строки обычного типа (String) хранятся как

        байт длины,  за которым следует последовательность символов. Мак-

        симальная длина строки в Паскале равна 255 символам.  Таким обра-

        зом, строка Паскаля занимает от 1 до 256 байт памяти.

 

             Строки с завершающим нулем не содержат байта  длины.  Вместо

        этого  они  состоят из последовательности ненулевых символов,  за

        которыми следует символ NULL (#0).  Никаких ограничений на  длину

        строк с завершающим нулем не накладывается, но 16-разрядная архи-

        тектура DOS и Windows ограничивает их размер 65535 символами.

Функции модуля Strings

             Borland Pascal  не имеет встроенных подпрограмм,  предназна-

        ченных специально для работы со строками с завершающим нулем. Эти

        функции вы  можете  найти в модуле Strings.  Среди них вы найдете

        функцию StrPCopy,  которую  можно  использовать  для  копирования

        строки Паскаля в строку с завершающим нулем, и StrPos, используе-

        мую для преобразования строки с завершающим нулем в строку Паска-

        ля. Приведем краткое описание каждой функции:

Функции модуля Strings

        і     Функция  і       Описание                                 і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrCat     і Добавляет исходную строку к концу целевой стро-і

        і              і ки и возвращает указатель на целевую строку.   і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrComp    і Сравнивает  две  строки  S1  и   S2. Возвращаеті

        і              і значение < 0, если S1 < S2, равное 0, если S1 =і

        і              і S2 и > 0, если S1 > S2.                        і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrCopy    і Копирует исходную строку  в  целевую  строку  иі

        і              і возвращает указатель на целевую строку.        і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrECopy   і Копирует исходную строку  в  целевую  строку  иі

        і              і возвращает указатель на конец целевой строки.  і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrIComp   і Сравнивает  две  строки  без  различия регистраі

        і              і символов.                                      і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrLCat    і Присоединяет  исходную  строку  к концу целевойі

        і              і строки. При этом обеспечивается,  что длина ре-і

        і              і зультирующей строки не превышает заданного мак-і

        і              і симума.  Возвращается указатель  на  строку-ре-і

        і              і зультат.                                       і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrLComp   і Сравнивает две строки с  заданной  максимальнойі

        і              і длиной.                                        і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrLCopy   і Копирует заданное число  символов  из  исходнойі

        і              і строки в  целевую строку и возвращает указательі

        і              і на целевую строку.                             і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrEnd     і Возвращает  указатель  на конец строки, то естьі

        і              і указатель на завершающий строку нулевой символ.і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrDispose і Уничтожает ранее выделенную строку.            і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrLen     і Возвращает длину строки.                       і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrLIComp  і Сравнивает две строки  с  заданной максимальнойі

        і              і длиной без различия регистра символов.         і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrLower   і Преобразует строку в нижний регистр и возвраща-і

        і              і ет указатель на нее.                           і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrMove    і Перемещает  блок  символов из исходной строки ві

        і              і целевую строку и возвращает указатель на  целе-і

        і              і вую строку. Два блока могут перекрываться.     і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrNew     і Выделяет  для  строки память в динамически рас-і

        і              і пределяемой области.                           і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrPas     і Преобразует строку с завершающим нулем в строкуі

        і              і Паскаля.                                       і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrPCopy   і Копирует  строку Паскаля в строку с завершающимі

        і              і нулем и возвращает указатель на строку с завер-і

        і              і шающим нулем.                                  і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrPos     і Возвращает указатель на первое вхождение задан-і

        і              і ной подстроки в строке, или nil, если подстрокаі

        і              і в строке не содержится.                        і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrRScan   і Возвращает  указатель  на  последнее  вхождениеі

        і              і указанного символа в строку, или nil, если сим-і

        і              і вол в строке отсутствует.                      і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrScan    і Возвращает  указатель  на первое вхождение ука-і

        і              і занного символа в строку,  или nil, если символі

        і              і в строке отсутствует.                          і

        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

        і   StrUpper   і Преобразует  строку в верхний регистр и возвра-і

        і              і щает указатель на нее.                         і

        АДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДЩ

Использование строк с завершающим нулем

             Строки с завершающим нулем хранятся в виде символьных масси-

        вов с нулевой базой (начинающихся с 0) с индексом целого типа, то

        есть в виде массива:

 

             array[0..X] of Char;

 

        где X - положительное ненулевое целое число.  Такие массивы назы-

        ваются символьными массивами с нулевой базой.  Приведем некоторые

        примеры описаний символьных массивов с нулевой базой, которые мо-

        гут использоваться для хранения завершающихся нулем строк.

 

             type

                TIdentifier = array[0..15] of Char;

                TFileName   = array[0..79] of Char;

                TMemoText   = array[0..1023] of Char;

             Более всего  строки Паскаля и строки с завершающим нулем от-

        личаются интенсивностью использования указателей.  Borland Pascal

        выполняет  операции  с этими указателями,  используя набор правил

        расширенного синтаксиса.  Кроме того,  в Borland  Pascal  имеется

        встроенный  тип  PChar,  который  представляет собой указатель на

        строку с завершающим нулем. В модуле System тип PChar определяет-

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

 

             type PChar = ^Char;

 

             Правилами расширенного синтаксиса управляет директива компи-

        лятора $X. В состоянии {$X+} (по умолчанию) расширенный синтаксис

        разрешен. Правила расширенного синтаксиса описываются в следующих

        разделах.

Символьные указатели и строковые литералы

             При разрешении  расширенного  синтаксиса  строковый  литерал

        совместим по присваиванию с типом PChar.  Это означает, что пере-

        менной типа PChar можно присвоить строковый литерал. Например:

 

             var

                P: PChar;

                .

                .

             begin

                P := 'Привет...';

             end;

 

             В результате  такого присваивания указатель указывает на об-

        ласть памяти,  содержащую строку с завершающим нулем,  являющуюся

        копией строкового литерала. Компилятор записывает строковые лите-

        ралы в сегмент данных, аналогично описанию "скрытых" типизирован-

        ных констант:

 

             const

                TempString: array[0..14] of Char = 'Привет...'#0;

             var

                P: PChar;

                .

                .

             begin

                P := @TempString;

             end;

 

             Когда соответствующие  формальные  параметры имеют тип Char,

        строковые литералы вы можете использовать как  фактические  пара-

        метры при вызовах процедур и функций. Например, если имеется про-

        цедура с описанием:

 

             procedure PrintStr(Str: PChar);

 

        то допустимы следующие вызовы процедуры:

 

             procedure PrintStr('Строка для проверки');

             PrintStr(#10#13);

 

             Аналогично тому, как это происходит при присваивании, компи-

        лятор генерирует строку с завершающим нулем, представляющую собой

        копию литеральной строки в сегменте данных,  и передает указатель

        на эту область памяти в параметре Str процедуры PrintStr.

 

             Наконец, типизированная константа типа PChar может инициали-

        зироваться строковой константой. Это справедливо также для струк-

        турных типов,  таких как массивы PChar и записи,  а также объекты

        PChar.

             const

                Message: PChar = 'Program terminated';

                Prompt: PChar = 'Enter values: ';

                Digits; array [0..9] of PChar = {

                 'Zero', 'One', 'Two', 'Three', 'Four', 'Five',

                 'Six', 'Seven', Eight', 'Nine'};

 

             Строковая выражение-константа всегда вычисляется как  строка

        Паскаля, даже  если она инициализируется как типизированная конс-

        танта типа PChar.  Таким образом,  строковое  выражение-константа

        всегда ограничено длиной в 255 символов.

Символьные указатели и символьные массивы

             Если вы с помощью директивы $X разрешаете  расширенный  син-

        таксис,  то  символьный  массив с нулевой базой совместим с типом

        PChar.  Это означает,  что там,  где предполагается использование

        типа PChar,  может использоваться символьный массив с нулевой ба-

        зой.  Когда символьный массив используется вместо значения PChar,

        компилятор  преобразует  символьный массив в указатель-константу,

        значение которой соответствует адресу первого  элемента  массива.

        Например:

 

             var

                A: array[0..63] of Char;

                P: PChar;

                .

                .

                .

             begin

                P := A;

                PrintStr(A);

                PrintStr(P);

             end;

 

             Благодаря оператору присваивания P теперь указывает на  пер-

        вый элемент массива A, поэтому PrintStr вызывается дважды с одним

        и тем же значением.

 

             Вы можете инициализировать типизованную  константу,  имеющую

        тип символьного массива с нулевой базой, с помощью строкового ли-

        терала,  имеющего меньшую длину,  чем размер массива.  Оставшиеся

        символы устанавливаются в значение NULL (#0),  и массив будет со-

        держать строку с завершающим нулем.

 

             type

                TFileName = array[0..79] of Char;

             const

                FileNameBuf: TfileName = 'TEST.PAS';

                FileNamePtr: PCahr = FileNameBuf;

Индексирование символьного указателя

             Так как  символьный  массив с нулевой базой совместим с сим-

        вольным указателем, символьный указатель можно индексировать ана-

        логично символьному массиву с нулевой базой.

 

             var

                A: array[0..63] of Char;

                P: PChar;

                Ch: Char;

                .

                .

                .

             begin

                P := A;

                Ch := A[5];

                Ch := P[5];

             end;

 

             Оба последних присваивания присваивают Ch значение, содержа-

        щееся в шестом символе-элементе A.

 

             При индексировании символьного указателя индекс задает безз-

        наковое смещение, которое добавляется к указателю перед его разы-

        менованием.  Таким образом, P[0] эквивалентно P^ и задает символ,

        на который указывает P. P[1] задает символ справа от того, на ко-

        торый указывает P,  P[2] задает следующий символ и т.д. Для целей

        индексирования  PChar  ведет  себя таким образом,  как если бы он

        описывался:

 

             type

               TCharArray = array[0..65535] of Char;

               Pchar = ^TCharArray;

 

             Компилятор при индексировании символьного указателя  не  вы-

        полняет проверку диапазона, так как у него нет информации о типе,

        по которой можно определить максимальную длину строки с завершаю-

        щим нулем, на которую указывает символьный указатель.

 

             Показанная ниже  функция StrUpper иллюстрирует использование

        символьного указателя для преобразования строки с завершающим ну-

        лем в верхний регистр.

 

             function StrUpper(Srt: Pchar): Pchar;

             var

                I: Word;

             begin

                I := 0;

                while Str[I] <> #0 do

                begin

                  Str[I] := UpCase(Str[I]);

                  Inc(I);

                end;

                StrUpper := Str;

             end;

 

             Обратите внимание,  что StrUppper - это функция, а не проце-

        дура,  и что она всегда возвращает значение, которое передавалось

        ей в качестве параметра.  Так как расширенный синтаксис допускает

        игнорирование  результата  функции,  StrUpper может интерпретиро-

        ваться, как процедура:

 

             StrUpper(A);

             PrintStr(A);

 

             Однако, StrUpper всегда возвращает передаваемое ей значение,

        приведенные выше операторы можно скомбинировать в один:

 

             PrintStr(StrUpper(A));

 

             Вложенные вызовы функций работы со  строками  с  завершающим

        нулем могут оказаться очень удобными, когда вы хотите указать оп-

        ределенную  взаимосвязь  между  последовательными  операциями  со

        строками.

Операции с символьными указателями

             Расширенный синтаксис Borland Pascal позволяет  использовать

        для работы с символьными указателями отдельные операции. Для уве-

        личения или уменьшения смещения в значении  указателя  можно  ис-

        пользовать операции плюс (+) и минус (-). Операцию минус (-) мож-

        но использовать для  вычисления  расстояния  (разности  смещений)

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

        представляют собой значения тип PChar,  а I - значение типа Word.

        Тогда допустимы следующие конструкции:

 

             P + I     I прибавляется к смещению P

             I + P     I прибавляется к смещению P

             P - I     I вычитается из смещения P

             P - Q     Смещение Q вычитается из смещения P

 

             В операциях P + I и I + P I прибавляется к адресу,  задавае-

        мому  P.  При  этом получается указатель,  который указывает на I

        символов после P.  В операции P - I I вычитается из адреса, зада-

        ваемого P,  и получается указатель,  указывающий на I символов до

        P.

 

             Операция P  - Q вычисляет расстояние между Q (младший адрес)

        и P (старший адрес).  При этом возвращается результат типа  Word,

        показывающий число символов между Q и P. Эта операция предполага-

        ет,  что P и Q указывают на один и тот же массив  символов.  Если

        эти два указателя указывают на разные символьные массивы,  то ре-

        зультат непредсказуем.

             Стандартный синтаксис Borland Pascal позволяет при сравнении

        указателей определять только их равенство или неравенство. Расши-

        ренный  синтаксис  (разрешенный  по  директиве компилятора {$X+})

        позволяет применять операции <, >, <= и <= к значениям PChar. За-

        метим,  однако,  что при таких проверках предполагается,  что два

        сравниваемых указателя указывают на один и тот же  массив  симво-

        лов. По этой причине сравниваются только смещения указателей. Ес-

        ли два указателя указывают на различные  символьные  массивы,  то

        результат не определен.

 

             var

                A, B: array[0..79] of Char;

                P, Q: PChar;

             begin

                P := A;                { P указывает на A[0] }

                Q := A + 5;            { Q указывает на A[5] }

                if P < Q then ...;     { допустимая проверка,

                                         результат - True }

                Q := B;                { Q указывает на B[0] }

                if P < Q then ...;     { результат не определен }

             end;

 

             Подробнее об операциях с PChar рассказывается в Главе 6.

Строки с завершающим нулем и стандартные процедуры

             Расширенный синтаксис Borland Pascal позволяет  применять  к

        символьным  массивам  с нулевой базой стандартные процедуры Read,

        ReadLn и Val, а к символьным массива с нулевой базой и символьным

        указателям - стандартные процедуры Write,  WriteLn, Val, Assign и

        Rename. Более подробные описания этих процедур можно найти в Гла-

        ве 1  ("Справочник по библиотеке") "Справочного руководства прог-

        раммиста".

Пример использования функций с завершающим нулем

             Приведем пример исходного кода,  показывающий, как можно ис-

        пользовать некоторые функции обработки строк. Этот пример исполь-

        зован при разработке функции FileSplit в модуле WinDos.

 

             { максимальные размеры компонентов имени файла }

 

             const

               fsPathName    = 79;            { имя маршрута }

               fsDirectory   = 67;            { имя каталога }

               fsFileName    = 8;             { имя файла }

               fsExtension   = 4;             { расширение имени файла }

 

             { флаги, возвращаемые FileSplit }

 

             const

                    fcWildcards   = $0008     { трафаретные символы }

                    fcDirectory   = $0004     { имя каталога }

                    fcFileName    = $0002     { имя файла }

                    fcExtension   = $0001     { расширение имени файла }

 

           { FileSplit разбивает имя файла,  заданное маршрутом,  на три }

           { компонента.  Dir  принимает  значение  диска  и  каталога с }

           { предшествующей и завершающей обратной  косой  чертой,  Name }

           { принимает  значение  имени  файла,  а  Ext  -  расширения с }

           { предшествующей  точкой.  Если  компонент   строки-параметра }

           { равен   NIL,   то   соответствующая   часть   маршрута   не }

           { записывается.  Если маршрут не содержит данного компонента, }

           { то    возвращаемая    строка   компонента   будет   пустой. }

           { Максимальные длины строк,  возвращаемых в Dir,  Name и Ext, }

           { определяются   битовыми  масками  fsDirectory,  fsFileName, }

           { fsExtension.  Возвращаемое  значение   представляет   собой }

           { комбинацию   битовых   масок   fсDirectory,   fсFileName  и }

           { fсExtension,  показывающую, какие компоненты присутствуют в }

           { маршруте.   Если  имя  и  расширение  содержат  трафаретные }

           { символы (* и ?), то в возвращаемом значении устанавливается }

           { флаг fcWildcards. }

 

             function FileSplit(Path, Dir, Name, Ext: PChar): Word;

             var

                DirLen, NameLEn, Flags: Word;

                NamePtr, ExtPtr: PChar;

             begin

                NamePtr := StrRScan(Path, '/');

                if NamePtr = nil then NamePtr := StrRScan(Path, ':');

                if NamePtr = nil then NamePtr := Path else Inc(NamePtr);

                ExtPtr := StrScan(NamePtr, '.');

                if ExtPtr = nil then ExtPtr := StrEnd(NamePtr);

                DirLen := NamePtr - Path;

                if DirLen > fsDirectory then DirLen := fsDirectory;

                NameLen := ExtPtr - NamePtr;

                if NameLen > fsFilename then NameLen := fsFileName;

                Flags := 0;

                if (StrScan(NamePtr, '?') <> nil) or

                   (StrScan(NamePtr, '*') <> nil) then

                   Falgs := fcWildcards;

                if DirLen <> 0 then Flags := Flags or fcDirectory;

                if NameLen <> 0 then Flags := Flags or fcFilename;

                if ExtPtr[0] <> #0 then Flags := Flags or fcExtension;

                if Dir <> nil then StrLCopy(Dir, Path, DirLen);

                if Name <> nil then StrLCopy(Name, NamePtr, NameLen);

                if Ext <> nil then StrLCopy(Ext, ExtPtr, fsExtension);

                FileSplit := Flags:

             end;