Глава 44 Строки



Строковый тип STRING известен нам с первых глав книги, без него компьютер не общался бы с нами на «человечьем» языке. Так изучим строки получше. В современных версиях Паскаля применяют несколько строковых типов, сейчас мы рассмотрим только короткие строки, введенные ещё в Borland Pascal (в новых версиях Паскаля этот тип называется ShortString).

Строка – особый род массива

С первого взгляда строка похожа на массив символов. Так ли это? – проверим. Известно, что строка может вместить до 255 символов. Объявим массив из 255 символов и сравним его размер с размером строки. Напомню, что функция SizeOf возвращает размер памяти, занимаемой переменной.


var S1 : array [1..255] of CHAR; { это массив из 255 символов }

    S2 : String;     { это строка длиной 255 символов }

begin

    Writeln (SizeOf(S1)); { печатает 255 }

    Writeln (SizeOf(S2)); { печатает 256 }

    Readln;

end.


Запустили программку? И что? Странно, но размер строки S2 оказался равным 256 байтам, что на единицу больше размера массива. Почему? Где прячется ещё один байтик? Ответ представлен на рис. 98.



Рис.98 – Размещение слова «PASCAL» в строковой переменной

Здесь показана внутренность строковой переменной со словом «PASCAL». Байты с 1-го по 6-й содержат буквы этого слова, а остальные байты не заняты. Но в начале массива обнаружен ещё один байт – с нулевым индексом. Он содержит число 6 – это длина слова «PASCAL». Значит, строковый тип – это массив со скрытым нулевым байтом, хранящим фактическую длину строки (эту длину возвращает функция Length).

Укороченные строки

Память – жилище переменных – всегда чем-нибудь занята. Даже пустая строка (нулевой длины) занимает 256 байтов памяти, и содержит что либо. Это «что либо» программисты называют мусором, а мусор никому не интересен. Так разумно ли отводить 256 байтов для строки, если большая её часть забита всяким вздором? Ведь память – ценный ресурс, и профессионал бережет её. К примеру, для строки, хранящей фамилию, вполне хватило бы и 20 байтов.

Это понимали и создатели Паскаля, они позаботились об экономии памяти. Строковые типы можно объявлять с указанием длины. Для этого после слова STRING в квадратных скобках указывают нужный размер строки, например:


type TStrA = string[11]; { строка для 11 символов }

    TStrB = string[31]; { строка для 31 символа }

var A : TStrA;     B : TStrB;


Здесь объявлены два строковых типа данных; первый из них вмещает до 11 символов, а второй – до 31. Соответственно переменная A будет занимать в памяти 12 байтов, а переменная B – 32 байта (с учетом нулевого байта). Согласитесь, – экономия солидная, особенно для массива из таких строк. Во всем остальном, кроме размера, короткие строки ничем не отличаются от переменных типа STRING.

А что случится при копировании длинной строки в короткую? А ничего, – не вместившиеся символы будут «отрублены». Следующая ниже программа «P_44_1» подтверждает это, испытайте её.


{ P_44_1 – укороченные строки }

var S1 : string;     { размер строки по умолчанию = 255 }

    S2 : string[5]; { размер укороченной строки = 5 символов }

begin

S1:='abc';     S2:='abcdefgh';


Writeln('Строка S1: Размер =', SizeOf(S1):4,' Длина = ', Length(S1):4,' Значение= '+S1);

Writeln('Строка S2: Размер =', SizeOf(S2):4,' Длина = ', Length(S2):4,' Значение= '+S2);


Writeln('Нулевой байт строки S1 = ', Byte(S1[0]));

Writeln('Нулевой байт строки S2 = ', Byte(S2[0]));

Readln;

end.


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

Итак, уяснив внутреннее устройство строк, обратимся к связанным с ними операциям. Что мы умеем делать со строками сейчас? А вот что:

• вводить и выводить строки процедурами ввода и вывода;

• объединять несколько строк в одну (складывать);

• определять длину строки функцией Length;

• проверять строки на равенство и неравенство;

• обращаться к отдельным символам строки (доступ по индексу).

Учитывая важность строкового типа, разработчики Паскаля предусмотрели для строк ещё несколько процедур и функций, позволяющих:

• искать одну строку внутри другой;

• копировать часть строки в другую строку;

• вставлять одну строку внутрь другой;

• удалять часть символов из строки;

• сравнивать две строки в смысле алфавитного порядка.

Рассмотрим всё это подробней. Представленные далее объявления процедур и функций даны мною лишь для пояснений, их не надо вставлять в программы.

Поиск в строке (Pos)

Функция Pos ищет одну строку внутри другой, её объявление выглядит так:


    function Pos(SubS: string; S: string): Integer;


Функция принимает два параметра:

• SubS – подстрока, которую ищут (то есть фрагмент строки);

• S – строка, в которой ищут.

