Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Самоучитель игры на Паскале. АВС и немного Турбо
.pdf
Глава 6.
Строки
Просто строка
Знаменательное событие. Было у нас два типа данных – целые и дробные,
integer и real, будет три. По-русски – строка, по-программистски – string.
Как там, в сказке – было у старика три сына, двое умных, третий дурак.
Короче, не совсем похожий на предыдущие типы. По сути,
замаскированный массив.
На самом деле, массив он и есть, и даже вовсе не замаскированный.
Теперь как всегда – объявить переменную типа строка, присвоить чтонибудь переменной типа строка, вывести переменную типа строка.
Поехали.
var
s : string;
Переменной цикла принято давать имя I, массив часто называют A, у
строки обычная кличка – S. Набрав слово string без ошибок, обнаружим,
что оно выделено полужирным шрифтом, то есть тоже относится к
разряду волшебных зарезервированных слов, в отличие от integer и re
му – тайна.
Поче
Присваиваем:
S:= ‘woodpecker’;
Напоминаем для озабоченных – если хочется, чтобы внутри строки была
кавычка, надо вместо одной поставить две, вот так: ‘wood’’pecker’.
Максимальная длина строки – 255 символов.
Для любознательных. Строку можно объявить вот так, например: s : string[30]; Максимальная длина такой строки не
255, а только 30 символов. Зачем это надо? Если очень хочется сэкономить оперативную память. Под просто string
выделяется 256 байтов, а под string[30] будет выделен 31 байт. Почему не тридцать?
Для пытливых умов. Есть ещё нулевой байт, s[0]. В нем хранится фактическая длина строки. То есть по
s:=’abcd’; значение нулево
Для любителей совать пальцы в розетку. Во времена Паскаля некоторые, чтобы узнать длину строки, вместо Length(s)
предпочитали писать Ord(s[0]). Ещё чаше, при необходимости оставить от строки, например, первые десять символов,
го байта будет равно четырем.
al.
сле оператора
101

писали s[0]:=Chr(10); И всё, в общем-то, у некоторых работало. И даже под Delphi работало. И как же эти некоторые
огребли при переходе на Delphi 2…
А что такое Ord и Chr, разузнайте сами. Потом, конечно, я расскажу.
Если кто-то сомневается, стоит ли экономить жалкие байты на такой ерунде, то задумайтесь – а если таких строк у вас
миллион?
Если у нас есть две целые переменные, то мы можем поставить между
ними знак арифметической операции и результат присвоить третьей целой
переменной. Для дробных чисел всё так же, только операции немного
другие. А для строк?
var
s1,s2,s3 : string;
begin
ClrScr;
s1:=’123 ‘;
s2:=’abcd ‘;
s3:=s1 + s2;
Writeln(s3);
Получим на экране
123 abcd
С плюсом разобрались – он склеивает две строки в од
ну. А ещё? А всё!
Больше операторов не предусмотрено. Почти. Придётся идти другим
путём.
Просто строка и её процедуры
Ну не совсем её процедуры. Скорее процедуры для неё. И не совсем
процедуры. Скорее функции. Но неважно. Сейчас перейдём к конкретике,
и всё будет по
Функция Length. Не очень ин
нятно. Более-менее.
тересная, но самая важная. Поскольку это
функция, вроде Sin, то она возвращает значение, которое можно
прис
воить переменной. Только у Sin значение дробное, а у Length целое.
Пишем вот такое:
var
102

