06. Строки. Форматирование.


Методы строк

Мы уже знаем что-то про строки (впрочем, основное, что нужно о них знать) - как брать срезы (подпоследовательности) строк, как их складывать, как узнать длину строки, два полезных метода join и split.

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

Библиотека string

Это очень небольшая библиотека, но она может быть полезна, чтобы иметь "группы" символов под рукой, а не создавать их самим (однако, там далеко не все символы, например, там нет букв не английского алфавита). Таким образом можно получить все пунктуационные и пробельный символы:

In [1]:
import string
string.punctuation, string.digits, string.whitespace
Out[1]:
('!"#$%&\'()*+,-./:;<=>[email protected][\\]^_`{|}~', '0123456789', ' \t\n\r\x0b\x0c')

Метод find

Это метод поиска подстроки в строке, как легко понять из названия. Он возвращает индекс (только неотрицательный) первого вхождения подстроки в строку, а если она не нашлась то -1.

In [2]:
s = 'python string'
s.find('o'), s.find('str'), s.find('P')
Out[2]:
(4, 7, -1)
In [3]:
s.find('t')
Out[3]:
2

Как видим t было найдено только одно - в слове python. В методе есть позиционные параметры в каком интервале искать подстроку. То есть метод выглядит в "общем" виде так:

Строка.find(ИскомаяПодстрока, Старт, Конец)

In [4]:
s = 'python string'
s.find('t', 0, 5), s.find('t', -5, -2), s.find('t', s.find('t') + 1), s.find('th', 5)
Out[4]:
(2, 8, 8, -1)

Метод rfind

Он работает аналогично find, но ищет самое правое вхождение (первое с конца).

In [5]:
s = 'python to do'
s.rfind('n to')
Out[5]:
5
In [6]:
s.rfind('t', 9, 100)
Out[6]:
-1
In [7]:
s = ' ' + 'a' * 100000
In [8]:
%timeit s.rfind(' ')
10.7 µs ± 1.05 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [9]:
%timeit s[::-1].find(' ')
329 µs ± 27.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Метод replace

Имеет три позиционных параметра - old, new и необязательный count. Возвращает строку, в которой все вхождения или, если указано, count первых вхождений подстроки old заменены на подстроку new.

In [10]:
s = 'Python 3 Python 3'
s_new = s.replace('P', 'C')
In [11]:
s_new2 = s_new.replace('3', ';)')
print(s, s_new, s_new2, sep=' -> ')
Python 3 Python 3 -> Cython 3 Cython 3 -> Cython ;) Cython ;)
In [12]:
s = 'AAAAAAAA' # 8 букв А
s_new = s.replace('AA', 'A')
s_new2 = s.replace('AA', 'A', 2)
print(s, s_new, s_new2)
AAAAAAAA AAAA AAAAAA

Метод count

Выдает целое число - количество непересекающихся вхождений подстроки. По аналогии с методом find есть параметры интервала поиска.

In [13]:
s = 'python python 3'
s.count(' python ') # обратите внимание на пробел
Out[13]:
1
In [14]:
s = 'AAAAAAAA' # 8 букв А
s.count('A'), s.count('AA', 3, 7), s.count('AAA', 3, 7), s[3:7] 
# обратитет внимание, что вхождения подстрок НЕ ПЕРЕСЕКАЮТСЯ!
Out[14]:
(8, 2, 1, 'AAAA')

Другие методы

Методы, проверяющие состав строки

S.isdigit() Состоит ли строка ТОЛЬКО из цифр

In [15]:
'123'.isdigit(), '1a'.isdigit(), '1,2,3'.isdigit(), '1 2 3'.isdigit(),\
''.isdigit(), ' '.isdigit()
Out[15]:
(True, False, False, False, False, False)

S.isalpha() Состоит ли строка только из букв

In [16]:
'абвσ'.isalpha(), 'a1b2b3'.isalpha(), 'a,b,c'.isalpha(), \
'a b c'.isalpha(), ''.isalpha(), ' '.isalpha()
Out[16]:
(True, False, False, False, False, False)

S.isalnum() Состоит ли строка из цифр или букв

In [17]:
'a'.isalnum(), '1'.isalnum(), 'a1'.isalnum(), \
'a,1'.isalnum(), 'a 1'.isalnum(), ''.isalnum(), ' '.isalnum()
Out[17]:
(True, True, True, False, False, False, False)

