8.1. Символьный и строковый типы (Char и String)

Язык Турбо Паскаль поддерживает стандартный символьный тип Char и, кроме того, динамические строки, описываемые типом String или String[ n ].

Значение типа Char — это непустой символ из алфавита ПЭВМ, заключенный в одинарные кавычки, например, ' ', 'А', '7' и т.п. Кроме этой, классической, формы записи символов, Турбо Паскаль вводит еще две. Одна из них — представление символа его кодом ASCII с помощью специального префикса # :

#97 = Chr(97) = 'а' (символ 'а'),

#0 = Chr(0) (нулевой символ),

#32 = Chr(32) = ' ' (пробел).

Символы, имеющие коды от 1 до 31 (управляющие), могут быть представлены их «клавиатурными» обозначениями — значком «^» и буквой алфавита с тем же номером (для диапазона кодов 1...26) или служебным знаком (для диапазона 27...31):

^A = #1 = Chr(1) – код 1,

^B = #2 = Chr( 2) – код 2,

...

^ = #26 = Chr(26) – код 26.

^[ = #27 = Chr(27) – код 27,

^_ = #31 = Chr(31) – код 31,

в том числе ^G — звонок (7), ^I — TAB (код 9), ^J — LF (код 10), ^M — CR (код 13) и т.п.

Рассмотрим более подробно строковый тип. Максимальная длина строки составляет 255 символов. Строки называются динамическими, поскольку могут иметь различные длины в пределах объявленных границ. Например, введя переменные

- 149 -

VAR

S32 : String[ 32];

S255 : String[255];

мы можем хранить в S32 строчные значения, длиной не более 32 символов, а в S255 — не более 255. Описание String без указания длины отводит место, как и описание String[255].

Тип String без длины является базовым строковым типом, и он совместим со всеми производными строковыми типами.

При попытке записать в переменную строку длиннее, чем объявлено в описании, «лишняя» часть будет отсечена. Можно присваивать пустые строки. Их обозначение (две одинарные кавычки подряд).

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

'abcde-абвгд'

'123 ? 321'

' '

''''

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

При необходимости включать в строку управляющие коды можно пользоваться «клавиатурными» обозначениями. В этом случае значение состоит из как бы склеенных кусков:

^G 'После сигнала нажмите '^J' клавишу пробела '^M^J

В такой записи не должно быть пробелов вне кавычек.

Более общим способом включения символов (любых) в строку является их запись по ASCII-коду через префикс #. Механизм включения в строку остался тем же:

'Номер п/п'#179' Ф.И.О. '#179' Должность '#179

#7#32#179#32#32#179 (то же, что ^G'| |')

Строки различных длин совместимы между собой в операторах присваивания и сравнения, но «капризно» ведут себя при передаче в процедуры и функции. Если тип объявленного строкового параметра в процедуре или функции является производным от типа String, то при вызове процедуры или обращении к функции тип передаваемой строки должен совпадать с типом параметра. Имеется ключ компилятора $V, управляющий режимом проверки типов соответствующих формальных и фактических параметров-строк. В режиме {$V+} такая проверка проводится и при несогласованности типов выдает сообщение об ошибке. Но если отключить ее, то можно подстав-

- 150 -

лять в вызовы процедур и функций фактические параметры, имеющие другой, отличающийся от формальных, производный от String тип. При этом во время работы программы могут возникнуть наложения значений одних строк на другие и путаница в значениях. Для того чтобы писать процедуры, работающие со строками любых строковых типов, нужно описывать формальные параметры исключительно типом String и компилировать программу в режиме {$V-}. Это даст процедурам и функциям гибкость и безопасность данных.

При описании параметров производным строковым типом нельзя конструировать типы в описаниях процедур (функций). Например, объявление

FUNCTION Xstr( S : String[32] ) : String[6];

совершенно неправильно. Корректно оно выглядит таким образом:

TYPE

String32 = String[32];

String6 = String[6];

...

FUNCTION Xstr( S : String32 ) : String6;

Можно, вообще говоря, рассматривать строки как массив символов. Турбо Паскаль разрешает такую трактовку, и любой символ в строке можно изъять по его номеру (рис. 8.1).

| VAR

| i : Byte;

| S : String[20];

| BEGIN

| S := 'Массив символов.....';

| for i:=1 to 20 do WriteLn( S[i]);

| ReadLn {Пауза до нажатия клавиши ввода}

| END.

Рис. 8.1

Отдельный символ совместим по типу со значением Char. Иными словами, можно совершать присваивания, представленные на рис. 8.2.

Каждая строка всегда «знает», сколько символов в ней содержится в данный момент. Символ S[0] содержит код, равный числу символов в значении S, т.е. длина строки S всегда равна Ord(S[0]). Эту особенность надо помнить при заполнении длинных строк

- 151 -

| VAR

| ch : Char;

| st : String;

| BEGIN

| st := 'Hello';

| ch := st[1]; { ch = H }

| st[2] := 'E'; { st = 'HEllo' }

| ch := 'x';

| st := ch; { st = 'x' }

| END.

Рис. 8.2

одинаковым символом. В самом деле, не писать же в программе S := '... и далее, например, 80 пробелов в кавычках! Проблему заполнения решает встроенная процедура FillChar:

VAR St : String;

...

FillChar( St[1], 80, ' ' );

St[0] := Chr(80); {!!!}

Здесь St[1] указывает, что надо заполнять строку с первого элемента. Можно начать заполнение и с любого другого элемента. В данном случае строка заполняется с начала 80 пробелами (' ' или #32). Очень важен последующий оператор: так как строка «закрашивается» принудительно, ей надо сообщить, сколько символов в ней стало, т.е. записать в St[0] символ с кодом, равным ее новой длине.

Первоначально строка содержит «мусор», и всегда рекомендуется перед использованием инициализировать строки пустыми значениями или чем-либо еще.

При использовании процедуры FillChar всегда есть риск «выскочить» за пределы, отводимые данной строке, и заполнять символом рабочую память данных. Компилятор таких ошибок не замечает, и вся ответственность ложится на программиста.