s : string;
len : integer;
begin
ClrScr;
s:=’12345’;
len:=Length(S);
Writeln(len);
Видим на экране цифру пять. Путём несложных умственных усилий
приходим к немудрёному выводу, что функция Length возвращает нам
количество символов в строке. А почему она самая важная? А это станет
ясно в следующем разделе.
Кстати, помните, что кавычка внутри строки кодируется двумя идущими
подряд кавычками? В подсчёте симв
следовало ожид
А пока – функция Copy.
s1:=’12345’;
s2:=Copy(S, 2, 3);
Writeln(s2);
В результате имеем 234. Copy выкусывает из строки часть,
начинающуюся с символа имеющего номер, указываемый вторым
параметром и в количестве символов, которое задает третий параметр.
Обратите внимание, что эта функция в качестве результата возвращает не
число, а строку.
А теперь функция Pos. Она во
поисками одной строки в другой. Вот так:
s1:=’12345’;
where:=Pos(‘
После выполнения where будет равно, разумеется, двум. А если искать в
строке что-нибудь эдакое, чего в ней нет, то в результате получим, как
принято, ноль.
Ещё совершенно необходимая – не функция – процедура Delete.
ать, идут за одну.
2’, s1);
олов эти две кавычки, как и
звращает целое число и занимается
103

s1:=’12345’
Delete( s1, 3,2);
В результате в s1 останется только 125. Двойка в параметрах говорит, что
из исходной строки надо удалить два символа, а тройка – что удалять
символы надо, начиная с третьей позиции в строке.
Если что-то можно удалить, то обычно где-то рядом должна быть и
возможность обратного процесса. И вот она – по
параметра – что вставить,
s1:=’12345’;
s2:=’abc’;
Insert( s2, s1, 3);
Writeln(s1);
Получим 12abc345.
Вот и вся компания.
Применяется этот зоопарк обычно не по одному, а в совокупности.
s1:=’x=f(y)’;
s2:=’z=x’;
Задача – подставить во вторую строку значение X из первой.
s3:=s2;
Delete( s3, Pos(‘=’,s2)+1, 999);
Insert( Copy(s1,Pos(‘=’,s1)+1,999), s3, Pos(‘=’,s3)+1);
В результате в s3 должны получить
‘z=f(y)’;
Магическое число 999 означает, что мы имеем в виду все си
конца строки. Copy и Delete, если третий параметр больше, чем в строке
ось символов, понимают его, как количество символов до конца
остал
строки.
куда, в какое место. То есть:
д названием Insert. Три
мволы до
104

Строка и цикл
С помощью процедур из предыдущего раздела и циклов можно решить
любую задачу обработки строк, хотя часто и несколько
противоестественным способом. Например, надо подсчитать количество
пробелов в строке. В цикле – находим пробел с помощью Pos,
увеличиваем счётчик, удаляем пробел с помощью Delete, снова находим
пробел… И так будем продолжать, пока строка не кончится. Цикл, правда,
нужен не
задача решаема,
по-другому – просто берём и считаем. Можно так? А как же.
Идея в том, что со строкой можно обращаться в точности, как с массивом.
Ну, почти. Например: s[5]:=’a’; - в пятом символе строки
‘a‘. Аналогично if s[5]=’b’ – проверяем – а не равняется ли случайно
пятый символ строки символу ‘b’?
Если мы написали s:=’12345’; то у нас в строке ровно пять символов.
Мы можем обращаться к символам от первого до пятого, но не более
того. Нельзя написать s[6]:=’6’, это плохо ко
‘6’;
А теперь, вооружённые но
все символы строки от первого до последнего. Проверяем – если пробел,
значит сосчитали.
Пишем цикл. От первого символа – это понятно. До последнего символа –
вспоминаем функцию Length. Как проверять на равенство пробелу, только
что познакомились. В результате имеем:
num:=0;
for i:=1 to Length(s) do begin
if s[i]= ‘ ‘ th
end;
А теперь задача – подсчитать, сколько раз в строке встречается
оп
проверять надо не только текущий символ, но и два последующих. Если
много другой, об этом ещё будет позже. Потом. В любом случае,
но в жизни подсчёт пробелов в строке решается совсем
будет символ
нчится. Надо так s:=s +
выми знаниями, считаем пробелы.. Перебираем
en num:=num + 1;
ределенный артикль ‘the’. Задача похожа на только что решённую, но
105

