Листинг 9.1. Вывод строки в перевернутом виде и по отдельным словам

Обработка строк текста

9.1. Типы данных CHAR и STRING

В Turbo Pascal имеется два типа данных для работы с текстами: О char — литерный или символьный тип;

П String — СТрОКОВЫЙ ТИП ИЛИ ПрОСТО СТрОКЭ.

Символьный тип

Значением переменных символьного типа char является один символ. Каж­дому символу соответствует код символа — целое число в диапазоне от О до 255. Из этого следует, что символьный тип является порядковым. В не­которых языках программирования (С, Java) тип char относят к целым ти­пам, что разумно, т. к. в памяти компьютера нет символов —- есть только их числовые коды. Это первый момент, который важно уяснить — все дейст­вия по обработке символов, в конечном счете, сводятся к действиям над целыми числами, расположенными строго по порядку.

Над данными символьного типа определены следующие операции отноше­ния: =, о, <, >, <=, >=, вырабатывающие результат логического типа.

Обратите внимание — при выполнении всех операций сравнения коды сим­волов сравниваются как обычные целые числа. При этом получается, что любая заглавная буква всегда меньше соответствующей ей строчной, т. к. в кодовой таблице сначала располагаются все заглавные буквы, а затем строчные (см. приложение 2). Точно так же любая буква латинского алфави­та всегда меньше любой буквы русского алфавита, даже если их изображе­ния на экране практически неразличимы (А латинская, А русская).


Для данных символьного типа определены следующие стандартные функции: П chr(x) — возвращает значение символа по его коду;

П ordfch) —- возвращает код заданного символа ch; П pred(ch) — возвращает предыдущий символ; О succ(ch) — возвращает следующий символ;

П upcase(ch) — преобразует строчную букву в заглавную. Обрабатывает буквы только латинского алфавита.

Например:

П ord('A')=65

О спг(128)='Б'

О pred('B'}='A>