Если искомый фрагмент SubS найден, функция возвращает его позицию – индекс первого символа SubS внутри строки S, а иначе возвращает ноль. Если строка S содержит несколько искомых фрагментов, возвращается индекс первого из них. Вот примеры.


    S:= 'BORLAND PASCAL';

    p:= Pos('LA', S);     { 4 }

    p:= Pos('PAS', S);     { 9 }

    p:= Pos('pas', S);     { 0 – подстрока не найдена }

    p:= Pos('A', S);     { 5 – первая из трех букв "A" }


Искомым фрагментом может быть и отдельный символ. Поиск ведется с учетом регистра; это значит, что заглавная и строчная буквы «P» считаются разными буквами.

Копирование части строки (Copy)

Функция Copy возвращает часть заданной строки.


    function Copy(S: string; Index, Count: Integer): string;


Входных параметров три:

• S – строка, из которой копируются символы;

• Index – индекс первого копируемого символа;

• Count – количество копируемых символов.

А вот примеры её применения.


    S:= ’Free Pascal forever!’;

    T:= Copy(S, 6, 6);     { ’Pascal’ }

    T:= Copy(S, 6, 255); { ’Pascal forever!’ }


Если копируемых символов затребовано больше, чем содержится в исходной строке, то скопируются все символы до конца строки (как в последнем примере).

Вставка в строку (Insert)

Объединять строки сложением просто. А если надо вставить строку в середину другой? Тогда обратитесь к процедуре Insert.


    procedure Insert(S1: string; var S2: string; Index: Integer);


Входные параметры:

• S1 – вставляемая строка;

• S2 – ссылка на принимающую строку;

• Index – позиция вставки.

Вот один пример.


    S:='Спартакчемпион!';

    { В позицию 8 вставляются три символа: тире и два пробела }

    Insert(' – ', S, 8);     { Спартак – чемпион! }


Если позиция вставки превышает длину строки S2, то строка S1 добавится в конец S2. Если длина итоговой строки S2 превысит допустимый размер, лишние символы будут отброшены.

Удаление символов из строки (Delete)

Говорят: ломать – не строить. Попытайтесь, однако, удалить часть символов из строки. Слабо? А процедура Delete справляется с этим играючи.


    procedure Delete(var S: string; Index, Count : Integer);


Параметры таковы:

• S – ссылка на строку;

• Index – индекс первого удаляемого символа;

• Count – количество удаляемых символов.

Вот пример её применения.


    S:= ’Free Pascal forever!’;

    Delete(S, 6, 7);     { ’Free forever!’ }


Сравнение строк

Мы уже сравнивали строки на равенство (вспомните проверку пароля). Но строки сравнивают и на больше–меньше — лексикографически. При этом сравниваются слева направо коды символов двух строк в смысле их алфавитного порядка. Если длины строк разные и короткая совпадает с началом длинной, то большей считается длинная строка. Вот примеры:


    Writeln (’Borland’ > ’Pascal’); { false }

    Writeln (’ABC’ > ’AB’);  { true }

    Writeln (’ABC’ > ’abc’);  { false }

    Writeln (’45’ > ’1000’);  { true, поскольку ’4’ > ’1’ }


В первом примере код буквы «B» меньше кода буквы «P», поэтому левая строка меньше правой. Во втором случае первые символы совпадают, но левая строка длиннее, а значит больше. В третьем примере левая строка меньше, — тоже в соответствии с таблицей кодировки. Обратите внимание на неожиданный результат сравнения строк, составленных из цифр, — это вам не числа!

Сравнивая строки, можно отсортировать их в лексикографическом порядке (как если бы они располагались в словаре). К сожалению, такое сравнение работает только для латинских букв, для русских оно не всегда верно, приходится изобретать свою функцию сравнения (в DELPHI этой проблемы нет).

Перевод символов в верхний регистр (UpСase)

Функция UpСase меняет код латинской буквы, переводя её из нижнего в верхний регистр. Иными словами, она превращает строчную (маленькую) латинскую букву в заглавную (большую). Объявление функции таково.


    function UpCase(Ch: Char): Char;


Входной параметр – символ, а возвращается почти тот же символ, только «подросший», вот примеры.


    c:= UpCase(’r’);     { ’R’ }

    c:= ’n’;

    c:= UpCase( c );     { ’N’ }


Подсунув этой функции большую латинскую букву, цифру или знак препинания, вы получите назад свой символ неизменным. То же будет и с русскими буквами – они не обрабатываются функцией UpСase.


    c:= UpCase(’R’);     { ’R’ }

    c:= UpCase(’8’);     { ’8’ }

    c:= UpCase(’ы’);     { ’ы’ }


Функцией UpСase обычно приводят введенные строки к определенному виду. Ведь пользователь может ввести данные как заглавными, так и строчными буквами, а это иногда мешает правильной обработке строки.

Ознакомившись со строковой теорией, применим её, что называется, «в бою».

Подсчет слов в строке

