Символы и строки
До сих пор наши программы работали только с числами. Но многим программам надо работать с текстовыми данными. Для этого есть два основных объекта — символы и строки.
Символьный тип данных (паскаль)
Для хранения отдельных символов (букв, цифр, всяких знаков препинания и т.п.) в паскале есть тип данных char:
var ch:char;— объявляет переменную, в которой можно хранить символ.
В такую переменную можно записать любой символ конструкциями следующего вида:
ch:='a'; ch:='$';Здесь в правой части присваивания так называемые символьные константы, т.е. нужные символы, заключенные в апострофы. Здесь первая команда записывает в переменную ch символ "a", вторая — символ "доллар".
Кроме того, символы можно вводить и выводить привычными конструкциями:
read(ch); write(ch); // не переводя строку writeln(ch); // с переводом строки
Символьный тип данных (питон)
В питоне, чтобы сохранить символ в переменной, надо просто написать
ch = "a" ch = "$"
и т.п.
При этом можно использовать как символы кавычек ("), так и символы апострофов ('), это не имеет значения. Главное, чтобы они были согласованы.
Вводить символы можно обычной командой input()
:
ch = input()
(именно прямо так)
выводить — обычным print
:
print(ch)
(На самом деле, в питоне нет отдельного "типа" для символов, символ в питоне — это просто строка длины 1, про строки см. ниже. Но часто удобно думать про символы отдельно от строк.)
Коды символов (общее и для паскаля, и для питона)
На самом деле, конечно, в памяти компьютера хранятся не символы (т.е. если мы написали ch:='$';
(паскаль) или ch="$"
(питон), то нигде в памяти не будет нарисован доллар). Компьютер умеет работать только с числами, и вместо символов он хранит тоже числа.
Есть общепринятая договоренность, которая каждому числу от 0 до 255 ставит в соответствие некоторый символ. Точнее, таких договоренностей есть несколько, они называется кодировки, но для латинских букв, цифр и частоупотребимых символов типа того же доллара, запятой или плюса, во всех кодировках соответствующие числа одинаковы. Для русских букв это не так: в разных кодировках им соответствуют разные числа, но это отдельная тема.
Эта общепринятая сейчас кодировка для латинских букв, цифр и частоупотребимых символов называется ASCII, иногда говорят таблица ASCII. Полностью эту таблицу (точнее, символы от 0 до 127 — эта часть собственно и называется ASCII; символы с номерами от 128 до 255 строго говоря не считаются ASCII, там как раз в разных вариантах русские буквы и т.п.) можно посмотреть, например, здесь. Здесь колонка Decimal — это номер символа, колонка Hex — номер символа, но в 16-ричной системе счисления (для тех, кто знает, что это такое, остальные игнорируйте колонку Hex), колонка Char — собственно сам символ. Пояснения: символы с номерами (кодами) до 31 включительно — это так называемые управляющие символы, они нам пока не очень интересны (равно как и символ 127); символ 32 — это пробел (в таблице написано SPACE). Остальные символы вроде понятны.
Например, символ доллар имеет номер (говорят код) 36, а символ N — 78.
Обратите внимание, что все цифры идут подряд, все заглавные буквы идут подряд, и все маленькие буквы идут подряд. Это нам будет очень полезно. (Для русских букв это выполняется не всегда.)
Что в паскале, что в питоне узнать код символа можно операцией ord, а узнать символ по коду можно операцией chr. Например:Паскаль | Питон |
var i:integer; ch:char; begin read(ch); // считали символ... writeln(ord(ch)); // и вывели его код i:=ord('$'); // записали в i код доллара writeln(i); read(i); // считали код writeln(chr(i)); // и вывели соответствующий символ ch:=chr(ord('$') + 1); writeln(ch); // вывели символ, следующий за долларом end. |
ch = input() # считали символ... print(ord(ch)) # и вывели его код i = ord('$') # записали в i код доллара print(i) i = int(input()) # считали код print(chr(i)); # и вывели соответствующий символ ch=chr(ord('$') + 1) print(ch) # вывели символ, следующий за долларом |
В большинстве случаев точное знание кодов символов вам не надо — вы всегда можете что надо вычислить через ord. Например, если мы знаем, что в переменной ch у нас цифра (т.е. символ, соответствующий цифре) — как в переменную i записать значение этой цифры (т.е. 0, 1, 2, ..., или 9)? Т.е. как перевести цифру-символ в число?
Нам поможет то, что все цифры идут подряд. Поэтому достаточно из кода цифры вычесть код нуля:
i:=ord(ch)-ord('0'); // паскаль i = ord(ch) - ord('0') # питон
Обратите внимание: нам не надо знать, что код нуля — 48. Мы прямо пишем ord('0'), а не 48, компьютер сам вычислит код нуля за нас!
Сравнения символов (и паскаль, и питон)
Символы можно сравнивать операторами =, >, <, >=, <=. На самом деле сравниваются их коды:
Паскаль | Питон |
if ch1=ch2 then // если два символа совпадают... .... if ch1>ch2 then // если код первого символа больше кода второго .... |
if ch1 == ch2: # если два символа совпадают... .... if ch1>ch2: # если код первого символа больше кода второго .... |
Благодаря тому, что однотипные символы идут подряд, очень легко можно проверять тип символа. Например, чтобы проверить, является ли символ цифрой, можно написать:
if (ch>='0') and (ch<='9') then... // паскаль if ch>='0' and ch<='9': ... # питон
Массивы и циклы (паскаль)
Массивы можно индексировать символами:
var a:array['a'..'z'] of integer; ... a['d']:=10;
Кроме того, можно делать циклы по символам:
var ch:char; ... for ch:='a' to 'z' do begin...
В обоих случаях порядок символов подразумевается по их кодам. Например, я могу сделать массив a:array['A'..'z'] of integer;
— здесь будет по элементу для каждого символа с кодами от A до z.
Массивы и циклы (питон)
В питоне нельзя так просто, как в паскале, индексировать массивы символами и делать циклы по символам. Если вам надо сделать массив, в элементах которого хранить что-то, связанное с цифрами, то надо переходить к кодам:
a = [0] * 256 # у нас всего 256 символов a[ord('d')] = 10 # в элемент, соответствующий d, записали 10 ... for x in range(ord('a'), ord('z')+1): ch = chr(x) print(ch) # выводим все символы от a до z
Но вообще это продвинутая тема, сейчас пока вам не особо нужная.
Строки
Строка — это последовательность символов. Поэтому представляется естественным использовать для хранения строк массив символов:
// паскаль: var s:array[1..1000] of char; // строка не длиннее 1000 символов ... // питон: s = ["T", "e", "s", "t"] {Но так делать не надо!}
В паскале есть специальный тип данных для строк — string
:
var s:string;
В питоне, чтобы записать строку в переменную, надо просто записать строку в переменную:
s = "Test"
Что в питоне, что в паскале, строка — это массив, каждым элементом которого является символ, но это не просто массив, а массив с дополнительными функциями.
Во-первых, вам не надо думать про длину строки. Паскаль и питон автоматически сами выделят под строку сколько надо памяти.
Внимание! В разных книжках по паскалю вы можете прочитать, что строки не бывают длиннее 255 символов. Это верно только в ряде вариантов паскаля. В используемом нами варианте — Free Pascal в режиме {$mode delphi} — строка может быть сколь угодно длинной — пока у программы не кончится доступная память.
Длину строки в паскале можно узнать командой length(s)
, в питоне — как и у массива, командой len(s)
:
writeln(length(s)); // паскаль print(len(s)) # питон
Во-вторых, строки, конечно, можно считывать и выводить. На паскале это делается стандартными командами:
readln(s); writeln(s);(Почему readln, а не read, — см. ниже.)
На питоне — вывод обычным print
, а ввод — обычным input()
, никакой лишней конвертации не надо, пишете s = input()
:
s = input() print(s)
В-третьих, строки можно складывать. Сложить две строки — значит приписать к одной строке другую:
Паскаль | Питон |
readln(s1); readln(s2); s:=s1+s2; writeln(s); // выведет две строки одну за другой |
s1 = input() s2 = input() s = s1 + s2 print(s) # выведет две строки одну за другой |
Прибавлять можно и символы:
s:=s+'A'; // паскаль s = s + 'A' # питон
Наконец, строковые константы — это уже привычные вам последовательности символов в апострофах (паскаль) и в кавычках (питон):
Паскаль | Питон |
s:='Test'; s:=s+'2'; writeln(s); // выводит Test2 |
s = "Test" s = s + '2' print(s) # выводит Test2 |
На самом деле, в питоне можно использовать как апострофы (символы '
), так и кавычки (символы "
)
Может возникнуть вопрос, как в строковой константе ввести собственно символ апостроф или кавычку. Просто так написать 'It's a string'
не получится, т.к. что паскаль, что питон подумают, что строка закончилась на втором апострофе; аналогично в питоне не сработает "Text"Text". Поэтому в паскале внутри строковых констант апострофы надо удваивать, в а питоне — приписывать символ \
перед апострофом или кавычкой. Например, чтобы записать в переменную строку "It's a string", надо написать
s:='It''s a string'; // паскаль s = 'It\'s a string' # питон s = "It's a string" # тоже питон s = "It's a \"string\"" # тоже питон когда в строке и кавычки, и апострофыАналогично для записи символа "апостроф"/"кавычка" в переменную типа char:
ch:=''''; // паскаль ch = '\'' # питон ch = "'" # тоже питон ch = "\"" # тоже питон ch = '"' # тоже питон
Еще частный случай строки — пустая строка, т.е. строка длины ноль:
s:=''; // паскаль s = "" # питон
Ну и наконец, строка — это все-таки массив символов. Можно использовать все известные вам операции над массивами (писать s[i], чтобы получить доступ к i-му символу строки, и т.д. В паскале фактически других операций нет, в питоне много). Например, так можно проверить, есть ли в строке пробелы:
// паскаль for i:=1 to length(s) do if s[i]=' ' then... # питон for i in range(len(s)): if s[i] == ' ': ...
Почему readln? (Паскаль)
До сих пор я требовал, чтобы вы всегда использовали команду read, а не readln. Но до сих пор мы работали с числами; и пробелы и переводы строк были нам просто разделителями чисел, и поэтому команда read прекрасно работала.
Но теперь нам надо особо отличать перевод строки. Когда мы считываем строку (string) с клавиатуры, нам надо считать ее до перевода строки. Поэтому нам важно различать и уметь применять команды read и readln.
Различие у них единственное: read только считывает то, что попросили, и тут же останавливается. Readln же, считав то, что попросили, дальше пропускает все введенные данные до конца строки, и пропускает этот конец строки.
Например:
var a,b,c:integer; begin read(a); read(b); read(c); ...
Пусть на вход мы подаем следующие данные:
2 3 4Первый read считает число 2 и тут же остановится. Второй read увидит, что текущий символ пробел, пропустит его, увидит 3, считает его и остановится. Третий read увидит, что строка кончилась (на самом деле конец строки — это один или два специальных символа), перейдет на следующую строку, увидит там 4, и считает число 4.
Если же в программе были бы команды readln, то получилось бы следующее. Первый readln считывает число 2 и пропускает все остальное, что было в этой строке, в том числе и перевод строки. Второй readln сразу же видит число 4, считывает его, и пропускает все до конца строки включительно. Третий readln видит, что ничего не осталось, и потому ждет, когда вы что-нибудь введете еще.
Для чтения чисел первое поведение (с read) абсолютно логично. Поэтому если вы чистаете числа, то используйте read.
Но пусть вы читаете строки:
var s1,s2:string; begin read(s1); read(s2); ...
Пусть вы вводите следующее:
abc def
Первый read считает 'abc', увидит перевод строки, и на этом остановится. Второй read увидит, что сразу идет перевод строки — он не будет его пропускать, а просто решит, что вы решили ничего не вводить, и s2 получится пустой строкой (длины 0).
Если бы были readln'ы, то первый readln считал бы 'abc' и пропустил бы перевод строки. Поэтому второй readln увидел бы символ d, считал бы 'def' и т.д.
В общем, будьте с этим внимательны и используйте те команды, которые вам нужны. Еще пример: если вводится число, а на следующей строке — строка, например:
5 abcто читать надо так (поймите, почему!)
readln(n); readln(s); // тут можно и read, если больше ничего не вводится
inttostr и т.п. (Паскаль)
Есть еще четыре полезных команды:inttostr // integer to string - целое число в строку strtoint // string to integer - строку в целое число floattostr // float to string - вещественное число в строку strtofloat // string to float --- строку в вещественное числоОни переводят числа в строки и обратно. Чтобы их использовать, надо в начале программы (после {$mode delphi}, но до var) написать uses sysutils;
{$mode delphi} uses sysutils; begin writeln(strtoint('2') + strtoint('55')); // выводит 57 writeln(inttostr(23) + 'abc' + inttostr(45)); // выводит 23abc45 writeln(strtofloat('2.5') * 2); // выводит 5.0000e0 writeln(floattostr(2.5) + 'a'); // выводит 2.5000e0a end.
int и т.п. (Питон)
Есть еще три полезных команды:int str floatОни переводят числа в строки и обратно, с
int
вы уже сталкивались.
print(str(23) + 'abc' + str(45)); // выводит 23abc45 print(float('2.5') * 2); // выводит 5.0000e0 print(str(2.5) + 'a'); // выводит 2.5000e0a
pos и т.п.
Паскаль: Есть еще ряд команд, работающих со строками, про которые вы можете прочитать в книжках — pos, copy, delete (паскаль) и т.п. Лучше их не используйте. В большинстве случаев можно обойтись без них, плюс вы точно не знаете, как долго они работают.
Питон: Вы знаете ряд хитрых команд работы с массивами, и иногда будет возникать желание их использовать при работе со строками. Лучше их не используйте, пока вы точно не будете понимать не только что, но и насколько быстро они работают. В большинстве случаев можно обойтись без них (и так даже будет проще!), плюс вы точно не знаете, как долго они работают. Аналогично про другие продвинутые функции типа find
.
(И паскаль, и питон) Например, пусть вам надо из строки удалить все пробелы. Можно писать примерно так (считаем, что у вас уже есть исходная строка s
):
// паскаль while pos(' ',s)<>0 do delete(s,pos(' ',s),1); // питон while s.find(" ") != -1: s = s[:s.find(" ")] + s[s.find(" ")+1:] # вырезаем этот символ
Но это работает долго (поверьте мне :) ) и требует от вас помнить все эти команды (а на питоне — еще и осознавать код). Проще так:
// паскаль s1:=''; for i:=1 to length(s) do if s[i]<>' ' then s1:=s1+s[i]; # питон s1 =''; for i in range(len(s)): if s[i] != ' ': s1 = s1 + s[i];
Результат лежит в s1
. Поймите, как это работает.
На самом деле, на паскале (но не на питоне) есть еще один способ, без второй строки и без длительных сдвигов. Можете подумать над ним.