CJ succ(T') = 'flr


upcase('n')='N'


 


Строковый тип

Строка — это последовательность символов. Максимальное количество символов в строке (длина строки) может изменяться от I до 255. Пере­менную строкового типа можно определить через описание типа в разделе определения типов или непосредственно в разделе объявления переменных.

Type

ИмяТипа = string[максимальная длина строки];

Var


Идентификатор,


ИмяТипа;


Строковую переменную можно задать и без предварительного объявления

типа:

var


Идентификатор,


string[MaKc. длина строки];


ИЛИ var

Идентификатор, ... : string;

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

Строковые данные могут использоваться в программе также в качестве кон­стант.

Например:

const stradres = 'ул. Мира, 35'; { строковая константа }

sadr: string[12] = ' ул. Мира' { типизированная константа, ее можно использовать как обычную переменную ]


type str!25 = string [125];

var strl :str!25; { описание с заданием типа }

stl : string; { по умолчанию длина строки = 255 |

st2,st3 : string[50];

strnazv : string[280]; { ошибка, длина больше 255 }

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

Строка в языке Turbo Pascal трактуется как массив символов. Для строки из п символов в памяти отводится п+l байт; п байтов — для хранения симво­лов строки, а один дополнительный байт — для значения текущей длины строки. Этот дополнительный байт имеет номер 0, соответственно первый символ строки имеет номер 1, второй — номер 2 и т. д.

Например, переменная sadr типа string [12] хранится в памяти таким обра­зом (табл. 9.1).

Таблица 9.1. Распределение памяти для хранения переменной

Всего строка занимает 13 байтов, из них последние 4 байта оказались неза­нятыми, т. к. реальная длина строки составляет 8 символов, это значение и хранится в нулевом байте строки. Незанятые байты составляют "хвост" строки, в котором может быть любой "мусор", однако программа не будет обращаться к "хвосту", т. к. реальный размер строки ей известен.

( Замечание ~^)

Из этого представления понятно и ограничение на максимальную длину стро­ки — 255 символов, т. к. для хранения длины строки отведен всего один байт (максимальное двоичное число, которое можно уместить в один байт, — восемь подряд идущих единиц, что соответствует десятичному числу 255).

К любому символу в строке можно обратиться как к элементу одномерного массива

array [0 .. n] of char

по номеру (индексу) данного символа в строке. Индекс определяется выра­жением целочисленного типа, которое записывается в квадратных скобках, как для массива.

Обратите внимание — в отличие от массивов переменные строкового типа могут участвовать целиком в операторах ввода/вывода.

Например,readln(stl); writeln(st2> ;


При вводе строки количество символов в ней определяется автоматически, при этом автоматически заполняется нулевой байт. Для получения длины строки имеется функция length, которая возвращает значение нулевого бай­та строки (см. разд. 9.3).

Разумеется, никто не запрещает вводить и выводить строки по отдельным символам, как массивы, используя любой из операторов цикла. Однако та­кая необходимость возникает редко. Посимвольныйввод строки стоит реа­лизовать только в том случае, если необходимо совместить ввод и какую-то обработку символов (см. листинг 9.9). Посимвольный вывод также удобно использовать для какой-либо нестандартной формы вывода.

Приведем пример, в котором исходная строка вводится целиком, а выво­дится по отдельным символам (листинг 9.1). В данном случае программа сначала выводит строку в "перевернутом" виде (например, вводим РОЗА, а выводится АЗОР). Затем та же строка выводится в прямом виде, но каждое слово с новой строки. Считаем, что слова разделены пробелами, причем не обязательно одиночными. Обратите внимание на условие, которое проверя­ет начало нового слова. Разбивка строки на слова — это типовое действие со строками.

Листинг 9.1. Вывод строки в перевернутом виде и по отдельным словам

var s:string;

i: integer; begin

write('Введите строку');readln(s);

for i:=length(s) ciownto 1 do write (s[i]) ;

for i:=l to length(s)-l do

if (s[i]) = ' ') and (s[i+l]<>' ') then writeln

else write(s[i]); readln; end

Еще один пример, в котором выполняется обработка строки как массива символов. Данная программа (листинг 9.2) проверяет правильность расста­новки скобок в выражении (можно считать ее маленькой частичкой компи­лятора, т. к. перед преобразованием любого выражения в исполнимый код всегда проверяется правильность расстановки скобок). В последней главе (см. листинг 13.3) приводится пример программы для решения аналогичной задачи в более сложной постановке.

;•.......... -................................. :................ •;•""............. ;•'-'................ '•........... """'.'........................................................ ......,,,.,..„..................................................... j

^Листинг 9.2. Проверка баланса скобок в скобочном выражении

var k,i:integer;{ k — номер символа, i — для определения баланса скобок )

s:string;


begin

writeln('Введите скобочное выражение:']; readln(s);

k:=l; i:=0;

while (k<=length(s)) and (i>=0) do

begin

{ если i<0, то выражение неправильно: ')' предшествует '(' }

if s[k]-'(' then i:=i+l;

if s[k]='} ' then i:=i-l,-

k:=k+l; ehd;

if i=0 then writeln('Скобки расставлены правильно.')
{ если i>0, то не хватает закрывающихся скобок }
else writeln{'Скобки расставлены неправильно'};
readln * *

end.

Операции над строками

Над строковыми данными допустимы операция сцепления (конкатенации) и операции отношения. Используя строковые переменные, константы, функ­ции и операцию сцепления, можно строить строковые выражения.

9.2.1. Операция сцепления (+)

Применяется для соединения нескольких строк.

Например:

Выражение: 'Turbo' + ' Pascal' + '7.0'

Результат: Turbo Pascal 7.0'

Обратите внимание— операция конкатенации, в отличие от операции сло­жения в математике, не подчиняется известному правилу: "от перестановки слагаемых сумма не меняется", в этой операции каждый из операндов дол­жен находиться строго на своем месте.

Замечание j

Для того чтобы предоставить программистам возможность использовать один и тот же знак операции "+" для работы с данными разных типов, разработчикам компилятора Turbo Pascal пришлось изрядно потрудиться. При обработке тек­ста программы компилятор анализирует типы операндов и формирует для опе­рации "+" различные машинные коды для целых, вещественных и строковых типов. Такое явление в программировании принято называть полиморфизмом. Запомните этот термин— он пригодится на следующих этапах постижения ис­кусства программирования.


Для присваивания строковой переменной результата строкового выражения используется оператор присваивания.

Например: strl := Труппа учащихся'; str2 := strl + ' школы-лицея';

Приведем пример использования операции конкатенации (листинг 9.3). До­пустим, необходимо дополнить исходную строку справа символом '*' до заданной длины. Такое действие иногда выполняют при формировании важных денежных документов, чтобы нельзя было ничего вписать в пустые места.

[Листинг 9.3.Дополнение строкизвездочками '

!...-.. ;.-...:........ .-•. ;. !.'..:.:.... :... :..:.'..... .-...

var s:string;

procedure dopstr(var s:string; n:integer);.

begin

while length(s)<n do s:=s+'*'; end; begin

writeln('Введите строку:');readln(s};

dopstr(s,80); writeln(s); readln end.

Если в тексте процедуры поменять s+' *' на ' *' +s, то строка будет дополнять­ся пробелами не справа, а слева. Было бы разумно добавить в процедуру dopstr еще два параметра: символ, которым следует дополнять строку, и спо­соб дополнения строки (слева, справа, слева и справа равномерно).

Обратите внимание — все строковые типы считаются совместимыми по при­сваиванию, т. е. можно присваивать друг другу значения строк различного размера. К сожалению, размеры строк при этом не контролируются. Если значение переменной после выполнения оператора присваивания превыша­ет по длине максимальный размер, указанный при объявлении, все лишние символы справа отбрасываются, что важно!

Например:

const si:string[13]='Turbo Pascal';

var s2:string[5];

begin

s2:=sl; . . .

Переменная s2 при этом получит значение 'Turbo', т. к. присваиваемое значение 'Turbo Pascal1 не разместить в тех пяти байтах, которые отведены под символы переменной s2. Фактически операция присваивания будет вы­полнена неверно, хотя никакого сообщения об ошибке не будет выдано.


Замечание

При отладке задач со строками целесообразно помещать в программу директи­ву {$R+}, которая проверяет выход за пределы диапазона изменения индекса. Однако в приведенном выше случае и это не поможет. Остается надеяться только на свою внимательность!

При передаче строки в качестве параметра процедуры совместимость строк разной длины возможна только при отключенной директиве компилятора строгой проверки типов строк ($v-). По умолчанию она включена — {$v+}, поэтому строковые параметры (и формальный, и фактический) должны бьЕТЬ одного размера. Рекомендуем использовать данную директиву, но с осторожностью, т. к. есть риск потери символов справа, если не хватит места в той строке, которая передается в качестве фактического параметра.

Операции отношения

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

Например:

Выражение: Результат:

'Иванов '='Иванов И.И. ' false

'Иванов'<'Иванов И.И.' true

'program1 >' PROGRAM1 true

Операции отношения над строковыми данными широко используются при обработке массивов строк. Наиболее важными действиями над такими мас­сивами являются:

П сортировка массива в лексикографическом порядке; П быстрый поиск данных в отсортированном массиве; П слияние двух отсортированных массивов.

При решении данных задач используются те же самые алгоритмы, что и для числовых массивов (см. разд. 4.2.3), достаточно только заменить тип эле­ментов массива на тип string. Например, при организации электронного телефонного справочника огромный массив абонентов обычно сортируется, и за счет этого можно в считанные секунды найти телефон абонента по его фамилии.


В данном разделе приведем в качестве примера алгоритм слияния двух от­сортированных массивов строк (листинг 9.4). Допустим, происходит слия­ние двух предприятий и требуется объединить списки сотрудников таким образом, чтобы не нарушить алфавитного (лексикографического) порядка.

I Листинг 9.4. Слияние двух отсортированных массивов строк,

const m=4; n=3;

a: array [1. .m] of string=( 'Абрамов1 , 'Григорьев' , 'Иванов' , 'Сидоров' b:array [l..n] of string= ( 'Васильев' , 'Петров ', 'Яковлев' );

var crarray [1. .m+n] of string;

k, i, j : integer; (индексы трех массивов}

begin

э .

for k:=l to m+n do

{ если индекс i вышел за пределы массива а или b[j]<a[i] }

if (i>ra) or (b[j]<a[ij) then

begin

c[k]:=b[j]; j:=j+l; end else begin

c[k] :=a[i] ; i:=i+l; end;

writelnf 'Массив с:');

for k:=l to m+n do write (c[k],' ' ) ; readln end.