Вот вам строка, посчитайте в ней количество слов «Pascal». Чуть подумав, вы остановитесь на функции Pos, – ведь она возвращает позицию искомого слова. Но функция обнаруживает лишь первое вхождение фрагмента, а как быть с остальными? Я предлагаю постепенно разрушать исходную строку. То есть, найдя искомый фрагмент, будем удалять его из строки и снова повторять поиск. На этом и построена программа «P_44_2».


{ P_44_2 - Подсчет слов «PASCAL» в строке }

var S : string; { исходная строка }

    p : integer; { позиция в строке }

    c : integer; { счетчик слов }

begin

S:='Лучший язык программирования – это PASCAL!'+

    'Изучите PASCAL! PASCAL не подведет!';

c:=0;

repeat

    p:= Pos('PASCAL', S); { ищем слово «PASCAL» }

    if p>0 then begin     { если нашли }

    Inc(c);     { то наращиваем счетчик }

    { и удаляем это слово из строки }

    Delete(S, p, Length('PASCAL'));

    end

until p=0;     { выход, если слов «PASCAL» больше нет }

Writeln('Найдено слов PASCAL: ',c); Readln;

end.


Контекстная замена

Любой текстовый редактор умеет заменять одну подстроку на другую, – это называется контекстной заменой. Устроим такую замену в строковой переменной. Итак, дана строка, содержащая несколько слов «Pascal». Заменим все вхождения слова «Pascal» словом «Паскаль» (чем не англо-русский переводчик?).

Разобравшись с предыдущей задачей, вы легко одолеете и эту. Для проверки вашего решения сравните его с моим («P_44_3»).


{ P_44_3 - Замена слов «Pascal» на «Паскаль» }

var S : string; { исходная строка }

    p : integer; { позиция в строке }

begin

S:='Лучший язык программирования – Pascal! '+

    'Изучите Pascal! Pascal не подведет!';

Writeln(S); { исходная строка }

repeat

    p:= Pos('Pascal', S);     { ищем слово 'Pascal' }

    if p>0 then begin     { если нашли }

    { удаляем это слово из строки }

    Delete(S, p, Length('Pascal'));

    { и вставляем в этом месте слово 'Паскаль'}

    Insert('Паскаль', S, p);

    end

until p=0; { выход, если слов 'Pascal' больше нет }

Writeln(S); { строка результата }

Readln;

end.


Итоги

• Строка родственна массиву символов. Дополнительный нулевой элемент этого массива содержит длину строки.

• Строка, объявленная без указания размера, по умолчанию занимает 256 байтов памяти и может содержать до 255 символов.

• Для экономии памяти используют строки меньшего размера. При объявлении таких строк размер указывают внутри квадратных скобок после слова STRING.

• В Паскале предусмотрен ряд встроенных процедур и функций, облегчающих обработку строк.

А слабо?

А) Напишите процедуру, переводящую все символы строки (латинские буквы) к верхнему регистру.

Б) Напишите функцию для приведения любой буквы к верхнему регистру (включая и русские). Подсказка: вспомните о таблице кодировки.

В) Напишите функцию для приведения любой буквы к нижнему регистру.

Г) Напишите собственные процедуры и функции обработки строк, повторяющие те, что встроены в Паскаль. Дайте им названия, похожие на стандартные, например: MyCopy, MyDelete и так далее.

Д) Вращение строки вправо. Напишите процедуру, перемещающую 1-й символ строки на место 2-го, 2-й – на место 3-го и т.д. Последний символ должен занять 1-е место. Примените средства обработки строк.

Е) Вращение строки влево. Напишите процедуру для перемещения 2-го символа на место 1-го, 3-го – на место 2-го и т.д. Первый символ должен стать последним.

Ж) Строка содержит несколько слов – предложение. Напишите программы для решения следующих задач.

• Напечатать в столбик отдельные слова введённого предложения.

• Определить количество слов в строке.

• Равномерно расставить пробелы между словами так, чтобы удлинить строку до 80 символов (исходная строка короче 80).

З) Напишите булеву функцию, определяющую, является ли строка (параметр) палиндромом. Палиндром читается одинаково в обоих направлениях.

И) Напишите булеву функцию, определяющую, можно ли из букв первого слова составить второе (например, «клавиша» и «вилка» – TRUE). Учитывается только набор букв, а не их количество. Подсказка: примените множества.

К) Дана строка, содержащая не менее трёх символов. Найти в ней три стоящих подряд символа, дающих максимальную сумму своих кодов.

Л) В строке найти возрастающую последовательность символов наибольшей длины (сравнивайте коды символов).

М) Напишите булеву функцию, проверяющую, следуют ли символы строки по неубыванию своих кодов.

Н) Напишите функцию для шифрования строки путём перестановки её символов, расположенных на нечётных позициях: первый символ обменивается с последним, третий – с третьим от конца и т.д.

Загрузка...