S.islower() Состоит ли строка только из символов в нижнем регистре

In [18]:
's'.islower(), 's,s'.islower(), ','.islower(), '1'.islower(),\
'S'.islower(), ''.islower(), ' '.islower()
Out[18]:
(True, True, False, False, False, False, False)

S.isupper() Состоит ли строка только из символов в верхнем регистре

In [19]:
'S'.isupper(), 'S,S'.isupper(), ','.isupper(), '1'.isupper(),\
's'.isupper(), ''.isupper(), ' '.isupper()
Out[19]:
(True, True, False, False, False, False, False)

S.istitle() Начинаются ли все слова (слова := идущие подряд буквы (=символы имеющие регистр) без разделения "небуквой" (=символ не имеющий регистр)) в строке с заглавной буквы

In [20]:
'Python 3 Python'.istitle(), 'Python3Python'.istitle(), 'A'.istitle(), '!P'.istitle(), \
'Python1python'.istitle(), 'python 3'.istitle(), '3py'.istitle(), ''.istitle(), ' '.istitle()
Out[20]:
(True, True, True, True, False, False, False, False, False)

S.startswith(str, start=0, end=-1) Начинается ли строка S[start:end] со строки str.

In [21]:
myfiles = ['forloop.png', 'lecture01_intro.ipynb', 'lecture01_intro_slides.ipynb', 
           'lecture02_if.ipynb', 'lecture02_if_slides.ipynb', 'lecture03_container.ipynb', 
           'lecture03_container_slides.ipynb', 'lecture04_for.ipynb', 'lecture04_for_slides.ipynb', 
           'lecture05_def.ipynb',  'lecture05_def_slides.ipynb', 'lecture06_str_slides.ipynb', 
           'qrcode.png']

for file in myfiles:
    print(file.startswith('lecture'), end=', ')
False, True, True, True, True, True, True, True, True, True, True, True, False, 

S.endswith(str) Заканчивается ли строка S строкой str

In [22]:
for file in myfiles:
    print(file.endswith('.png'), end=', ')
True, False, False, False, False, False, False, False, False, False, False, False, True, 
In [23]:
for file in myfiles:
    print(file.split('.')[0].endswith('slides'), end=', ')
False, False, True, False, True, False, True, False, True, False, True, True, False, 

Методы, работающие с регистром символов

S.capitalize() Переводит первый символ строки в верхний регистр, а все остальные в нижний

In [24]:
s, S = 'the StOry oF My life1', 'SALE 100%'
s.capitalize(), S.capitalize()
Out[24]:
('The story of my life1', 'Sale 100%')

S.swapcase() Переводит символы нижнего регистра в верхний, а верхнего – в нижний

In [25]:
s.swapcase(), S.swapcase()
Out[25]:
('THE sToRY Of mY LIFE1', 'sale 100%')

S.title() Первую букву каждого слова переводит в верхний регистр, а все остальные в нижний

In [26]:
s.title(), S.title()
Out[26]:
('The Story Of My Life1', 'Sale 100%')

S.upper() Преобразование строки к верхнему регистру

In [27]:
s.upper()
Out[27]:
'THE STORY OF MY LIFE1'

S.lower() Преобразование строки к нижнему регистру

In [28]:
S.lower()
Out[28]:
'sale 100%'

Функции для работы с кодом Unicode

ord(символ) Возвращает код символа

In [29]:
ord('A'), ord('a'), ord('&')
Out[29]:
(65, 97, 38)

chr(число) Возвращает символ по его коду

In [30]:
chr(42), chr(57), chr(69), chr(100)
Out[30]:
('*', '9', 'E', 'd')

Форматирование строк

Подробнее

Бывает нам хочется напечатать что-то вроде такого:

In [31]:
n, m = int(input()), int(input())

print('Number of students\nGroup 1:', n, '\nGroup 2:', m, '\nOverall:', n + m)
12
23
Number of students
Group 1: 12 
Group 2: 23 
Overall: 35

Проблема: приходится "изменяемые" переменные вставлять через запятые, остальной строковой текст все время обрамлять в кавычки. Конечно, приведенный пример это далеко на самый плохой вариант, н опредставьте, если таких переменных, например, 100? Уже сложнее. Питонисты любят когда все коротко и ясно. Поэтому есть такой метод (впрочем, не только), .format(). Работает он следующим образом:

In [32]:
s = 'Это число {}, это буква "{}", это предложение "{}"'
s.format(n, m, n+m)
Out[32]:
'Это число 12, это буква "23", это предложение "35"'
In [33]:
s = 'Это число {2}, это буква "{1}", это предложение "{0}"'
s.format(1, 'a', 'Hi, Monty!!')
Out[33]:
'Это число Hi, Monty!!, это буква "a", это предложение "1"'
In [34]:
s = 'Это число {x[2]}, это буква "{x[1]}", это предложение "{x[0]}"'
p = (1, 'a', 'Hi, Monty!!')
s.format(x=p)
Out[34]:
'Это число Hi, Monty!!, это буква "a", это предложение "1"'

То есть Питон считывает строку, видит в ней "фигурные скобки" - места куда вставить объекты, и то, что лежит внутри метода .format() вставляет в том порядке, в котором они указаны (по дефолту подряд), создавая новый объект типа строка.

Также можно указать сколько чисел после запятой вывести у вещественного числа:

In [35]:
pi = 3.14159265559
print('Number pi = {:.5f}'.format(pi), 
      'Number pi = {:.0f}'.format(pi),
      'Number pi = {: 6.2f}'.format(pi), sep='\n')
Number pi = 3.14159
Number pi = 3
Number pi =   3.14

Можно "паддить" строки:

In [36]:
print('|{:>10}|\n|{:^10}|\n|{:<10}|\n|{:.^10}|'.format('Hello', 'Hi', 'Hey', 'Hi'))
|     Hello|
|    Hi    |
|Hey       |
|....Hi....|

Usage case:

In [37]:
# генерируем строку, в которую хотим вставить какие-то значения
toprint = '\n'.join([chr(65 + i) + chr(97 + i) +': {: 5.0f}|' for i in range(26)])
toprint
Out[37]:
'Aa: {: 5.0f}|\nBb: {: 5.0f}|\nCc: {: 5.0f}|\nDd: {: 5.0f}|\nEe: {: 5.0f}|\nFf: {: 5.0f}|\nGg: {: 5.0f}|\nHh: {: 5.0f}|\nIi: {: 5.0f}|\nJj: {: 5.0f}|\nKk: {: 5.0f}|\nLl: {: 5.0f}|\nMm: {: 5.0f}|\nNn: {: 5.0f}|\nOo: {: 5.0f}|\nPp: {: 5.0f}|\nQq: {: 5.0f}|\nRr: {: 5.0f}|\nSs: {: 5.0f}|\nTt: {: 5.0f}|\nUu: {: 5.0f}|\nVv: {: 5.0f}|\nWw: {: 5.0f}|\nXx: {: 5.0f}|\nYy: {: 5.0f}|\nZz: {: 5.0f}|'
In [38]:
# вводим какие-то данные
tocount = input().lower()
eqtwyetwfdgvgghvsbxvgdcfztraeqwtyieijqtwyetwfdgvgghvsbxvgdcfztraeqwtyieijqtwyetwfdgvgghvsbxvgdcfztraeqwtyieijqtwyetwfdgvgghvsbxvgdcfztraeqwtyieij
In [39]:
# производим вычисления с переменными данными
counted_alphabet = [tocount.count(chr(97 + i)) for i in range(26)]
In [40]:
# печатаем с помощью форматирования
print(toprint.format(*counted_alphabet))
Aa:     4|
Bb:     4|
Cc:     4|
Dd:     8|
Ee:    13|
Ff:     8|
Gg:    16|
Hh:     4|
Ii:     8|
Jj:     4|
Kk:     0|
Ll:     0|
Mm:     0|
Nn:     0|
Oo:     0|
Pp:     0|
Qq:     8|
Rr:     4|
Ss:     4|
Tt:    16|
Uu:     0|
Vv:    12|
Ww:    12|
Xx:     4|
Yy:     8|
Zz:     4|

f-string

Абсолютно то же самое (в новых версиях Питона) можно делать следующим более кратким и удобным (не всегда) способом:

In [41]:
name = 'Sonya'
yr = 2020
old = 22
f'Hi, my name is {name}. I was born in {yr-old}.'
Out[41]:
'Hi, my name is Sonya. I was born in 1998.'
In [44]:
'Hi, my name is {}. I was born in {}.'.format(name, yr-old)
Out[44]:
'Hi, my name is Sonya. I was born in 1998.'