текущий символ имеет индекс I, то у следующего будет индекс I+1, а у
того, что после этого I+2. В целом получится что-то вроде:
num:=0;
for i:=1 to Length(s) do begin
if (s[i]= ‘t‘) and (s[i+1]=’h’) and (s[i+2]=’e’) then num:=num + 1;
end;
Почему “что-то вроде”? Потому что когда переменная цикла I будет
указывать на последний символ строки (Length(s)), мы попытаемся
проверить ещё и сл
получится. А с другой стороны – разве мож
едующий символ (I+1) и ничего хорошего из этого не
ет слово из трех букв
начинаться в последнем символе строки – ясно, что нет, ведь не уместится
же. Не может начинаться и в предпоследней, по той же причине (так что
рухнет наша программа ещё раньше, при I, равном Length(s))-1). А раз оно
там начинаться не может, так зачем
проверять? Урезаем цикл на два
исполнения:
num:=0;
for i:=1 to Length(s)-2 do begin
if (s[i]= ‘t‘) and (s[i+1]=’h’) and (s[i+2]=’e’) then num:=num + 1;
end;
Может, у кого-то затесалась в голову мыслишка, что, ежели мы обнаружили слово “the”, начинающееся с позиции I,
то проверять следующие две позиции на наличие символа ‘t’ – пустое расточительство? Немедленно топтать
мыслишку коваными сапогами. Нам не надо, как лучше – нам надо чтоб работало. Работает – отойди, не трогай.
На самом деле при таком подходе мы сосчитаем и все слова типа then,
содержащие внутри себя последовательность символов the, но не будем
обращать внимание на такие мелочи. Пока.
Помните, как считается сумма элементов массива?
sum:=0;
for i:=1 to N do
sum:=sum + a[i];
106

У строки ведь тоже есть оператор “+”, хотя он выполняет не
арифметическое сложение, а склейку строк. Простая задача – заполнить
строку одним и тем же символом, например ‘-’. Всё аналогично. В начале
цикла целой переменной присваивается значение ноль. А что будет
аналогом ноля для строки? Пустая строка, то есть строка, в которой нет ни
ого символа. Обозначается это чудо вот так ‘’ – две кавычки подряд,
одн
без проб
И вот текст программы:
s:=’’;
for i:=1 to N do
s:=s + ‘-‘;
Обратите внимание, мы заполнили строку, в которой до того не было
ничего. Если мы хотим заполнить каким то символом строку из
предыдущего примера, то есть уже заполненную, то выглядеть это бу
так:
for i:=1 to Length(s) do
s[i]:=‘*‘;
Очень похоже на м
А теперь ещё две задачи, которые в разных вариациях будут встречаться
весьма часто.
Удалить из строки (например, ‘123 456 789’) все пробелы.
Напрашивается быстрая модификация недавно созданной программы:
s:=’123 456 789’;
for i:=1 to Length(s) do begin
if s[i]= ‘ ‘ then Delete( s, i, 1);
end;
В результате имеем на вых
вредности подсовываем на вх
группами по два пробела). Результат – ‘123 456 789’. Что-то не так,
пробелы уменьшились в количестве, но всё-таки остались. Почему? Очень
ела между ними.
дет
ассив. Обдумайте.
оде: ‘123456789’. Замечательно! А теперь из
од вот такую строку – ‘123 456 789’ (между
107

просто. Сначала проверили четвёртый символ, потом пятый. Четвертый
оказался пробелом, его удалили. Но при этом вся остальная часть строки
сдвинулась влево и нумерация оставшихся символов изменилась.
Следующий пробел, бывший пятый, стал четвёртым. Но четвёртый
символ мы уже проверили и переходим к пятому, а туда съехала цифра 4
из бывшей шестой позиции. Вот так всё не
будем?
Если мы начнём просматривать строку с конца к началу, то, удалив
первый встреченный пробел (последний в строке), мы изменим
нумерацию всех символов справа, но там-то мы уже были. А с символами
слева, куда мы и направляемся, всё останется в порядке.
s:=’123 456 789’;
for i:=L
if s[i]= ‘ ‘ then Delete( s, i, 1);
end;
Если у нас цикл движется от большего значения к меньшему, то, вместо to
должно использоваться downto.
Запомните – удаление чего-то из чего-то пронумерованного обычно
происходит, начиная с конца этого второго чего-то. Такое ненарушаемое
правило.
А теперь задача - подсчитать количество слов в строке. Количе
пробелов между словами – произвольное, то же с пробелами в
конце строки, так что идея просто подсчитать пробелы нас к решению не
приблизит. Задача это встречается чаще, чем кажется с первого взгляда.
Но сначала придётся отвлечься и мимоходом ввести ещё один тип
данных. Тянул я с этим до последнего.
ength(s) do
wnto 1 do begin
Ой, кто пришел!
удачно получило
сь. Что делать
ство
начале и в
Встречаем – логические переменные, они же булевские. Зачем нужны?
Да незаче
то, излишняя сущность. Гвозди вполне можно и топором заколачивать. В
переводе на программирование – забываем про булевский тип, объявляем
целую переменную и договариваемся, что будем присваивать ей строго
108
м, как обычно. Вполне можно обойтись. Молоток, он, в общем-

только ноль или единицу. Причем, опять же для себя, решаем, что ноль
значит “нет”, единица значит “да”. Да ведь мы это уже делали – когда
сортировали массив! А теперь ещё раз, проиллюстрируем на другом
примере.
Хотелось бы знать – а не состоит ли наша строка полностью, только, и
исключительно из одних пробелов? Вопро
как мы считали пробелы в строке, можно додуматься до такого варианта:
подсчитать пробелы в строке и, если их количество совпадет с длиной
строки – то да. А если нет – то нет.
num:=0;
for i:=1 to Length(s) do begin
if (s[i]= ‘ ‘) then num:=num + 1;
end;
if num=Length(s)
then Writeln(‘Yes!’)
else Writeln(‘No!);
Работает, конечно. Но как-то неизящно. И зачем дел
Зачем сч
нас же никто про это не спрашивал. Вопрос был – да или нет?
Пишем вот так (onlySpaces, понятно, целое и означает, что строка только
из пробелов):
onlySpaces:=1;
for i:=1 to Length(s) do begin
if (s[i]<> ‘ ‘) then onlySpaces:=0;
end;
if onlySpaces = 1
then Writeln(‘Yes!’)
else W
А теперь доведём наш шедевр до по
переменными. Сначала теория, разумеется.
Объявляем:
var
итать пробелы и помнить (до условного оператора) сколько их? У
riteln(‘No!);
с очень реальный. Вспо
ать лишнюю работу?
лного блеска булевскими
мнив,
109

onlySpaces : boolean;
Булевская переменная может получить только два значения:
onlySpaces:=true; { да} или
onlySpaces:=false; {нет}
Использовать её в условном операторе можно как обычно:
if onlySpaces = true { да} или
if onlySpaces = false {нет}.
Но так никогда не пишут, поскольку ради булевских переменных
разрешена, для наглядности, сокращённая форма записи:
if onlySpaces { да} или
if not onlySpaces {нет}.
Окончательный вариант (onlyS
onlySpaces:=true;
for i:=1 to Lengt
if (s[i]<> ‘ ‘) then onlySpaces:=false;
end;
if onlySpaces
then Writeln(‘Yes!’)
else Writeln(‘No!);
Обратите внимание – это важно! Если нам надо определить, есть ли в
строке хоть один пробел (для умных – квантор существования), то
признак перед началом цикла устанавливается в ложь (false), а при
встрече первого пробела устанавливается в истину (true). Напротив, если
надо опред
ости), то символ устанавливается перед циклом в истину, а при
общн
встрече первого не пробела устанавливается в ложь.
h(s) do begin
елить, все ли символы в строке пробелы (для умных – квантор
paces объявлено как boolean):
110
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
