ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 05.12.2023
Просмотров: 815
Скачиваний: 3
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Глава 6. Строки и двоичные данные
93
• e
— вещественное число в экспоненциальной форме (буква e
в нижнем регистре):
>>> print("%e %e" % (3000, 18657.81452))
3.000000e+03 1.865781e+04
•
E
— вещественное число в экспоненциальной форме (буква
E
в верхнем регистре):
>>> print("%E %E" % (3000, 18657.81452))
3.000000E+03 1.865781E+04
• g
— эквивалентно f
или e
(выбирается более короткая запись числа):
>>> print("%g %g %g" % (0.086578, 0.000086578, 1.865E-005))
0.086578 8.6578e-05 1.865e-05
•
G
— эквивалентно f
или
E
(выбирается более короткая запись числа):
>>> print("%G %G %G" % (0.086578, 0.000086578, 1.865E-005))
0.086578 8.6578E-05 1.865E-05
Если внутри строки необходимо использовать символ процента, этот символ следует удво- ить, иначе будет выведено сообщение об ошибке:
>>> print("% %s" % ("- это символ процента")) # Ошибка
Traceback (most recent call last):
File "
", line 1, in
TypeError: not all arguments converted during string formatting
>>> print("%% %s" % ("- это символ процента")) # Нормально
% — это символ процента
Форматирование строк очень удобно использовать при передаче данных в шаблон веб- страницы. Для этого заполняем словарь данными и указываем его справа от символа
%
, а сам шаблон — слева. Продемонстрируем это на примере (листинг 6.1).
Листинг 6.1. Пример использования форматирования строк
# -*- coding: utf-8 -*- html = """
1 ... 6 7 8 9 10 11 12 13 ... 83
%(h1)s
%(content)s
94
Часть I. Основы языка Python
Это заголовок первого уровня
Это основное содержание страницы
Глава 6. Строки и двоичные данные
95
>>> s = "str"
>>> "'%15s' '%-15s' '%s'" % (s, s, s.center(15))
"' str' 'str ' ' str '"
Если количество символов в строке превышает ширину поля, то значение ширины игно- рируется, и строка возвращается полностью:
>>> s = "string"
>>> s.center(6), s.center(5)
('string', 'string')
ljust(<Ширина>[, <Символ>])
— производит выравнивание строки по левому краю внутри поля указанной ширины. Если второй параметр не указан, справа от исходной строки будут добавлены пробелы. Если количество символов в строке превышает шири- ну поля, значение ширины игнорируется, и строка возвращается полностью:
>>> s = "string"
>>> s.ljust(15), s.ljust(15, "-")
('string ', 'string---------')
>>> s.ljust(6), s.ljust(5)
('string', 'string')
rjust(<Ширина>[, <Символ>])
— производит выравнивание строки по правому краю внутри поля указанной ширины. Если второй параметр не указан, слева от исходной строки будут добавлены пробелы. Если количество символов в строке превышает шири- ну поля, значение ширины игнорируется, и строка возвращается полностью:
>>> s = "string"
>>> s.rjust(15), s.rjust(15, "-")
(' string', '---------string')
>>> s.rjust(6), s.rjust(5)
('string', 'string')
zfill(<Ширина>)
— производит выравнивание фрагмента по правому краю внутри поля указанной ширины. Слева от фрагмента будут добавлены нули. Если количество симво- лов в строке превышает ширину поля, значение ширины игнорируется, и строка возвра- щается полностью:
>>> "5".zfill(20), "123456".zfill(5)
('00000000000000000005', '123456')
6.5. Метод format()
Помимо операции форматирования, мы можем использовать для этой же цели метод format()
. Он имеет следующий синтаксис:
<Строка> = <Строка специального формата>.format(*args, **kwargs)
В параметре
<Строка специального формата>
внутри символов фигурных скобок
{
и
}
ука- зываются спецификаторы, имеющие следующий синтаксис:
{[<Поле>][!<Функция>][:<Формат>]}
Все символы, расположенные вне фигурных скобок, выводятся без преобразований. Если внутри строки необходимо использовать символы
{
и
}
, то эти символы следует удвоить, иначе возбуждается исключение
ValueError
:
96
Часть I. Основы языка Python
>>> print("Символы {{ и }} — {0}".format("специальные"))
Символы { и } — специальные
В качестве параметра
<Поле>
можно указать порядковый номер (нумерация начинается с нуля) или ключ параметра, указанного в методе format()
. Допустимо комбинировать по- зиционные и именованные параметры, при этом именованные параметры следует ука- зать последними:
>>> "{0} - {1} - {2}".format(10, 12.3, "string") # Индексы '10 - 12.3 - string'
>>> arr = [10, 12.3, "string"]
>>> "{0} - {1} - {2}".format(*arr) # Индексы '10 - 12.3 - string'
>>> "{model} - {color}".format(color="red", model="BMW") # Ключи 'BMW - red'
>>> d = {"color": "red", "model": "BMW"}
>>> "{model} - {color}".format(**d) # Ключи 'BMW - red'
>>> "{color} - {0}".format(2015, color="red") # Комбинация 'red - 2015'
В вызове метода format()
можно указать список, словарь или объект. Для доступа к элементам списка по индексу внутри строки формата применяются квадратные скобки, а для доступа к элементам словаря или атрибутам объекта используется точечная нотация:
>>> arr = [10, [12.3, "string"] ]
>>> "{0[0]} - {0[1][0]} - {0[1][1]}".format(arr) # Индексы '10 - 12.3 - string'
>>> "{arr[0]} - {arr[1][1]}".format(arr=arr) # Индексы '10 - string'
>>> class Car: color, model = "red", "BMW"
>>> car = Car()
>>> "{0.model} - {0.color}".format(car) # Атрибуты 'BMW - red'
Существует также краткая форма записи, при которой
<Поле>
не указывается. В этом случае скобки без указанного индекса нумеруются слева направо, начиная с нуля:
>>> "{} - {} - {} - {n}".format(1, 2, 3, n=4) # "{0} - {1} - {2} - {n}"
'1 - 2 - 3 - 4'
>>> "{} - {} - {n} - {}".format(1, 2, 3, n=4) # "{0} - {1} - {n} - {2}"
'1 - 2 - 4 - 3'
Параметр
<Функция>
задает функцию, с помощью которой обрабатываются данные перед вставкой в строку. Если указано значение s
, то данные обрабатываются функцией str()
, если значение r
, то функцией repr()
, а если значение a
, то функцией ascii()
. Если параметр не указан, для преобразования данных в строку используется функция str()
:
>>> print("{0!s}".format("строка")) # str() строка
>>> print("{0!r}".format("строка")) # repr()
'строка'
>>> print("{0!a}".format("строка")) # ascii()
'\u0441\u0442\u0440\u043e\u043a\u0430'
Глава 6. Строки и двоичные данные
97
В параметре
<Формат>
указывается значение, имеющее следующий синтаксис:
[[<Заполнитель>]<Выравнивание>][<Знак>][#][0][<Ширина>][,]
[.<Точность>][<Преобразование>]
•
Параметр
<Ширина>
задает минимальную ширину поля. Если строка не помещается в указанную ширину, то значение игнорируется, и строка выводится полностью:
>>> "'{0:10}' '{1:3}'".format(3, "string")
"' 3' 'string'"
Ширину поля можно передать в качестве параметра в методе format()
. В этом случае вместо числа указывается индекс параметра внутри фигурных скобок:
>>> "'{0:{1}}'".format(3, 10) # 10 — это ширина поля "' 3'"
•
Параметр
<Выравнивание>
управляет выравниванием значения внутри поля. Поддер- живаются следующие значения:
<
— по левому краю;
>
— по правому краю (поведение по умолчанию);
^
— по центру поля. Пример:
>>> "'{0:<10}' '{1:>10}' '{2:^10}'".format(3, 3, 3)
"'3 ' ' 3' ' 3 '"
=
— знак числа выравнивается по левому краю, а число по правому краю:
>>> "'{0:=10}' '{1:=10}'".format(-3, 3)
"'- 3' ' 3'"
Как видно из приведенного примера, пространство между знаком и числом по умолчанию заполняется пробелами, а знак положительного числа не указывается.
Чтобы вместо пробелов пространство заполнялось нулями, необходимо указать нуль перед шириной поля:
>>> "'{0:=010}' '{1:=010}'".format(-3, 3)
"'-000000003' '0000000003'"
•
Параметр
<Заполнитель>
задает символ, которым будет заполняться свободное про- странство в поле (по умолчанию — пробел). Такого же эффекта можно достичь, ука- зав нуль в параметре
<Заполнитель>
:
>>> "'{0:0=10}' '{1:0=10}'".format(-3, 3)
"'-000000003' '0000000003'"
>>> "'{0:*<10}' '{1:+>10}' '{2:.^10}'".format(3, 3, 3)
"'3*********' '+++++++++3' '....3.....'"
•
Параметр
<Знак>
управляет выводом знака числа. Допустимые значения:
+ — задает обязательный вывод знака как для отрицательных, так и для положи- тельных чисел;
-
— вывод знака только для отрицательных чисел (значение по умолчанию);
пробел
— вставляет пробел перед положительным числом. Перед отрицательным числом будет стоять минус. Пример:
98
Часть I. Основы языка Python
>>> "'{0:+}' '{1:+}' '{0:-}' '{1:-}'".format(3, -3)
"'+3' '-3' '3' '-3'"
>>> "'{0: }' '{1: }'".format(3, -3) # Пробел "' 3' '-3'"
•
Для целых чисел в параметре
<Преобразование>
могут быть указаны следующие опции:
b
— преобразование в двоичную систему счисления:
>>> "'{0:b}' '{0:#b}'".format(3)
"'11' '0b11'"
c
— преобразование числа в соответствующий символ:
>>> "'{0:c}'".format(100)
"'d'"
d
— преобразование в десятичную систему счисления;
n
— аналогично опции d
, но учитывает настройки локали. Например, выведем большое число с разделением тысячных разрядов пробелом:
>>> import locale
>>> locale.setlocale(locale.LC_NUMERIC, 'Russian_Russia.1251')
'Russian_Russia.1251'
>>> print("{0:n}".format(100000000).replace("\uffa0", " "))
100 000 000
В Python 3 между разрядами вставляется символ с кодом
\uffa0
, который отобра- жается квадратиком. Чтобы вывести символ пробела, мы производим замену в строке с помощью метода replace()
. В Python версии 2 поведение было другим.
Там вставлялся символ с кодом
\xa0
и не нужно было производить замену. Чтобы в Python 3 вставлялся символ с кодом
\xa0
, следует воспользоваться функцией format()
из модуля locale
:
>>> import locale
>>> locale.setlocale(locale.LC_NUMERIC, "Russian_Russia.1251")
'Russian_Russia.1251'
>>> print(locale.format("%d", 100000000, grouping = True))
100 000 000
>>> locale.localeconv()["thousands_sep"]
'\xa0'
Можно также разделить тысячные разряды запятой, указав ее в строке формата:
>>> print("{0:,d}".format(100000000))
100,000,000
o
— преобразование в восьмеричную систему счисления:
>>> "'{0:d}' '{0:o}' '{0:#o}'".format(511)
"'511' '777' '0o777'"
x
— преобразование в шестнадцатеричную систему счисления в нижнем регистре:
>>> "'{0:x}' '{0:#x}'".format(255)
"'ff' '0xff'"
Глава 6. Строки и двоичные данные
99
X
— преобразование в шестнадцатеричную систему счисления в верхнем регистре:
>>> "'{0:X}' '{0:#X}'".format(255)
"'FF' '0XFF'"
•
Для вещественных чисел в параметре
<Преобразование>
могут быть указаны следую- щие опции:
f
и
F
— преобразование в десятичную систему счисления:
>>> "'{0:f}' '{1:f}' '{2:f}'".format(30, 18.6578145, -2.5)
"'30.000000' '18.657815' '-2.500000'"
По умолчанию выводимое число имеет шесть знаков после запятой. Задать другое количество знаков после запятой мы можем в параметре
<Точность>
:
>>> "'{0:.7f}' '{1:.2f}'".format(18.6578145, -2.5)
"'18.6578145' '-2.50'"
e
— вывод в экспоненциальной форме (буква e
в нижнем регистре):
>>> "'{0:e}' '{1:e}'".format(3000, 18657.81452)
"'3.000000e+03' '1.865781e+04'"
E
— вывод в экспоненциальной форме (буква
E
в верхнем регистре):
>>> "'{0:E}' '{1:E}'".format(3000, 18657.81452)
"'3.000000E+03' '1.865781E+04'"
Здесь по умолчанию количество знаков после запятой также равно шести, но мы можем указать другую величину этого параметра:
>>> "'{0:.2e}' '{1:.2E}'".format(3000, 18657.81452)
"'3.00e+03' '1.87E+04'"
g
— эквивалентно f
или e
(выбирается более короткая запись числа):
>>> "'{0:g}' '{1:g}'".format(0.086578, 0.000086578)
"'0.086578' '8.6578e-05'"
n
— аналогично опции g
, но учитывает настройки локали;
G
— эквивалентно f
или
E
(выбирается более короткая запись числа):
>>> "'{0:G}' '{1:G}'".format(0.086578, 0.000086578)
"'0.086578' '8.6578E-05'"
%
— умножает число на 100 и добавляет символ процента в конец. Значение ото- бражается в соответствии с опцией f
:
>>> "'{0:%}' '{1:.4%}'".format(0.086578, 0.000086578)
"'8.657800%' '0.0087%'"
6.5.1. Форматируемые строки
В Python 3.6 появилась весьма удобная альтернатива методу format()
— форматируемые строки.
Форматируемая строка обязательно должна предваряться буквой f
или
F
. В нужных местах такой строки записываются команды на вставку в эти места значений, хранящихся в пере-
100
Часть I. Основы языка Python менных, — точно так же, как и в строках специального формата, описанных ранее. Такие команды имеют следующий синтаксис:
{[<Переменная>][!<Функция>][:<Формат>]}
Параметр
<Переменная>
задает имя переменной, из которой будет извлечено вставляемое в строку значение. Вместо имени переменной можно записать выражение, вычисляющее значение, которое нужно вывести. Параметры
<Функция>
и
<Формат>
имеют то же назначе- ние и записываются так же, как и в случае метода format()
:
>>> a = 10; b = 12.3; s = "string"
>>> f"{a} - {b} - {s}" # Простой вывод чисел и строк '10 - 12.3 - string'
>>> f"{a} - {b:5.2f} - {s}" # Вывод с форматированием '10 - 12.30 - string'
>>> d = 3
>>> f"{a} - {b:5.{d}f} - {s}" # В командах можно использовать
# значения из переменных '10 - 12.300 - string'
>>> arr = [3, 4]
>>> f"{arr[0]} - {arr[1]}" # Вывод элементов массива '3 - 4'
>>> f"{arr[0]} - {arr[1] * 2}" # Использование выражений '3 - 8'
6.6. Функции и методы для работы со строками
Рассмотрим основные функции для работы со строками:
str([<Объект>])
— преобразует любой объект в строку. Если параметр не указан, воз- вращается пустая строка. Используется функцией print()
для вывода объектов:
>>> str(), str([1, 2]), str((3, 4)), str({"x": 1})
('', '[1, 2]', '(3, 4)', "{'x': 1}")
>>> print("строка1\nстрока2") строка1 строка2
repr(<Объект>)
— возвращает строковое представление объекта. Используется при вы- воде объектов в окне Python Shell редактора IDLE:
>>> repr("Строка"), repr([1, 2, 3]), repr({"x": 5})
("'Строка'", '[1, 2, 3]', "{'x': 5}")
>>> repr("строка1\nстрока2")
"'строка1\\nстрока2'"
ascii(<Объект>)
— возвращает строковое представление объекта. В строке могут быть символы только из кодировки ASCII:
>>> ascii([1, 2, 3]), ascii({"x": 5})
('[1, 2, 3]', "{'x': 5}")
>>> ascii("строка")
"'\\u0441\\u0442\\u0440\\u043e\\u043a\\u0430'"
Глава 6. Строки и двоичные данные
101
len(<Строка>)
— возвращает длину строки — количество символов в ней:
>>> len("Python"), len("\r\n\t"), len(r"\r\n\t")
(6, 3, 6)
>>> len("строка")
6
Приведем перечень основных методов для работы со строками:
strip([<Символы>])
— удаляет указанные в параметре символы в начале и в конце стро- ки. Если параметр не задан, удаляются пробельные символы: пробел, символ перевода строки (
\n
), символ возврата каретки (
\r
), символы горизонтальной (
\t
) и вертикальной
(
\v
) табуляции:
>>> s1, s2 = " str\n\r\v\t", "strstrstrokstrstrstr"
>>> "'%s' — '%s'" % (s1.strip(), s2.strip("tsr"))
"'str' — 'ok'"
lstrip([<Символы>])
— удаляет пробельные или указанные символы в начале строки:
>>> s1, s2 = " str ", "strstrstrokstrstrstr"
>>> "'%s' — '%s'" % (s1.lstrip(), s2.lstrip("tsr"))
"'str ' — 'okstrstrstr'"
rstrip([<Символы>])
— удаляет пробельные или указанные символы в конце строки:
>>> s1, s2 = " str ", "strstrstrokstrstrstr"
>>> "'%s' — '%s'" % (s1.rstrip(), s2.rstrip("tsr"))
"' str' — 'strstrstrok'"
split([<Разделитель>[, <Лимит>]])
— разделяет строку на подстроки по указанному разделителю и добавляет эти подстроки в список, который возвращается в качестве ре- зультата. Если первый параметр не указан или имеет значение
None
, то в качестве разде- лителя используется символ пробела. Во втором параметре можно задать количество подстрок в результирующем списке — если он не указан или равен
-1
, в список попадут все подстроки. Если подстрок больше указанного количества, то список будет содержать еще один элемент — с остатком строки:
>>> s = "word1 word2 word3"
>>> s.split(), s.split(None, 1)
(['word1', 'word2', 'word3'], ['word1', 'word2 word3'])
>>> s = "word1\nword2\nword3"
>>> s.split("\n")
['word1', 'word2', 'word3']
Если в строке содержатся несколько пробелов подряд и разделитель не указан, то пустые элементы не будут добавлены в список:
>>> s = "word1 word2 word3 "
>>> s.split()
['word1', 'word2', 'word3']
При использовании другого разделителя могут возникнуть пустые элементы:
>>> s = ",,word1,,word2,,word3,,"
>>> s.split(",")
['', '', 'word1', '', 'word2', '', 'word3', '', '']
>>> "1,,2,,3".split(",")
['1', '', '2', '', '3']
102
Часть I. Основы языка Python
Если разделитель не найден в строке, то список будет состоять из одного элемента, представляющего исходную строку:
>>> "word1 word2 word3".split("\n")
['word1 word2 word3']
rsplit([<Разделитель>[, <Лимит>]])
— аналогичен методу split()
, но поиск символа- разделителя производится не слева направо, а наоборот — справа налево:
>>> s = "word1 word2 word3"
>>> s.rsplit(), s.rsplit(None, 1)
(['word1', 'word2', 'word3'], ['word1 word2', 'word3'])
>>> "word1\nword2\nword3".rsplit("\n")
['word1', 'word2', 'word3']
splitlines([False])
— разделяет строку на подстроки по символу перевода строки (
\n
) и добавляет их в список. Символы новой строки включаются в результат, только если необязательный параметр имеет значение
True
. Если разделитель не найден в строке, список будет содержать только один элемент:
>>> "word1\nword2\nword3".splitlines()
['word1', 'word2', 'word3']
>>> "word1\nword2\nword3".splitlines(True)
['word1\n', 'word2\n', 'word3']
>>> "word1\nword2\nword3".splitlines(False)
['word1', 'word2', 'word3']
>>> "word1 word2 word3".splitlines()
['word1 word2 word3']
partition(<Разделитель>)
— находит первое вхождение символа-разделителя в строку и возвращает кортеж из трех элементов: первый элемент будет содержать фрагмент, рас- положенный перед разделителем, второй элемент — сам разделитель, а третий эле- мент — фрагмент, расположенный после разделителя. Поиск производится слева напра- во. Если символ-разделитель не найден, то первый элемент кортежа будет содержать всю строку, а остальные элементы останутся пустыми:
>>> "word1 word2 word3".partition(" ")
('word1', ' ', 'word2 word3')
>>> "word1 word2 word3".partition("\n")
('word1 word2 word3', '', '')
rpartition(<Разделитель>)
— метод аналогичен методу partition()
, но поиск символа- разделителя производится не слева направо, а наоборот — справа налево. Если символ- разделитель не найден, то первые два элемента кортежа окажутся пустыми, а третий элемент будет содержать всю строку:
>>> "word1 word2 word3".rpartition(" ")
('word1 word2', ' ', 'word3')
>>> "word1 word2 word3".rpartition("\n")
('', '', 'word1 word2 word3')
join()
— преобразует последовательность в строку. Элементы добавляются через ука- занный разделитель. Формат метода:
<Строка> = <Разделитель>.join(<Последовательность>)
Глава 6. Строки и двоичные данные
103
В качестве примера преобразуем список и кортеж в строку:
>>> " => ".join(["word1", "word2", "word3"])
'word1 => word2 => word3'
>>> " ".join(("word1", "word2", "word3"))
'word1 word2 word3'
Обратите внимание на то, что элементы последовательностей должны быть строками, иначе возбуждается исключение
TypeError
:
>>> " ".join(("word1", "word2", 5))
Traceback (most recent call last):
File "
", line 1, in
" ".join(("word1", "word2", 5))
TypeError: sequence item 2: expected str instance, int found
Как вы уже знаете, строки относятся к неизменяемым типам данных. Если попытаться из- менить символ по индексу, возникнет ошибка. Чтобы изменить символ по индексу, можно преобразовать строку в список с помощью функции list()
, произвести изменения, а затем с помощью метода join()
преобразовать список обратно в строку:
>>> s = "Python"
>>> arr = list(s); arr # Преобразуем строку в список
['P', 'y', 't', 'h', 'o', 'n']
>>> arr[0] = "J"; arr # Изменяем элемент по индексу
['J', 'y', 't', 'h', 'o', 'n']
>>> s = "".join(arr); s # Преобразуем список в строку 'Jython'
В Python 3 также можно преобразовать строку в тип bytearray
, а затем изменить символ по индексу:
>>> s = "Python"
>>> b = bytearray(s, "cp1251"); b bytearray(b'Python')
>>> b[0] = ord("J"); b bytearray(b'Jython')
>>> s = b.decode("cp1251"); s
'Jython'
6.7. Настройка локали
Для установки локали (совокупности локальных настроек системы) служит функция setlocale()
из модуля locale
. Прежде чем использовать функцию, необходимо подклю- чить модуль с помощью выражения: import locale
Функция setlocale()
имеет следующий формат: setlocale(<Категория>[, <Локаль>]);
Параметр
<Категория>
может принимать следующие значения:
locale.LC_ALL
— устанавливает локаль для всех режимов;
locale.LC_COLLATE
— для сравнения строк;
104
Часть I. Основы языка Python
locale.LC_CTYPE
— для перевода символов в нижний или верхний регистр;
locale.LC_MONETARY
— для отображения денежных единиц;
locale.LC_NUMERIC
— для форматирования чисел;
locale.LC_TIME
— для форматирования вывода даты и времени.
Получить текущее значение локали позволяет функция getlocale([<Категория>])
. В ка- честве примера настроим локаль под Windows вначале на кодировку Windows-1251, потом на кодировку UTF-8, а затем на кодировку по умолчанию. Далее выведем текущее значение локали для всех категорий и только для locale.LC_COLLATE
:
>>> import locale
>>> # Для кодировки windows-1251
>>> locale.setlocale(locale.LC_ALL, "Russian_Russia.1251")
'Russian_Russia.1251'
>>> # Устанавливаем локаль по умолчанию
>>> locale.setlocale(locale.LC_ALL, "")
'Russian_Russia.1251'
>>> # Получаем текущее значение локали для всех категорий
>>> locale.getlocale()
('Russian_Russia', '1251')
>>> # Получаем текущее значение категории locale.LC_COLLATE
>>> locale.getlocale(locale.LC_COLLATE)
('Russian_Russia', '1251')
Получить настройки локали позволяет функция localeconv()
. Функция возвращает словарь с настройками. Результат выполнения функции для локали
Russian_Russia.1251
выглядит следующим образом:
>>> locale.localeconv()
{'decimal_point': ',', 'thousands_sep': '\xa0', 'grouping': [3, 0],
'int_curr_symbol': 'RUB', 'currency_symbol': '?', 'mon_decimal_point': ',',
'mon_thousands_sep': '\xa0', 'mon_grouping': [3, 0], 'positive_sign': '',
'negative_sign': '-', 'int_frac_digits': 2, 'frac_digits': 2, 'p_cs_precedes': 0,
'p_sep_by_space': 1, 'n_cs_precedes': 0, 'n_sep_by_space': 1, 'p_sign_posn': 1,
'n_sign_posn': 1}
6.8. Изменение регистра символов
Для изменения регистра символов предназначены следующие методы:
upper()
— заменяет все символы строки соответствующими прописными буквами:
>>> print("строка".upper())
СТРОКА
lower()
— заменяет все символы строки соответствующими строчными буквами:
>>> print("СТРОКА".lower()) строка
swapcase()
— заменяет все строчные символы соответствующими прописными буквами, а все прописные символы — строчными:
Глава 6. Строки и двоичные данные
105
>>> print("СТРОКА строка".swapcase()) строка СТРОКА
capitalize()
— делает первую букву строки прописной:
>>> print("строка строка".capitalize())
Строка строка
title()
— делает первую букву каждого слова прописной:
>>> s = "первая буква каждого слова станет прописной"
>>> print(s.title())
Первая Буква Каждого Слова Станет Прописной
casefold()
— то же самое, что и lower()
, но дополнительно преобразует все символы с диакритическими знаками и лигатуры в буквы стандартной латиницы. Обычно приме- няется для сравнения строк:
>>> "Python".casefold() == "python".casefold()
True
>>> "grosse".casefold() == "große".casefold()
True
6.9. Функции для работы с символами
Для работы с отдельными символами предназначены следующие функции:
chr(<Код символа>)
— возвращает символ по указанному коду:
>>> print(chr(1055))
П
ord(<Символ>)
— возвращает код указанного символа:
>>> print(ord("П"))
1055 6.10. Поиск и замена в строке
Для поиска и замены в строке используются следующие методы:
find()
— ищет подстроку в строке. Возвращает номер позиции, с которой начинается вхождение подстроки в строку. Если подстрока в строку не входит, то возвращается зна- чение
-1
. Метод зависит от регистра символов. Формат метода:
<Строка>.find(<Подстрока>[, <Начало>[, <Конец>]])
Если начальная позиция не указана, то поиск будет осуществляться с начала строки.
Если параметры
<Начало>
и
<Конец>
указаны, то производится операция извлечения среза:
<Строка>[<Начало>:<Конец>] и поиск подстроки будет выполняться в этом фрагменте:
>>> s = "пример пример Пример"
>>> s.find("при"), s.find("При"), s.find("тест")
(0, 14, -1)
>>> s.find("при", 9), s.find("при", 0, 6), s.find("при", 7, 12)
(-1, 0, 7)
106
Часть I. Основы языка Python
index()
— метод аналогичен методу find()
, но если подстрока в строку не входит, воз- буждается исключение
ValueError
. Формат метода:
<Строка>.index(<Подстрока>[, <Начало>[, <Конец>]])
Пример:
>>> s = "пример пример Пример"
>>> s.index("при"), s.index("при", 7, 12), s.index("При", 1)
(0, 7, 14)
>>> s.index("тест")
Traceback (most recent call last):
File "
", line 1, in
ValueError: substring not found
rfind()
— ищет подстроку в строке. Возвращает позицию последнего вхождения под- строки в строку. Если подстрока в строку не входит, возвращается значение
-1
. Метод зависит от регистра символов. Формат метода:
<Строка>.rfind(<Подстрока>[, <Начало>[, <Конец>]])
Если начальная позиция не указана, то поиск будет производиться с начала строки. Если параметры
<Начало>
и
<Конец>
указаны, то выполняется операция извлечения среза, и поиск подстроки будет производиться в этом фрагменте:
>>> s = "пример пример Пример Пример"
>>> s.rfind("при"), s.rfind("При"), s.rfind("тест")
(7, 21, -1)
>>> s.find("при", 0, 6), s.find("При", 10, 20)
(0, 14)
rindex()
— метод аналогичен методу rfind()
, но если подстрока в строку не входит, возбуждается исключение
ValueError
. Формат метода:
<Строка>.rindex(<Подстрока>[, <Начало>[, <Конец>]])
Пример:
>>> s = "пример пример Пример Пример"
>>> s.rindex("при"), s.rindex("При"), s.rindex("при", 0, 6)
(7, 21, 0)
>>> s.rindex("тест")
Traceback (most recent call last):
File "
", line 1, in
ValueError: substring not found
count()
— возвращает число вхождений подстроки в строку. Если подстрока в строку не входит, возвращается значение
0
. Метод зависит от регистра символов. Формат метода:
<Строка>.count(<Подстрока>[, <Начало>[, <Конец>]])
Пример:
>>> s = "пример пример Пример Пример"
>>> s.count("при"), s.count("при", 6), s.count("При")
(2, 1, 2)
Глава 6. Строки и двоичные данные
107
>>> s.count("тест")
0
startswith()
— проверяет, начинается ли строка с указанной подстроки. Если начинает- ся, возвращается значение
True
, в противном случае —
False
. Метод зависит от регистра символов. Формат метода:
<Строка>.startswith(<Подстрока>[, <Начало>[, <Конец>]])
Если начальная позиция не указана, сравнение будет производиться с началом строки.
Если параметры
<Начало>
и
<Конец>
указаны, выполняется операция извлечения среза, и сравнение будет производиться с началом фрагмента:
>>> s = "пример пример Пример Пример"
>>> s.startswith("при"), s.startswith("При")
(True, False)
>>> s.startswith("при", 6), s.startswith("При", 14)
(False, True)
Начиная с Python версии 2.5, параметр
<Подстрока>
может быть кортежем:
>>> s = "пример пример Пример Пример"
>>> s.startswith(("при", "При"))
True
endswith()
— проверяет, заканчивается ли строка указанной подстрокой. Если заканчи- вается, то возвращается значение
True
, в противном случае —
False
. Метод зависит от регистра символов. Формат метода:
<Строка>.endswith(<Подстрока>[, <Начало>[, <Конец>]])
Если начальная позиция не указана, то сравнение будет производиться с концом строки.
Если параметры
<Начало>
и
<Конец>
указаны, то выполняется операция извлечения среза, и сравнение будет производиться с концом фрагмента:
>>> s = "подстрока ПОДСТРОКА"
>>> s.endswith("ока"), s.endswith("ОКА")
(False, True)
>>> s.endswith("ока", 0, 9)
True
Начиная с Python версии 2.5, параметр
<Подстрока>
может быть кортежем:
>>> s = "подстрока ПОДСТРОКА"
>>> s.endswith(("ока", "ОКА"))
True
replace()
— производит замену всех вхождений заданной подстроки в строке на другую подстроку и возвращает результат в виде новой строки. Метод зависит от регистра сим- волов. Формат метода:
<Строка>.replace(<Подстрока для замены>, <Новая подстрока>[,
<Максимальное количество замен>])
Если количество замен не указано, будет выполнена замена всех найденных подстрок:
>>> s = "Привет, Петя"
>>> print(s.replace("Петя", "Вася"))
Привет, Вася
108
Часть I. Основы языка Python
>>> print(s.replace("петя", "вася")) # Зависит от регистра
Привет, Петя
>>> s = "strstrstrstrstr"
>>> s.replace("str", ""), s.replace("str", "", 3)
('', 'strstr')
translate(<Таблица символов>)
— заменяет символы в соответствии с параметром
<Таблица символов>
. Параметр
<Таблица символов>
должен быть словарем, ключами которого являются Unicode-коды заменяемых символов, а значениями — Unicode-коды заменяющих символов. Если в качестве значения указать
None
, то символ будет удален.
Для примера удалим букву
П
, а также изменим регистр всех букв р
:
>>> s = "Пример"
>>> d = {ord("П"): None, ord("р"): ord("Р")}
>>> d
{1088: 1056, 1055: None}
>>> s.translate(d)
'РимеР'
Упростить создание параметра
<Таблица символов>
позволяет статический метод maketrans()
. Формат метода: str.maketrans(
Если указан только первый параметр, то он должен быть словарем:
>>> t = str.maketrans({"а": "А", "о": "О", "с": None})
>>> t
{1072: 'А', 1089: None, 1086: 'О'}
>>> "строка".translate(t)
'трОкА'
Если указаны два первых параметра, то они должны быть строками одинаковой длины.
В результате будет создан словарь с ключами из строки
и значениями из строки
, расположенными в той же позиции. Изменим регистр некоторых символов:
>>> t = str.maketrans("абвгдежзи", "АБВГДЕЖЗИ")
>>> t
{1072: 1040, 1073: 1041, 1074: 1042, 1075: 1043, 1076: 1044,
1077: 1045, 1078: 1046, 1079: 1047, 1080: 1048}
>>> "абвгдежзи".translate(t)
'АБВГДЕЖЗИ'
В третьем параметре можно дополнительно указать строку из символов, которым будет сопоставлено значение
None
. После выполнения метода translate()
эти символы будут удалены из строки. Заменим все цифры на
0
, а некоторые буквы удалим из строки:
>>> t = str.maketrans("123456789", "0" * 9, "str")
>>> t
{116: None, 115: None, 114: None, 49: 48, 50: 48, 51: 48,
52: 48, 53: 48, 54: 48, 55: 48, 56: 48, 57: 48}
>>> "str123456789str".translate(t)
'000000000'
Глава 6. Строки и двоичные данные
109 6.11. Проверка типа содержимого строки
Для проверки типа содержимого предназначены следующие методы:
isalnum()
— возвращает
True
, если строка содержит только буквы и (или) цифры, в про- тивном случае —
False
. Если строка пустая, возвращается значение
False
:
>>> "0123".isalnum(), "123abc".isalnum(), "abc123".isalnum()
(True, True, True)
>>> "строка".isalnum()
True
>>> "".isalnum(), "123 abc".isalnum(), "abc, 123.".isalnum()
(False, False, False)
isalpha()
— возвращает
True
, если строка содержит только буквы, в противном слу- чае —
False
. Если строка пустая, возвращается значение
False
:
>>> "string".isalpha(), "строка".isalpha(), "".isalpha()
(True, True, False)
>>> "123abc".isalpha(), "str str".isalpha(), "st,st".isalpha()
(False, False, False)
isdigit()
— возвращает
True
, если строка содержит только цифры, в противном слу- чае —
False
:
>>> "0123".isdigit(), "123abc".isdigit(), "abc123".isdigit()
(True, False, False)
isdecimal()
— возвращает
True
, если строка содержит только десятичные символы, в противном случае —
False
. К десятичным символам относятся десятичные цифры в кодировке ASCII, а также надстрочные и подстрочные десятичные цифры в других языках:
>>> "123".isdecimal(), "123стр".isdecimal()
(True, False)
isnumeric()
— возвращает
True
, если строка содержит только числовые символы, в про- тивном случае —
False
. К числовым символам относятся десятичные цифры в кодиров- ке ASCII, символы римских чисел, дробные числа и др.:
>>> "\u2155".isnumeric(), "\u2155".isdigit()
(True, False)
>>> print("\u2155") # Выведет символ "1/5"
isupper()
— возвращает
True
, если строка содержит буквы только верхнего регистра, в противном случае —
False
:
>>> "STRING".isupper(), "СТРОКА".isupper(), "".isupper()
(True, True, False)
>>> "STRING1".isupper(), "СТРОКА, 123".isupper(), "123".isupper()
(True, True, False)
>>> "string".isupper(), "STRing".isupper()
(False, False)
islower()
— возвращает
True
, если строка содержит буквы только нижнего регистра, в противном случае —
False
:
110
Часть I. Основы языка Python
>>> "srting".islower(), "строка".islower(), "".islower()
(True, True, False)
>>> "string1".islower(), "str, 123".islower(), "123".islower()
(True, True, False)
>>> "STRING".islower(), "Строка".islower()
(False, False)
istitle()
— возвращает
True
, если все слова в строке начинаются с заглавной буквы, в противном случае —
False
. Если строка пустая, также возвращается
False
:
>>> "Str Str".istitle(), "Стр Стр".istitle()
(True, True)
>>> "Str Str 123".istitle(), "Стр Стр 123".istitle()
(True, True)
>>> "Str str".istitle(), "Стр стр".istitle()
(False, False)
>>> "".istitle(), "123".istitle()
(False, False)
isprintable()
— возвращает
True
, если строка содержит только печатаемые символы, в противном случае —
False
. Отметим, что пробел относится к печатаемым символам:
>>> "123".isprintable()
True
>>> "PHP Python".isprintable()
True
>>> "\n".isprintable()
False
isspace()
— возвращает
True
, если строка содержит только пробельные символы, в про- тивном случае —
False
:
>>> "".isspace(), " \n\r\t".isspace(), "str str".isspace()
(False, True, False)
isidentifier()
— возвращает
True
, если строка представляет собой допустимое с точки зрения Python имя переменной, функции или класса, в противном случае —
False
:
>>> "s".isidentifier()
True
>>> "func".isidentifier()
True
>>> "123func".isidentifier()
False
Следует иметь в виду, что метод isidentifier()
лишь проверяет, удовлетворяет ли за- данное имя правилам языка. Он не проверяет, совпадает ли это имя с ключевым словом
Python. Для этого надлежит применять функцию iskeyword()
, объявленную в модуле keyword
, которая возвращает
True
, если переданная ей строка совпадает с одним из клю- чевых слов:
>>> keyword.iskeyword("else")
True
>>> keyword.iskeyword("elsewhere")
False
Глава 6. Строки и двоичные данные
111
Переделаем нашу программу суммирования произвольного количества целых чисел, вве- денных пользователем (см. листинг 4.12), таким образом, чтобы при вводе строки вместо числа программа не завершалась с фатальной ошибкой. Кроме того, предусмотрим возмож- ность ввода отрицательных целых чисел (листинг 6.2).
Листинг 6.2. Суммирование неопределенного количества чисел
# -*- coding: utf-8 -*- print("Введите слово 'stop' для получения результата") summa = 0 while True: x = input("Введите число: ") if x == "stop": break # Выход из цикла if x == "": print("Вы не ввели значение!") continue if x[0] == "-": # Если первым символом является минус if not x[1:].isdigit(): # Если фрагмент не состоит из цифр print("Необходимо ввести число, а не строку!") continue else: # Если минуса нет, то проверяем всю строку if not x.isdigit(): # Если строка не состоит из цифр print("Необходимо ввести число, а не строку!") continue x = int(x) # Преобразуем строку в число summa += x print("Сумма чисел равна:", summa) input()
Процесс ввода значений и получения результата выглядит так (значения, введенные поль- зователем, выделены здесь полужирным шрифтом):
Введите слово 'stop' для получения результата
Введите число:
10
Введите число:
Вы не ввели значение!
Введите число: str
Необходимо ввести число, а не строку!
Введите число:
-5
Введите число:
-str
Необходимо ввести число, а не строку!
Введите число: stop
Сумма чисел равна: 5 6.12. Тип данных bytes
Тип данных str отлично подходит для хранения текста, но что делать, если необходимо обрабатывать изображения? Ведь изображение не имеет кодировки, а значит, оно не может быть преобразовано в Unicode-строку. Для решения этой проблемы в Python 3 были введе-
112
Часть I. Основы языка Python ны типы bytes и bytearray
, которые позволяют хранить последовательность целых чисел в диапазоне от
0
до
255
, т. е. байтов. Тип данных bytes относится к неизменяемым типам, как и строки, а тип данных bytearray
— к изменяемым, как и списки.
Создать объект типа bytes можно следующими способами:
с помощью функции bytes([<Строка>, <Кодировка>[, <Обработка ошибок>]])
. Если параметры не указаны, то возвращается пустой объект. Чтобы преобразовать строку в объект типа bytes
, необходимо передать минимум два первых параметра. Если строка указана только в первом параметре, то возбуждается исключение
TypeError
:
>>> bytes() b''
>>> bytes("строка", "cp1251") b'\xf1\xf2\xf0\xee\xea\xe0'
>>> bytes("строка")
Traceback (most recent call last):
File "
", line 1, in
TypeError: string argument without an encoding
В третьем параметре могут быть указаны значения "strict"
(при ошибке возбуждается исключение
UnicodeEncodeError
— значение по умолчанию),
"replace"
(неизвестный символ заменяется символом вопроса) или "ignore"
(неизвестные символы игнориру- ются):
>>> bytes("string\uFFFD", "cp1251", "strict")
Traceback (most recent call last):
File "
", line 1, in
File "C:\Python36\lib\encodings\cp1251.py", line 12, in encode return codecs.charmap_encode(input,errors,encoding_table)
UnicodeEncodeError: 'charmap' codec can't encode character
'\ufffd' in position 6: character maps to
>>> bytes("string\uFFFD", "cp1251", "replace") b'string?'
>>> bytes("string\uFFFD", "cp1251", "ignore") b'string'
с помощью строкового метода encode([encoding="utf-8"][, errors="strict"])
. Если кодировка не указана, строка преобразуется в последовательность байтов в кодировке
UTF-8. В параметре errors могут быть указаны значения "strict"
(значение по умолча- нию),
"replace"
,
"ignore"
,
"xmlcharrefreplace" или "backslashreplace"
:
>>> "строка".encode() b'\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xba\xd0\xb0'
>>> "строка".encode(encoding="cp1251") b'\xf1\xf2\xf0\xee\xea\xe0'
>>> "строка\uFFFD".encode(encoding="cp1251", errors="xmlcharrefreplace") b'\xf1\xf2\xf0\xee\xea\xe0�'
>>> "строка\uFFFD".encode(encoding="cp1251", errors="backslashreplace") b'\xf1\xf2\xf0\xee\xea\xe0\\ufffd'
Глава 6. Строки и двоичные данные
113
указав букву b
(регистр не имеет значения) перед строкой в апострофах, кавычках, трой- ных апострофах или тройных кавычках. Обратите внимание на то, что в строке могут быть только символы с кодами, входящими в кодировку ASCII. Все остальные символы должны быть представлены специальными последовательностями:
>>> b"string", b'string', b"""string""", b'''string'''
(b'string', b'string', b'string', b'string')
>>> b"строка"
SyntaxError: bytes can only contain ASCII literal characters.
>>> b"\xf1\xf2\xf0\xee\xea\xe0" b'\xf1\xf2\xf0\xee\xea\xe0'
с помощью функции bytes(<Последовательность>)
, которая преобразует последователь- ность целых чисел от
0
до
255
в объект типа bytes
. Если число не попадает в диапазон, возбуждается исключение
ValueError
:
>>> b = bytes([225, 226, 224, 174, 170, 160])
>>> b b'\xe1\xe2\xe0\xae\xaa\xa0'
>>> str(b, "cp866")
'строка'
с помощью функции bytes(<Число>)
, которая задает количество элементов в последова- тельности. Каждый элемент будет содержать нулевой символ:
>>> bytes(10) b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
с помощью метода bytes.fromhex(<Строка>)
. Строка в этом случае должна содержать шестнадцатеричные значения символов:
>>> b = bytes.fromhex(" e1 e2e0ae aaa0 ")
>>> b b'\xe1\xe2\xe0\xae\xaa\xa0'
>>> str(b, "cp866")
'строка'
Объекты типа bytes относятся к последовательностям. Каждый элемент такой последова- тельности может хранить целое число от
0
до
255
, которое обозначает код символа. Как и все последовательности, объекты поддерживают обращение к элементу по индексу, полу- чение среза, конкатенацию, повторение и проверку на вхождение:
>>> b = bytes("string", "cp1251")
>>> b b'string'
>>> b[0] # Обращение по индексу
115
>>> b[1:3] # Получение среза b'tr'
>>> b + b"123" # Конкатенация b'string123'
>>> b * 3 # Повторение b'stringstringstring'
>>> 115 in b, b"tr" in b, b"as" in b
(True, True, False)
114
Часть I. Основы языка Python
Как видно из примера, при выводе объекта целиком, а также при извлечении среза, произ- водится попытка отображения символов. Однако доступ по индексу возвращает целое чис- ло, а не символ. Если преобразовать объект в список, то мы получим последовательность целых чисел:
>>> list(bytes("string", "cp1251"))
[115, 116, 114, 105, 110, 103]
Тип bytes относится к неизменяемым типам. Это означает, что можно получить значение по индексу, но изменить его нельзя:
>>> b = bytes("string", "cp1251")
>>> b[0] = 168
Traceback (most recent call last):
File "
", line 1, in b[0] = 168
TypeError: 'bytes' object does not support item assignment
Объекты типа bytes поддерживают большинство строковых методов, которые мы рассмат- ривали в предыдущих разделах. Однако некоторые из этих методов могут некорректно ра- ботать с русскими буквами — в этих случаях следует использовать тип str
, а не тип bytes
Не поддерживаются объектами типа bytes строковые методы encode()
, isidentifier()
, isprintable()
, isnumeric()
, isdecimal()
, format_map()
и format()
, а также операция фор- матирования.
При использовании методов следует учитывать, что в параметрах нужно указывать объекты типа bytes
, а не строки:
>>> b = bytes("string", "cp1251")
>>> b.replace(b"s", b"S") b'String'
Необходимо также помнить, что смешивать строки и объекты типа bytes в выражениях нельзя. Предварительно необходимо явно преобразовать объекты к одному типу, а лишь затем производить операцию:
>>> b"string" + "string"
Traceback (most recent call last):
File "
", line 1, in b"string" + "string"
TypeError: can't concat bytes to str
>>> b"string" + "string".encode("ascii") b'stringstring'
Объект типа bytes может содержать как однобайтовые, так и многобайтовые символы. При использовании многобайтовых символов некоторые функции могут работать не так, как предполагалось, — например, функция len()
вернет количество байтов, а не символов:
>>> len("строка")
6
>>> len(bytes("строка", "cp1251"))
6
>>> len(bytes("строка", "utf-8"))
12
Глава 6. Строки и двоичные данные
115
Преобразовать объект типа bytes в строку позволяет метод decode()
. Метод имеет следую- щий формат: decode([encoding="utf-8"][, errors="strict"])
Параметр encoding задает кодировку символов (по умолчанию UTF-8) в объекте bytes
, а параметр errors
— способ обработки ошибок при преобразовании. В параметре errors можно указать значения "strict"
(значение по умолчанию),
"replace"
или "ignore"
. При- мер преобразования:
>>> b = bytes("строка", "cp1251")
>>> b.decode(encoding="cp1251"), b.decode("cp1251")
('строка', 'строка')
Для преобразования можно также воспользоваться функцией str()
:
>>> b = bytes("строка", "cp1251")
>>> str(b, "cp1251")
'строка'
Чтобы изменить кодировку данных, следует сначала преобразовать тип bytes в строку, а затем произвести обратное преобразование, указав нужную кодировку. Преобразуем дан- ные из кодировки Windows-1251 в кодировку KOI8-R, а затем обратно:
>>> w = bytes("Строка", "cp1251") # Данные в кодировке windows-1251
>>> k = w.decode("cp1251").encode("koi8-r")
>>> k, str(k, "koi8-r") # Данные в кодировке KOI8-R
(b'\xf3\xd4\xd2\xcf\xcb\xc1', 'Строка')
>>> w = k.decode("koi8-r").encode("cp1251")
>>> w, str(w, "cp1251") # Данные в кодировке windows-1251
(b'\xd1\xf2\xf0\xee\xea\xe0', 'Строка')
В Python 3.5 появились два полезных инструмента для работы с типом данных bytes
. Во- первых, теперь можно форматировать такие данные с применением описанного в разд. 6.4 оператора
%
:
>>> b"%i - %i - %f" % (10, 20, 30) b'10 - 20 - 30.000000'
Однако здесь нужно помнить, что тип преобразования s
(т. е. вывод в виде Unicode-строки) в этом случае не поддерживается, и его использование приведет к возбуждению исключе- ния
TypeError
:
>>> b"%s - %s - %s" % (10, 20, 30)
Traceback (most recent call last):
File "
", line 1, in b"%s - %s - %s" % (10, 20, 30)
TypeError: %b requires a bytes-like object, or an object that implements __bytes__, not 'int'
Во-вторых, тип bytes получил поддержку метода hex()
, который возвращает строку с шест- надцатеричным представлением значения:
>>> b"string".hex()
'737472696e67'
116
Часть I. Основы языка Python
6.13. Тип данных bytearray
Тип данных bytearray является разновидностью типа bytes и поддерживает те же самые методы и операции (включая оператор форматирования
%
и метод hex()
, описанные ранее).
В отличие от типа bytes
, тип bytearray допускает возможность непосредственного измене- ния объекта и содержит дополнительные методы, позволяющие выполнять эти изменения.
Создать объект типа bytearray можно следующими способами:
с помощью функции bytearray([<Строка>, <Кодировка>[, <Обработка ошибок>]])
. Если параметры не указаны, то возвращается пустой объект. Чтобы преобразовать строку в объект типа bytearray
, необходимо передать минимум два первых параметра. Если строка указана только в первом параметре, то возбуждается исключение
TypeError
:
>>> bytearray() bytearray(b'')
>>> bytearray("строка", "cp1251") bytearray(b'\xf1\xf2\xf0\xee\xea\xe0')
>>> bytearray("строка")
Traceback (most recent call last):
File "
", line 1, in bytearray("строка")
TypeError: string argument without an encoding
В третьем параметре могут быть указаны значения "strict"
(при ошибке возбуждается исключение
UnicodeEncodeError
— значение по умолчанию),
"replace"
(неизвестный символ заменяется символом вопроса) или "ignore"
(неизвестные символы игнорируются):
>>> bytearray("string\uFFFD", "cp1251", "strict")
Traceback (most recent call last):
File "
", line 1, in bytearray("string\uFFFD", "cp1251", "strict")
File "C:\Python36\lib\encodings\cp1251.py", line 12, in encode return codecs.charmap_encode(input,errors,encoding_table)
UnicodeEncodeError: 'charmap' codec can't encode character
'\ufffd' in position 6: character maps to
>>> bytearray("string\uFFFD", "cp1251", "replace") bytearray(b'string?')
>>> bytearray("string\uFFFD", "cp1251", "ignore") bytearray(b'string')
с помощью функции bytearray(<Последовательность>)
, которая преобразует последова- тельность целых чисел от
0
до
255
в объект типа bytearray
. Если число не попадает в диапазон, возбуждается исключение
ValueError
:
>>> b = bytearray([225, 226, 224, 174, 170, 160])
>>> b bytearray(b'\xe1\xe2\xe0\xae\xaa\xa0')
>>> bytearray(b'\xe1\xe2\xe0\xae\xaa\xa0') bytearray(b'\xe1\xe2\xe0\xae\xaa\xa0')
>>> str(b, "cp866")
'строка'
с помощью функции bytearray(<Число>)
, которая задает количество элементов в после- довательности. Каждый элемент будет содержать нулевой символ:
Глава 6. Строки и двоичные данные
117
>>> bytearray(5) bytearray(b'\x00\x00\x00\x00\x00')
с помощью метода bytearray.fromhex(<Строка>)
. Строка в этом случае должна содер- жать шестнадцатеричные значения символов:
>>> b = bytearray.fromhex(" e1 e2e0ae aaa0 ")
>>> b bytearray(b'\xe1\xe2\xe0\xae\xaa\xa0')
>>> str(b, "cp866")
'строка'
Тип bytearray относится к изменяемым типам. Поэтому можно не только получить значе- ние по индексу, но и изменить его (что не свойственно строкам):
>>> b = bytearray("Python", "ascii")
>>> b[0] # Можем получить значение
80
>>> b[0] = b"J"[0] # Можем изменить значение
>>> b bytearray(b'Jython')
При изменении значения важно помнить, что присваиваемое значение должно быть целым числом в диапазоне от
0
до
255
. Чтобы получить число в предыдущем примере, мы создали объект типа bytes
, а затем присвоили значение, расположенное по индексу
0
(
b[0] = b"J"[0]
). Можно, конечно, сразу указать код символа, но ведь держать все коды символов в памяти свойственно компьютеру, а не человеку.
Для изменения объекта можно также использовать следующие методы:
append(<Число>)
— добавляет один элемент в конец объекта. Метод изменяет текущий объект и ничего не возвращает:
>>> b = bytearray("string", "ascii")
>>> b.append(b"1"[0]); b bytearray(b'string1')
extend(<Последовательность>)
— добавляет элементы последовательности в конец объекта. Метод изменяет текущий объект и ничего не возвращает:
>>> b = bytearray("string", "ascii")
>>> b.extend(b"123"); b bytearray(b'string123')
Добавить несколько элементов можно с помощью операторов
+
и
+=
:
>>> b = bytearray("string", "ascii")
>>> b + b"123" # Возвращает новый объект bytearray(b'string123')
>>> b += b"456"; b # Изменяет текущий объект bytearray(b'string456')
Кроме того, можно воспользоваться операцией присваивания значения срезу:
>>> b = bytearray("string", "ascii")
>>> b[len(b):] = b"123" # Добавляем элементы в последовательность
>>> b bytearray(b'string123')
118
Часть I. Основы языка Python
insert(<Индекс>, <Число>)
— добавляет один элемент в указанную позицию. Осталь- ные элементы смещаются. Метод изменяет текущий объект и ничего не возвращает. До- бавим элемент в начало объекта:
>>> b = bytearray("string", "ascii")
>>> b.insert(0, b"1"[0]); b bytearray(b'1string')
Метод insert()
позволяет добавить только один элемент. Чтобы добавить несколько элементов, можно воспользоваться операцией присваивания значения срезу. Добавим несколько элементов в начало объекта:
>>> b = bytearray("string", "ascii")
>>> b[:0] = b"123"; b bytearray(b'123string')
pop([<Индекс>])
— удаляет элемент, расположенный по указанному индексу, и воз- вращает его. Если индекс не указан, удаляет и возвращает последний элемент:
>>> b = bytearray("string", "ascii")
>>> b.pop() # Удаляем последний элемент
103
>>> b bytearray(b'strin')
>>> b.pop(0) # Удаляем первый элемент
115
>>> b bytearray(b'trin')
Удалить элемент списка позволяет также оператор del
:
>>> b = bytearray("string", "ascii")
>>> del b[5] # Удаляем последний элемент
>>> b bytearray(b'strin')
>>> del b[:2] # Удаляем первый и второй элементы
>>> b bytearray(b'rin')
remove(<Число>)
— удаляет первый элемент, содержащий указанное значение. Если элемент не найден, возбуждается исключение
ValueError
. Метод изменяет текущий объ- ект и ничего не возвращает:
>>> b = bytearray("strstr", "ascii")
>>> b.remove(b"s"[0]) # Удаляет только первый элемент
>>> b bytearray(b'trstr')
reverse()
— изменяет порядок следования элементов на противоположный. Метод изменяет текущий объект и ничего не возвращает:
>>> b = bytearray("string", "ascii")
>>> b.reverse(); b bytearray(b'gnirts')
Преобразовать объект типа bytearray в строку позволяет метод decode()
. Метод имеет сле- дующий формат: decode([encoding="utf-8"][, errors="strict"])
Глава 6. Строки и двоичные данные
119
Параметр encoding задает кодировку символов (по умолчанию UTF-8) в объекте bytearray
, а параметр errors
— способ обработки ошибок при преобразовании. В параметре errors можно указать значения "strict"
(значение по умолчанию), "
replace"
или "ignore"
. При- мер преобразования:
>>> b = bytearray("строка", "cp1251")
>>> b.decode(encoding="cp1251"), b.decode("cp1251")
('строка', 'строка')
Для преобразования можно также воспользоваться функцией str()
:
>>> b = bytearray("строка", "cp1251")
>>> str(b, "cp1251")
'строка'
6.14. Преобразование объекта в последовательность байтов
Преобразовать объект в последовательность байтов (выполнить его сериализацию), а затем восстановить (десериализовать) объект позволяет модуль pickle
. Прежде чем использовать функции из этого модуля, необходимо подключить модуль с помощью инструкции: import pickle
Для преобразования предназначены две функции:
dumps(<Объект>[, protocol=None][, fix_imports=True])
— производит сериализацию объекта и возвращает последовательность байтов специального формата. Формат зави- сит от указанного во втором параметре протокола, который задается в виде числа от
0
до значения константы pickle.HIGHEST_PROTOCOL
. Если второй параметр не указан, будет использован протокол
3
(константа pickle.DEFAULT_PROTOCOL
). Пример преобразования списка и кортежа:
>>> import pickle
>>> obj1 = [1, 2, 3, 4, 5] # Список
>>> obj2 = (6, 7, 8, 9, 10) # Кортеж
>>> pickle.dumps(obj1) b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04K\x05e.'
>>> pickle.dumps(obj2) b'\x80\x03(K\x06K\x07K\x08K\tK\ntq\x00.'
loads(<Последовательность байтов>[, fix_imports=True][, encoding="ASCII"][, errors="strict"])
— преобразует последовательность байтов специального формата обратно в объект, выполняя его десериализацию. Пример восстановления списка и кор- тежа:
>>> pickle.loads(b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04K\x05e.')
[1, 2, 3, 4, 5]
>>> pickle.loads(b'\x80\x03(K\x06K\x07K\x08K\tK\ntq\x00.')
(6, 7, 8, 9, 10)
120
Часть I. Основы языка Python
6.15. Шифрование строк
Для шифрования строк предназначен модуль hashlib
. Прежде чем использовать функции из этого модуля, необходимо подключить модуль с помощью инструкции: import hashlib
Модуль предоставляет следующие функции: md5()
, sha1()
, sha224()
, sha256()
, sha384()
, sha512()
, в Python 3.6 появилась поддержка функций sha3_224()
, sha3_256()
, sha3_384()
, sha3_512()
, shake_128()
и shake_256().
В качестве необязательного параметра функциям можно передать шифруемую последовательность байтов:
>>> import hashlib
>>> h = hashlib.sha1(b"password")
Передать последовательность байтов можно также с помощью метода update()
. В этом случае объект присоединяется к предыдущему значению:
>>> h = hashlib.sha1()
>>> h.update(b"password")
Получить зашифрованную последовательность байтов и строку позволяют два метода: digest()
и hexdigest()
. Первый метод возвращает значение, относящееся к типу bytes
, а второй — строку, содержащую шестнадцатеричные цифры:
>>> h = hashlib.sha1(b"password")
>>> h.digest() b'[\xaaa\xe4\xc9\xb9??\x06\x82%\x0bl\xf83\x1b\xe6\x8f\xd8'
>>> h.hexdigest()
'5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'
Наиболее часто применяемой является функция md5()
, которая шифрует строку с помощью алгоритма MD5. Эта функция используется для шифрования паролей, т. к. не существует алгоритма для дешифровки зашифрованных ей значений. Для сравнения введенного поль- зователем пароля с сохраненным в базе необходимо зашифровать введенный пароль, а за- тем произвести сравнение:
>>> import hashlib
>>> h = hashlib.md5(b"password")
>>> p = h.hexdigest()
>>> p # Пароль, сохраненный в базе '5f4dcc3b5aa765d61d8327deb882cf99'
>>> h2 = hashlib.md5(b"password") # Пароль, введенный пользователем
>>> if p == h2.hexdigest(): print("Пароль правильный")
Пароль правильный
Свойство digest_size хранит размер значения, генерируемого описанными ранее функция- ми шифрования, в байтах:
>>> h = hashlib.sha512(b"password")
>>> h.digest_size
64
Поддерживается еще несколько функций, выполняющих устойчивое к взлому шифрование паролей:
Глава 6. Строки и двоичные данные
121
pbkdf2_hmac(<Основной алгоритм шифрования>, <Шифруемый пароль>, <"Соль">, <Коли- чество проходов шифрования>, dklen=None)
. В качестве основного алгоритма шифрова- ния следует указать строку с наименованием этого алгоритма:
"md5"
,
"sha1"
,
"sha224"
,
"sha256"
,
"sha384"
и "sha512"
. Шифруемый пароль указывается в виде значения типа bytes
"Соль"
— это особая величина типа bytes
, выступающая в качестве ключа шиф- рования, — ее длина не должна быть менее 16 символов. Количество проходов шифро- вания следует указать достаточно большим (так, при использовании алгоритма SHA512 оно должно составлять 100 000). Последним параметром функции pbkdf2_hmac()
можно указать длину результирующего закодированного значения в байтах — если она не за- дана или равна
None
, будет создано значение стандартной для выбранного алгоритма длины (64 байта для алгоритма SHA512). Закодированный пароль возвращается в виде величины типа bytes
:
>>> import hashlib
>>> dk = hashlib.pbkdf2_hmac('sha512', b'1234567', b'saltsaltsaltsalt', 100000)
>>> dk b"Sb\x85tc-\xcb@\xc5\x97\x19\x90\x94@\x9f\xde\x07\xa4p-\x83\x94\xf4\x94\x99\x07\ xec\xfa\xf3\xcd\xc3\x88jv\xd1\xe5\x9a\x119\x15/\xa4\xc2\xd3N\xaba\x02\xc0s\xc1\ xd1\x0b\x86xj(\x8c>Mr'@\xbb"
П
РИМЕЧАНИЕ
Кодирование данных с применением функции pbkdf2_hmac() отнимает очень много сис- темных ресурсов и может занять значительное время, особенно на маломощных компью- терах.
Поддержка двух следующих функций появилась в Python 3.6:
blake2s([<Шифруемый пароль>][, digest_size=32], [salt=b””])
— шифрует пароль по алгоритму BLAKE2s, оптимизированному для 32-разрядных систем. Второй параметр задает размер значения в виде числа от 1 до 32, третий — «соль» в виде величины типа bytes
, которая может иметь в длину не более 8 байтов. Возвращает объект, хранящий закодированный пароль:
>>> h = hashlib.blake2s(b"string", digest_size=16, salt=b"saltsalt")
>>> h.digest() b'\x96l\xe0\xfa\xb4\xe7Bw\x11\xf7D\xc2\xa4\xcf\x06\xf7'
blake2b([<Шифруемый пароль>][, digest_size=64], [salt=b””])
— шифрует пароль по алгоритму BLAKE2b, оптимизированному для 64-разрядных систем. Второй параметр задает размер значения в виде числа от 1 до 64, третий — «соль» в виде величины типа bytes
, которая может иметь в длину не более 16 байтов. Возвращает объект, хранящий закодированный пароль:
>>> h = hashlib.blake2b(b"string", digest_size=48, salt=b"saltsaltsalt")
>>> h.digest() b'\x0e\xcf\xb9\xc8G;q\xbaV\xbdV\x16\xd4@/J\x97W\x0c\xc4\xc5{\xd4\xb6\x12\x01z\ x9f\xdd\xf6\xf1\x03o\x97&v\xfd\xa6\x90\x81\xc4T\xb8z\xaf\xc3\x9a\xd9'
П
РИМЕЧАНИЕ
Функции blake2b() и blake2s() поддерживают большое количество параметров, которые применяются только в специфических случаях. Полное описание этих функций можно най- ти в документации по Python.
ГЛ А В А
7
Регулярные выражения
Регулярные выражения предназначены для выполнения в строке сложного поиска или за- мены. В языке Python использовать регулярные выражения позволяет модуль re
. Прежде чем задействовать функции из этого модуля, необходимо подключить модуль с помощью инструкции: import re
7.1. Синтаксис регулярных выражений
Создать откомпилированный шаблон регулярного выражения позволяет функция compile()
. Функция имеет следующий формат:
<Шаблон> = re.compile(<Регулярное выражение>[, <Модификатор>])
В параметре
<Модификатор>
могут быть указаны следующие флаги (или их комбинация через оператор
|
):
I
или
IGNORECASE
— поиск без учета регистра:
>>> import re
>>> p = re.compile(r"^[а-яе]+$", re.I | re.U)
>>> print("Найдено" if p.search("АБВГДЕЕ") else "Нет")
Найдено
>>> p = re.compile(r"^[а-яе]+$", re.U)
>>> print("Найдено" if p.search("АБВГДЕЕ") else "Нет")
Нет
M
или
MULTILINE
— поиск в строке, состоящей из нескольких подстрок, разделенных символом новой строки (
"\n"
). Символ
^
соответствует привязке к началу каждой под- строки, а символ
$
— позиции перед символом перевода строки;
S
или
DOTALL
— метасимвол «точка» по умолчанию соответствует любому символу, кро- ме символа перевода строки (
\n
). Символу перевода строки метасимвол «точка» будет соответствовать в присутствии дополнительного модификатора. Символ
^
соответствует привязке к началу всей строки, а символ
$
— привязке к концу всей строки:
>>> p = re.compile(r"^.$")
>>> print("Найдено" if p.search("\n") else "Нет")
Нет
>>> p = re.compile(r"^.$", re.M)
Глава 7. Регулярные выражения
123
>>> print("Найдено" if p.search("\n") else "Нет")
Нет
>>> p = re.compile(r"^.$", re.S)
>>> print("Найдено" if p.search("\n") else "Нет")
Найдено
X
или
VERBOSE
— если флаг указан, то пробелы и символы перевода строки будут проиг- норированы. Внутри регулярного выражения можно использовать и комментарии:
>>> p = re.compile(r"""^ # Привязка к началу строки
[0-9]+ # Строка должна содержать одну цифру (или более)
$ # Привязка к концу строки """, re.X | re.S)
>>> print("Найдено" if p.search("1234567890") else "Нет")
Найдено
>>> print("Найдено" if p.search("abcd123") else "Нет")
Нет
A
или
ASCII
— классы
\w
,
\W
,
\b
,
\B
,
\d
,
\D
,
\s и
\S
будут соответствовать символам в ко- дировке ASCII (по умолчанию указанные классы соответствуют Unicode-символам);
П
РИМЕЧАНИЕ
Флаги U и UNICODE, включающие режим соответствия Unicode-символам классов \w, \W, \b,
\B
, \d, \D, \s и \S, сохранены в Python 3 лишь для совместимости с ранними версиями это- го языка и никакого влияния на обработку регулярных выражений не оказывают.
L
или
LOCALE
— учитываются настройки текущей локали. Начиная с Python 3.6, могут быть использованы только в том случае, когда регулярное выражение задается в виде значения типов bytes или bytearray
Как видно из примеров, перед всеми строками, содержащими регулярные выражения, указан модификатор r
. Иными словами, мы используем неформатированные строки. Если модификатор не указать, то все слэши необходимо экранировать. Например, строку: p = re.compile(r"^\w+$") нужно было бы записать так: p = re.compile("^\\w+$")
Внутри регулярного выражения символы
,
^
,
$
,
*
,
+
,
?
,
{
,
[
,
]
,
\
,
|
,
(
и
)
имеют специальное значение. Если эти символы должны трактоваться как есть, их следует экранировать с по- мощью слэша. Некоторые специальные символы теряют свое особое значение, если их раз- местить внутри квадратных скобок, — в этом случае экранировать их не нужно. Например, как уже было отмечено ранее, метасимвол «точка» по умолчанию соответствует любому символу, кроме символа перевода строки. Если необходимо найти именно точку, то перед точкой нужно указать символ
\
или разместить точку внутри квадратных скобок:
[.]
. Про- демонстрируем это на примере проверки правильности введенной даты (листинг 7.1).
Листинг 7.1. Проверка правильности ввода даты
# -*- coding: utf-8 -*- import re # Подключаем модуль d = "29,12.2009" # Вместо точки указана запятая p = re.compile(r"^[0-3][0-9].[01][0-9].[12][09][0-9][0-9]$")
124
Часть I. Основы языка Python
# Символ "\" не указан перед точкой if p.search(d): print("Дата введена правильно") else: print("Дата введена неправильно")
# Так как точка означает любой символ,
# выведет: Дата введена правильно p = re.compile(r"^[0-3][0-9]\.[01][0-9]\.[12][09][0-9][0-9]$")
# Символ "\" указан перед точкой if p.search(d): print("Дата введена правильно") else: print("Дата введена неправильно")
# Так как перед точкой указан символ "\",
# выведет: Дата введена неправильно p = re.compile(r"^[0-3][0-9][.][01][0-9][.][12][09][0-9][0-9]$")
# Точка внутри квадратных скобок if p.search(d): print("Дата введена правильно") else: print("Дата введена неправильно")
# Выведет: Дата введена неправильно input()
В этом примере мы осуществляли привязку к началу и концу строки с помощью следующих метасимволов:
^
— привязка к началу строки или подстроки. Она зависит от флагов
M
(или
MULTILINE
) и
S
(или
DOTALL
);
$
— привязка к концу строки или подстроки. Она зависит от флагов
M
(или
MULTILINE
) и
S
(или
DOTALL
);
\A
— привязка к началу строки (не зависит от модификатора);
\Z
— привязка к концу строки (не зависит от модификатора).
Если в параметре
<Модификатор>
указан флаг
M
(или
MULTILINE
), то поиск производится в строке, состоящей из нескольких подстрок, разделенных символом новой строки (
\n
).
В этом случае символ
^
соответствует привязке к началу каждой подстроки, а символ
$
— позиции перед символом перевода строки:
>>> p = re.compile(r"^.+$") # Точка не соответствует \n
>>> p.findall("str1\nstr2\nstr3") # Ничего не найдено
[]
>>> p = re.compile(r"^.+$", re.S) # Теперь точка соответствует \n
>>> p.findall("str1\nstr2\nstr3") # Строка полностью соответствует
['str1\nstr2\nstr3']
>>> p = re.compile(r"^.+$", re.M) # Многострочный режим
>>> p.findall("str1\nstr2\nstr3") # Получили каждую подстроку
['str1', 'str2', 'str3']
Глава 7. Регулярные выражения
125
Привязку к началу и концу строки следует использовать, если строка должна полностью соответствовать регулярному выражению. Например, для проверки, содержит ли строка число (листинг 7.2).
Листинг 7.2. Проверка наличия целого числа в строке
# -*- coding: utf-8 -*- import re # Подключаем модуль p = re.compile(r"^[0-9]+$", re.S) if p.search("245"): print("Число") # Выведет: Число else: print("Не число") if p.search("Строка245"): print("Число") else: print("Не число") # Выведет: Не число input()
Если убрать привязку к началу и концу строки, то любая строка, содержащая хотя бы одну цифру, будет распознана как
Число
(листинг 7.3).
Листинг 7.3. Отсутствие привязки к началу или концу строки
# -*- coding: utf-8 -*- import re # Подключаем модуль p = re.compile(r"[0-9]+", re.S) if p.search("Строка245"): print("Число") # Выведет: Число else: print("Не число") input()
Кроме того, можно указать привязку только к началу или только к концу строки (листинг 7.4).
Листинг 7.4. Привязка к началу и концу строки
# -*- coding: utf-8 -*- import re # Подключаем модуль p = re.compile(r"[0-9]+$", re.S) if p.search("Строка245"): print("Есть число в конце строки") else: print("Нет числа в конце строки")
# Выведет: Есть число в конце строки p = re.compile(r"^[0-9]+", re.S) if p.search("Строка245"): print("Есть число в начале строки") else: print("Нет числа в начале строки")
# Выведет: Нет числа в начале строки input()
126
Часть I. Основы языка Python
Также поддерживаются два метасимвола, позволяющие указать привязку к началу или кон- цу слова:
\b
— привязка к началу слова (началом слова считается пробел или любой символ, не являющийся буквой, цифрой или знаком подчеркивания);
\B
— привязка к позиции, не являющейся началом слова.
Рассмотрим несколько примеров:
>>> p = re.compile(r"\bpython\b")
>>> print("Найдено" if p.search("python") else "Нет")
Найдено
>>> print("Найдено" if p.search("pythonware") else "Нет")
Нет
>>> p = re.compile(r"\Bth\B")
>>> print("Найдено" if p.search("python") else "Нет")
Найдено
>>> print("Найдено" if p.search("this") else "Нет")
Нет
В квадратных скобках
[]
можно указать символы, которые могут встречаться на этом месте в строке. Разрешается записать символы подряд или указать диапазон через дефис:
[09]
— соответствует числу 0 или 9;
[0-9]
— соответствует любому числу от 0 до 9;
[абв]
— соответствует буквам «а», «б» и «в»;
[а-г]
— соответствует буквам «а», «б», «в» и «г»;
[а-яё]
— соответствует любой букве от «а» до «я»;
[АБВ]
— соответствует буквам «А», «Б» и «В»;
[А-ЯЁ]
— соответствует любой букве от «А» до «Я»;
[а-яА-ЯёЁ]
— соответствует любой русской букве в любом регистре;
[0-9а-яА-ЯёЁa-zA-Z]
— любая цифра и любая буква независимо от регистра и языка.
В
НИМАНИЕ
!
Буква «ё» не входит в диапазон [а-я], а буква «Ё» — в диапазон [А-Я].
Значение в скобках инвертируется, если после первой скобки вставить символ
^
. Таким образом можно указать символы, которых не должно быть на этом месте в строке:
[^09]
— не цифра 0 или 9;
[^0-9]
— не цифра от 0 до 9;
[^а-яА-ЯёЁa-zA-Z]
— не буква.
Как вы уже знаете, точка теряет свое специальное значение, если ее заключить в квадрат- ные скобки. Кроме того, внутри квадратных скобок могут встретиться символы, которые имеют специальное значение (например,
^
и
-
). Символ
^
теряет свое специальное значение, если он не расположен сразу после открывающей квадратной скобки. Чтобы отменить спе- циальное значение символа
-
, его необходимо указать после всех символов, перед закры- вающей квадратной скобкой или сразу после открывающей квадратной скобки. Все специ- альные символы можно сделать обычными, если перед ними указать символ
\
Глава 7. Регулярные выражения
127
Метасимвол
|
позволяет сделать выбор между альтернативными значениями. Выражение n|m соответствует одному из символов: n
или m
:
>>> p = re.compile(r"красн((ая)|(ое))")
>>> print("Найдено" if p.search("красная") else "Нет")
Найдено
>>> print("Найдено" if p.search("красное") else "Нет")
Найдено
>>> print("Найдено" if p.search("красный") else "Нет")
Нет
Вместо указания символов можно использовать стандартные классы:
\d
— соответствует любой цифре. При указании флага
A
(
ASCII
) эквивалентно
[0-9]
;
\w
— соответствует любой букве, цифре или символу подчеркивания. При указании фла- га
A
(
ASCII
) эквивалентно
[a-zA-Z0-9_]
;
\s
— любой пробельный символ. При указании флага
A
(
ASCII
) эквивалентно
[\t\n\r\f\v]
;
\D
— не цифра. При указании флага
A
(
ASCII
) эквивалентно
[^0-9]
;
\W
— не буква, не цифра и не символ подчеркивания. При указании флага
A
(
ASCII
) экви- валентно
[^a-zA-Z0-9_]
;
\S
— не пробельный символ. При указании флага
A
(
ASCII
) эквивалентно
[^\t\n\r\f\v]
П
РИМЕЧАНИЕ
В Python 3 поддержка Unicode в регулярных выражениях установлена по умолчанию. При этом все классы трактуются гораздо шире. Так, класс \d соответствует не только десятич- ным цифрам, но и другим цифрам из кодировки Unicode, — например, дробям, класс \w включает не только латинские буквы, но и любые другие, а класс \s охватывает также не- разрывные пробелы. Поэтому на практике лучше явно указывать символы внутри квадрат- ных скобок, а не использовать классы.
Количество вхождений символа в строку задается с помощью квантификаторов:
{n}
— n
вхождений символа в строку. Например, шаблон r"^[0-9]{2}$"
соответствует двум вхождениям любой цифры;
{n,}
— n
или более вхождений символа в строку. Например, шаблон r"^[0-9]{2,}$"
соответствует двум и более вхождениям любой цифры;
{n,m}
— не менее n
и не более m
вхождений символа в строку. Числа указываются через запятую без пробела. Например, шаблон r"^[0-9]{2,4}$"
соответствует от двух до четырех вхождений любой цифры;
*
— ноль или большее число вхождений символа в строку. Эквивалентно комбинации
{0,}
;
+
— одно или большее число вхождений символа в строку. Эквивалентно комбинации
{1,}
;
?
— ни одного или одно вхождение символа в строку. Эквивалентно комбинации
{0,1}
Все квантификаторы являются «жадными». При поиске соответствия ищется самая длинная подстрока, соответствующая шаблону, и не учитываются более короткие соответствия. Рас- смотрим это на примере и получим содержимое всех тегов
вместе с тегами:
>>> s = "Text1Text2Text3"
>>> p = re.compile(r".*", re.S)
128
Часть I. Основы языка Python
>>> p.findall(s)
['Text1Text2Text3']
Вместо желаемого результата мы получили полностью строку. Чтобы ограничить «жад- ность», необходимо после квантификатора указать символ
?
:
>>> s = "Text1Text2Text3"
>>> p = re.compile(r".*?", re.S)
>>> p.findall(s)
['Text1', 'Text3']
Этот код вывел то, что мы искали. Если необходимо получить содержимое без тегов, то нужный фрагмент внутри шаблона следует разместить внутри круглых скобок:
>>> s = "Text1Text2Text3"
>>> p = re.compile(r"(.*?)", re.S)
>>> p.findall(s)
['Text1', 'Text3']
Круглые скобки часто используются для группировки фрагментов внутри шаблона. В этом случае не требуется, чтобы фрагмент запоминался и был доступен в результатах поиска.
Чтобы избежать захвата фрагмента, следует после открывающей круглой скобки разместить символы
?:
(вопросительный знак и двоеточие):
>>> s = "test text"
>>> p = re.compile(r"([a-z]+((st)|(xt)))", re.S)
>>> p.findall(s)
[('test', 'st', 'st', ''), ('text', 'xt', '', 'xt')]
>>> p = re.compile(r"([a-z]+(?:(?:st)|(?:xt)))", re.S)
>>> p.findall(s)
['test', 'text']
В первом примере мы получили список с двумя элементами. Каждый элемент списка явля- ется кортежем, содержащим четыре элемента. Все эти элементы соответствуют фрагмен- там, заключенным в шаблоне в круглые скобки. Первый элемент кортежа содержит фраг- мент, расположенный в первых круглых скобках, второй — во вторых круглых скобках и т. д. Три последних элемента кортежа являются лишними. Чтобы они не выводились в результатах, мы добавили символы
?:
после каждой открывающей круглой скобки. В ре- зультате список состоит только из фрагментов, полностью соответствующих регулярному выражению.
К найденному фрагменту в круглых скобках внутри шаблона можно обратиться с помощью механизма обратных ссылок. Для этого порядковый номер круглых скобок в шаблоне ука- зывается после слэша, например, так:
\1
. Нумерация скобок внутри шаблона начинается с
1
Для примера получим текст между одинаковыми парными тегами:
>>> s = "Text1Text2Text3Text4"
>>> p = re.compile(r"<([a-z]+)>(.*?)\1>", re.S | re.I)
>>> p.findall(s)
[('b', 'Text1'), ('I', 'Text3'), ('b', 'Text4')]
Фрагментам внутри круглых скобок можно дать имена, создав тем самым именованные фрагменты. Для этого после открывающей круглой скобки следует указать комбинацию символов
?P
. В качестве примера разберем e-mail на составные части:
Глава 7. Регулярные выражения
129
>>> email = "test@mail.ru"
>>> p = re.compile(r"""(?P[a-z0-9_.-]+) # Название ящика
@ # Символ "@"
(?P
1 ... 8 9 10 11 12 13 14 15 ... 83
>>> bytes("string\uFFFD", "cp1251", "replace") b'string?'
>>> bytes("string\uFFFD", "cp1251", "ignore") b'string'
с помощью строкового метода encode([encoding="utf-8"][, errors="strict"])
. Если кодировка не указана, строка преобразуется в последовательность байтов в кодировке
UTF-8. В параметре errors могут быть указаны значения "strict"
(значение по умолча- нию),
"replace"
,
"ignore"
,
"xmlcharrefreplace" или "backslashreplace"
:
>>> "строка".encode() b'\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xba\xd0\xb0'
>>> "строка".encode(encoding="cp1251") b'\xf1\xf2\xf0\xee\xea\xe0'
>>> "строка\uFFFD".encode(encoding="cp1251", errors="xmlcharrefreplace") b'\xf1\xf2\xf0\xee\xea\xe0�'
>>> "строка\uFFFD".encode(encoding="cp1251", errors="backslashreplace") b'\xf1\xf2\xf0\xee\xea\xe0\\ufffd'
Глава 6. Строки и двоичные данные
113
указав букву b
(регистр не имеет значения) перед строкой в апострофах, кавычках, трой- ных апострофах или тройных кавычках. Обратите внимание на то, что в строке могут быть только символы с кодами, входящими в кодировку ASCII. Все остальные символы должны быть представлены специальными последовательностями:
>>> b"string", b'string', b"""string""", b'''string'''
(b'string', b'string', b'string', b'string')
>>> b"строка"
SyntaxError: bytes can only contain ASCII literal characters.
>>> b"\xf1\xf2\xf0\xee\xea\xe0" b'\xf1\xf2\xf0\xee\xea\xe0'
с помощью функции bytes(<Последовательность>)
, которая преобразует последователь- ность целых чисел от
0
до
255
в объект типа bytes
. Если число не попадает в диапазон, возбуждается исключение
ValueError
:
>>> b = bytes([225, 226, 224, 174, 170, 160])
>>> b b'\xe1\xe2\xe0\xae\xaa\xa0'
>>> str(b, "cp866")
'строка'
с помощью функции bytes(<Число>)
, которая задает количество элементов в последова- тельности. Каждый элемент будет содержать нулевой символ:
>>> bytes(10) b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
с помощью метода bytes.fromhex(<Строка>)
. Строка в этом случае должна содержать шестнадцатеричные значения символов:
>>> b = bytes.fromhex(" e1 e2e0ae aaa0 ")
>>> b b'\xe1\xe2\xe0\xae\xaa\xa0'
>>> str(b, "cp866")
'строка'
Объекты типа bytes относятся к последовательностям. Каждый элемент такой последова- тельности может хранить целое число от
0
до
255
, которое обозначает код символа. Как и все последовательности, объекты поддерживают обращение к элементу по индексу, полу- чение среза, конкатенацию, повторение и проверку на вхождение:
>>> b = bytes("string", "cp1251")
>>> b b'string'
>>> b[0] # Обращение по индексу
115
>>> b[1:3] # Получение среза b'tr'
>>> b + b"123" # Конкатенация b'string123'
>>> b * 3 # Повторение b'stringstringstring'
>>> 115 in b, b"tr" in b, b"as" in b
(True, True, False)
114
Часть I. Основы языка Python
Как видно из примера, при выводе объекта целиком, а также при извлечении среза, произ- водится попытка отображения символов. Однако доступ по индексу возвращает целое чис- ло, а не символ. Если преобразовать объект в список, то мы получим последовательность целых чисел:
>>> list(bytes("string", "cp1251"))
[115, 116, 114, 105, 110, 103]
Тип bytes относится к неизменяемым типам. Это означает, что можно получить значение по индексу, но изменить его нельзя:
>>> b = bytes("string", "cp1251")
>>> b[0] = 168
Traceback (most recent call last):
File "
", line 1, in
TypeError: 'bytes' object does not support item assignment
Объекты типа bytes поддерживают большинство строковых методов, которые мы рассмат- ривали в предыдущих разделах. Однако некоторые из этих методов могут некорректно ра- ботать с русскими буквами — в этих случаях следует использовать тип str
, а не тип bytes
Не поддерживаются объектами типа bytes строковые методы encode()
, isidentifier()
, isprintable()
, isnumeric()
, isdecimal()
, format_map()
и format()
, а также операция фор- матирования.
При использовании методов следует учитывать, что в параметрах нужно указывать объекты типа bytes
, а не строки:
>>> b = bytes("string", "cp1251")
>>> b.replace(b"s", b"S") b'String'
Необходимо также помнить, что смешивать строки и объекты типа bytes в выражениях нельзя. Предварительно необходимо явно преобразовать объекты к одному типу, а лишь затем производить операцию:
>>> b"string" + "string"
Traceback (most recent call last):
File "
", line 1, in
TypeError: can't concat bytes to str
>>> b"string" + "string".encode("ascii") b'stringstring'
Объект типа bytes может содержать как однобайтовые, так и многобайтовые символы. При использовании многобайтовых символов некоторые функции могут работать не так, как предполагалось, — например, функция len()
вернет количество байтов, а не символов:
>>> len("строка")
6
>>> len(bytes("строка", "cp1251"))
6
>>> len(bytes("строка", "utf-8"))
12
Глава 6. Строки и двоичные данные
115
Преобразовать объект типа bytes в строку позволяет метод decode()
. Метод имеет следую- щий формат: decode([encoding="utf-8"][, errors="strict"])
Параметр encoding задает кодировку символов (по умолчанию UTF-8) в объекте bytes
, а параметр errors
— способ обработки ошибок при преобразовании. В параметре errors можно указать значения "strict"
(значение по умолчанию),
"replace"
или "ignore"
. При- мер преобразования:
>>> b = bytes("строка", "cp1251")
>>> b.decode(encoding="cp1251"), b.decode("cp1251")
('строка', 'строка')
Для преобразования можно также воспользоваться функцией str()
:
>>> b = bytes("строка", "cp1251")
>>> str(b, "cp1251")
'строка'
Чтобы изменить кодировку данных, следует сначала преобразовать тип bytes в строку, а затем произвести обратное преобразование, указав нужную кодировку. Преобразуем дан- ные из кодировки Windows-1251 в кодировку KOI8-R, а затем обратно:
>>> w = bytes("Строка", "cp1251") # Данные в кодировке windows-1251
>>> k = w.decode("cp1251").encode("koi8-r")
>>> k, str(k, "koi8-r") # Данные в кодировке KOI8-R
(b'\xf3\xd4\xd2\xcf\xcb\xc1', 'Строка')
>>> w = k.decode("koi8-r").encode("cp1251")
>>> w, str(w, "cp1251") # Данные в кодировке windows-1251
(b'\xd1\xf2\xf0\xee\xea\xe0', 'Строка')
В Python 3.5 появились два полезных инструмента для работы с типом данных bytes
. Во- первых, теперь можно форматировать такие данные с применением описанного в разд. 6.4 оператора
%
:
>>> b"%i - %i - %f" % (10, 20, 30) b'10 - 20 - 30.000000'
Однако здесь нужно помнить, что тип преобразования s
(т. е. вывод в виде Unicode-строки) в этом случае не поддерживается, и его использование приведет к возбуждению исключе- ния
TypeError
:
>>> b"%s - %s - %s" % (10, 20, 30)
Traceback (most recent call last):
File "
", line 1, in
TypeError: %b requires a bytes-like object, or an object that implements __bytes__, not 'int'
Во-вторых, тип bytes получил поддержку метода hex()
, который возвращает строку с шест- надцатеричным представлением значения:
>>> b"string".hex()
'737472696e67'
116
Часть I. Основы языка Python
6.13. Тип данных bytearray
Тип данных bytearray является разновидностью типа bytes и поддерживает те же самые методы и операции (включая оператор форматирования
%
и метод hex()
, описанные ранее).
В отличие от типа bytes
, тип bytearray допускает возможность непосредственного измене- ния объекта и содержит дополнительные методы, позволяющие выполнять эти изменения.
Создать объект типа bytearray можно следующими способами:
с помощью функции bytearray([<Строка>, <Кодировка>[, <Обработка ошибок>]])
. Если параметры не указаны, то возвращается пустой объект. Чтобы преобразовать строку в объект типа bytearray
, необходимо передать минимум два первых параметра. Если строка указана только в первом параметре, то возбуждается исключение
TypeError
:
>>> bytearray() bytearray(b'')
>>> bytearray("строка", "cp1251") bytearray(b'\xf1\xf2\xf0\xee\xea\xe0')
>>> bytearray("строка")
Traceback (most recent call last):
File "
", line 1, in
TypeError: string argument without an encoding
В третьем параметре могут быть указаны значения "strict"
(при ошибке возбуждается исключение
UnicodeEncodeError
— значение по умолчанию),
"replace"
(неизвестный символ заменяется символом вопроса) или "ignore"
(неизвестные символы игнорируются):
>>> bytearray("string\uFFFD", "cp1251", "strict")
Traceback (most recent call last):
File "
", line 1, in
File "C:\Python36\lib\encodings\cp1251.py", line 12, in encode return codecs.charmap_encode(input,errors,encoding_table)
UnicodeEncodeError: 'charmap' codec can't encode character
'\ufffd' in position 6: character maps to
>>> bytearray("string\uFFFD", "cp1251", "replace") bytearray(b'string?')
>>> bytearray("string\uFFFD", "cp1251", "ignore") bytearray(b'string')
с помощью функции bytearray(<Последовательность>)
, которая преобразует последова- тельность целых чисел от
0
до
255
в объект типа bytearray
. Если число не попадает в диапазон, возбуждается исключение
ValueError
:
>>> b = bytearray([225, 226, 224, 174, 170, 160])
>>> b bytearray(b'\xe1\xe2\xe0\xae\xaa\xa0')
>>> bytearray(b'\xe1\xe2\xe0\xae\xaa\xa0') bytearray(b'\xe1\xe2\xe0\xae\xaa\xa0')
>>> str(b, "cp866")
'строка'
с помощью функции bytearray(<Число>)
, которая задает количество элементов в после- довательности. Каждый элемент будет содержать нулевой символ:
Глава 6. Строки и двоичные данные
117
>>> bytearray(5) bytearray(b'\x00\x00\x00\x00\x00')
с помощью метода bytearray.fromhex(<Строка>)
. Строка в этом случае должна содер- жать шестнадцатеричные значения символов:
>>> b = bytearray.fromhex(" e1 e2e0ae aaa0 ")
>>> b bytearray(b'\xe1\xe2\xe0\xae\xaa\xa0')
>>> str(b, "cp866")
'строка'
Тип bytearray относится к изменяемым типам. Поэтому можно не только получить значе- ние по индексу, но и изменить его (что не свойственно строкам):
>>> b = bytearray("Python", "ascii")
>>> b[0] # Можем получить значение
80
>>> b[0] = b"J"[0] # Можем изменить значение
>>> b bytearray(b'Jython')
При изменении значения важно помнить, что присваиваемое значение должно быть целым числом в диапазоне от
0
до
255
. Чтобы получить число в предыдущем примере, мы создали объект типа bytes
, а затем присвоили значение, расположенное по индексу
0
(
b[0] = b"J"[0]
). Можно, конечно, сразу указать код символа, но ведь держать все коды символов в памяти свойственно компьютеру, а не человеку.
Для изменения объекта можно также использовать следующие методы:
append(<Число>)
— добавляет один элемент в конец объекта. Метод изменяет текущий объект и ничего не возвращает:
>>> b = bytearray("string", "ascii")
>>> b.append(b"1"[0]); b bytearray(b'string1')
extend(<Последовательность>)
— добавляет элементы последовательности в конец объекта. Метод изменяет текущий объект и ничего не возвращает:
>>> b = bytearray("string", "ascii")
>>> b.extend(b"123"); b bytearray(b'string123')
Добавить несколько элементов можно с помощью операторов
+
и
+=
:
>>> b = bytearray("string", "ascii")
>>> b + b"123" # Возвращает новый объект bytearray(b'string123')
>>> b += b"456"; b # Изменяет текущий объект bytearray(b'string456')
Кроме того, можно воспользоваться операцией присваивания значения срезу:
>>> b = bytearray("string", "ascii")
>>> b[len(b):] = b"123" # Добавляем элементы в последовательность
>>> b bytearray(b'string123')
118
Часть I. Основы языка Python
insert(<Индекс>, <Число>)
— добавляет один элемент в указанную позицию. Осталь- ные элементы смещаются. Метод изменяет текущий объект и ничего не возвращает. До- бавим элемент в начало объекта:
>>> b = bytearray("string", "ascii")
>>> b.insert(0, b"1"[0]); b bytearray(b'1string')
Метод insert()
позволяет добавить только один элемент. Чтобы добавить несколько элементов, можно воспользоваться операцией присваивания значения срезу. Добавим несколько элементов в начало объекта:
>>> b = bytearray("string", "ascii")
>>> b[:0] = b"123"; b bytearray(b'123string')
pop([<Индекс>])
— удаляет элемент, расположенный по указанному индексу, и воз- вращает его. Если индекс не указан, удаляет и возвращает последний элемент:
>>> b = bytearray("string", "ascii")
>>> b.pop() # Удаляем последний элемент
103
>>> b bytearray(b'strin')
>>> b.pop(0) # Удаляем первый элемент
115
>>> b bytearray(b'trin')
Удалить элемент списка позволяет также оператор del
:
>>> b = bytearray("string", "ascii")
>>> del b[5] # Удаляем последний элемент
>>> b bytearray(b'strin')
>>> del b[:2] # Удаляем первый и второй элементы
>>> b bytearray(b'rin')
remove(<Число>)
— удаляет первый элемент, содержащий указанное значение. Если элемент не найден, возбуждается исключение
ValueError
. Метод изменяет текущий объ- ект и ничего не возвращает:
>>> b = bytearray("strstr", "ascii")
>>> b.remove(b"s"[0]) # Удаляет только первый элемент
>>> b bytearray(b'trstr')
reverse()
— изменяет порядок следования элементов на противоположный. Метод изменяет текущий объект и ничего не возвращает:
>>> b = bytearray("string", "ascii")
>>> b.reverse(); b bytearray(b'gnirts')
Преобразовать объект типа bytearray в строку позволяет метод decode()
. Метод имеет сле- дующий формат: decode([encoding="utf-8"][, errors="strict"])
Глава 6. Строки и двоичные данные
119
Параметр encoding задает кодировку символов (по умолчанию UTF-8) в объекте bytearray
, а параметр errors
— способ обработки ошибок при преобразовании. В параметре errors можно указать значения "strict"
(значение по умолчанию), "
replace"
или "ignore"
. При- мер преобразования:
>>> b = bytearray("строка", "cp1251")
>>> b.decode(encoding="cp1251"), b.decode("cp1251")
('строка', 'строка')
Для преобразования можно также воспользоваться функцией str()
:
>>> b = bytearray("строка", "cp1251")
>>> str(b, "cp1251")
'строка'
6.14. Преобразование объекта в последовательность байтов
Преобразовать объект в последовательность байтов (выполнить его сериализацию), а затем восстановить (десериализовать) объект позволяет модуль pickle
. Прежде чем использовать функции из этого модуля, необходимо подключить модуль с помощью инструкции: import pickle
Для преобразования предназначены две функции:
dumps(<Объект>[, protocol=None][, fix_imports=True])
— производит сериализацию объекта и возвращает последовательность байтов специального формата. Формат зави- сит от указанного во втором параметре протокола, который задается в виде числа от
0
до значения константы pickle.HIGHEST_PROTOCOL
. Если второй параметр не указан, будет использован протокол
3
(константа pickle.DEFAULT_PROTOCOL
). Пример преобразования списка и кортежа:
>>> import pickle
>>> obj1 = [1, 2, 3, 4, 5] # Список
>>> obj2 = (6, 7, 8, 9, 10) # Кортеж
>>> pickle.dumps(obj1) b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04K\x05e.'
>>> pickle.dumps(obj2) b'\x80\x03(K\x06K\x07K\x08K\tK\ntq\x00.'
loads(<Последовательность байтов>[, fix_imports=True][, encoding="ASCII"][, errors="strict"])
— преобразует последовательность байтов специального формата обратно в объект, выполняя его десериализацию. Пример восстановления списка и кор- тежа:
>>> pickle.loads(b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04K\x05e.')
[1, 2, 3, 4, 5]
>>> pickle.loads(b'\x80\x03(K\x06K\x07K\x08K\tK\ntq\x00.')
(6, 7, 8, 9, 10)
120
Часть I. Основы языка Python
6.15. Шифрование строк
Для шифрования строк предназначен модуль hashlib
. Прежде чем использовать функции из этого модуля, необходимо подключить модуль с помощью инструкции: import hashlib
Модуль предоставляет следующие функции: md5()
, sha1()
, sha224()
, sha256()
, sha384()
, sha512()
, в Python 3.6 появилась поддержка функций sha3_224()
, sha3_256()
, sha3_384()
, sha3_512()
, shake_128()
и shake_256().
В качестве необязательного параметра функциям можно передать шифруемую последовательность байтов:
>>> import hashlib
>>> h = hashlib.sha1(b"password")
Передать последовательность байтов можно также с помощью метода update()
. В этом случае объект присоединяется к предыдущему значению:
>>> h = hashlib.sha1()
>>> h.update(b"password")
Получить зашифрованную последовательность байтов и строку позволяют два метода: digest()
и hexdigest()
. Первый метод возвращает значение, относящееся к типу bytes
, а второй — строку, содержащую шестнадцатеричные цифры:
>>> h = hashlib.sha1(b"password")
>>> h.digest() b'[\xaaa\xe4\xc9\xb9??\x06\x82%\x0bl\xf83\x1b\xe6\x8f\xd8'
>>> h.hexdigest()
'5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'
Наиболее часто применяемой является функция md5()
, которая шифрует строку с помощью алгоритма MD5. Эта функция используется для шифрования паролей, т. к. не существует алгоритма для дешифровки зашифрованных ей значений. Для сравнения введенного поль- зователем пароля с сохраненным в базе необходимо зашифровать введенный пароль, а за- тем произвести сравнение:
>>> import hashlib
>>> h = hashlib.md5(b"password")
>>> p = h.hexdigest()
>>> p # Пароль, сохраненный в базе '5f4dcc3b5aa765d61d8327deb882cf99'
>>> h2 = hashlib.md5(b"password") # Пароль, введенный пользователем
>>> if p == h2.hexdigest(): print("Пароль правильный")
Пароль правильный
Свойство digest_size хранит размер значения, генерируемого описанными ранее функция- ми шифрования, в байтах:
>>> h = hashlib.sha512(b"password")
>>> h.digest_size
64
Поддерживается еще несколько функций, выполняющих устойчивое к взлому шифрование паролей:
Глава 6. Строки и двоичные данные
121
pbkdf2_hmac(<Основной алгоритм шифрования>, <Шифруемый пароль>, <"Соль">, <Коли- чество проходов шифрования>, dklen=None)
. В качестве основного алгоритма шифрова- ния следует указать строку с наименованием этого алгоритма:
"md5"
,
"sha1"
,
"sha224"
,
"sha256"
,
"sha384"
и "sha512"
. Шифруемый пароль указывается в виде значения типа bytes
"Соль"
— это особая величина типа bytes
, выступающая в качестве ключа шиф- рования, — ее длина не должна быть менее 16 символов. Количество проходов шифро- вания следует указать достаточно большим (так, при использовании алгоритма SHA512 оно должно составлять 100 000). Последним параметром функции pbkdf2_hmac()
можно указать длину результирующего закодированного значения в байтах — если она не за- дана или равна
None
, будет создано значение стандартной для выбранного алгоритма длины (64 байта для алгоритма SHA512). Закодированный пароль возвращается в виде величины типа bytes
:
>>> import hashlib
>>> dk = hashlib.pbkdf2_hmac('sha512', b'1234567', b'saltsaltsaltsalt', 100000)
>>> dk b"Sb\x85tc-\xcb@\xc5\x97\x19\x90\x94@\x9f\xde\x07\xa4p-\x83\x94\xf4\x94\x99\x07\ xec\xfa\xf3\xcd\xc3\x88jv\xd1\xe5\x9a\x119\x15/\xa4\xc2\xd3N\xaba\x02\xc0s\xc1\ xd1\x0b\x86xj(\x8c>Mr'@\xbb"
П
РИМЕЧАНИЕ
Кодирование данных с применением функции pbkdf2_hmac() отнимает очень много сис- темных ресурсов и может занять значительное время, особенно на маломощных компью- терах.
Поддержка двух следующих функций появилась в Python 3.6:
blake2s([<Шифруемый пароль>][, digest_size=32], [salt=b””])
— шифрует пароль по алгоритму BLAKE2s, оптимизированному для 32-разрядных систем. Второй параметр задает размер значения в виде числа от 1 до 32, третий — «соль» в виде величины типа bytes
, которая может иметь в длину не более 8 байтов. Возвращает объект, хранящий закодированный пароль:
>>> h = hashlib.blake2s(b"string", digest_size=16, salt=b"saltsalt")
>>> h.digest() b'\x96l\xe0\xfa\xb4\xe7Bw\x11\xf7D\xc2\xa4\xcf\x06\xf7'
blake2b([<Шифруемый пароль>][, digest_size=64], [salt=b””])
— шифрует пароль по алгоритму BLAKE2b, оптимизированному для 64-разрядных систем. Второй параметр задает размер значения в виде числа от 1 до 64, третий — «соль» в виде величины типа bytes
, которая может иметь в длину не более 16 байтов. Возвращает объект, хранящий закодированный пароль:
>>> h = hashlib.blake2b(b"string", digest_size=48, salt=b"saltsaltsalt")
>>> h.digest() b'\x0e\xcf\xb9\xc8G;q\xbaV\xbdV\x16\xd4@/J\x97W\x0c\xc4\xc5{\xd4\xb6\x12\x01z\ x9f\xdd\xf6\xf1\x03o\x97&v\xfd\xa6\x90\x81\xc4T\xb8z\xaf\xc3\x9a\xd9'
П
РИМЕЧАНИЕ
Функции blake2b() и blake2s() поддерживают большое количество параметров, которые применяются только в специфических случаях. Полное описание этих функций можно най- ти в документации по Python.
ГЛ А В А
7
Регулярные выражения
Регулярные выражения предназначены для выполнения в строке сложного поиска или за- мены. В языке Python использовать регулярные выражения позволяет модуль re
. Прежде чем задействовать функции из этого модуля, необходимо подключить модуль с помощью инструкции: import re
7.1. Синтаксис регулярных выражений
Создать откомпилированный шаблон регулярного выражения позволяет функция compile()
. Функция имеет следующий формат:
<Шаблон> = re.compile(<Регулярное выражение>[, <Модификатор>])
В параметре
<Модификатор>
могут быть указаны следующие флаги (или их комбинация через оператор
|
):
I
или
IGNORECASE
— поиск без учета регистра:
>>> import re
>>> p = re.compile(r"^[а-яе]+$", re.I | re.U)
>>> print("Найдено" if p.search("АБВГДЕЕ") else "Нет")
Найдено
>>> p = re.compile(r"^[а-яе]+$", re.U)
>>> print("Найдено" if p.search("АБВГДЕЕ") else "Нет")
Нет
M
или
MULTILINE
— поиск в строке, состоящей из нескольких подстрок, разделенных символом новой строки (
"\n"
). Символ
^
соответствует привязке к началу каждой под- строки, а символ
$
— позиции перед символом перевода строки;
S
или
DOTALL
— метасимвол «точка» по умолчанию соответствует любому символу, кро- ме символа перевода строки (
\n
). Символу перевода строки метасимвол «точка» будет соответствовать в присутствии дополнительного модификатора. Символ
^
соответствует привязке к началу всей строки, а символ
$
— привязке к концу всей строки:
>>> p = re.compile(r"^.$")
>>> print("Найдено" if p.search("\n") else "Нет")
Нет
>>> p = re.compile(r"^.$", re.M)
Глава 7. Регулярные выражения
123
>>> print("Найдено" if p.search("\n") else "Нет")
Нет
>>> p = re.compile(r"^.$", re.S)
>>> print("Найдено" if p.search("\n") else "Нет")
Найдено
X
или
VERBOSE
— если флаг указан, то пробелы и символы перевода строки будут проиг- норированы. Внутри регулярного выражения можно использовать и комментарии:
>>> p = re.compile(r"""^ # Привязка к началу строки
[0-9]+ # Строка должна содержать одну цифру (или более)
$ # Привязка к концу строки """, re.X | re.S)
>>> print("Найдено" if p.search("1234567890") else "Нет")
Найдено
>>> print("Найдено" if p.search("abcd123") else "Нет")
Нет
A
или
ASCII
— классы
\w
,
\W
,
\b
,
\B
,
\d
,
\D
,
\s и
\S
будут соответствовать символам в ко- дировке ASCII (по умолчанию указанные классы соответствуют Unicode-символам);
П
РИМЕЧАНИЕ
Флаги U и UNICODE, включающие режим соответствия Unicode-символам классов \w, \W, \b,
\B
, \d, \D, \s и \S, сохранены в Python 3 лишь для совместимости с ранними версиями это- го языка и никакого влияния на обработку регулярных выражений не оказывают.
L
или
LOCALE
— учитываются настройки текущей локали. Начиная с Python 3.6, могут быть использованы только в том случае, когда регулярное выражение задается в виде значения типов bytes или bytearray
Как видно из примеров, перед всеми строками, содержащими регулярные выражения, указан модификатор r
. Иными словами, мы используем неформатированные строки. Если модификатор не указать, то все слэши необходимо экранировать. Например, строку: p = re.compile(r"^\w+$") нужно было бы записать так: p = re.compile("^\\w+$")
Внутри регулярного выражения символы
,
^
,
$
,
*
,
+
,
?
,
{
,
[
,
]
,
\
,
|
,
(
и
)
имеют специальное значение. Если эти символы должны трактоваться как есть, их следует экранировать с по- мощью слэша. Некоторые специальные символы теряют свое особое значение, если их раз- местить внутри квадратных скобок, — в этом случае экранировать их не нужно. Например, как уже было отмечено ранее, метасимвол «точка» по умолчанию соответствует любому символу, кроме символа перевода строки. Если необходимо найти именно точку, то перед точкой нужно указать символ
\
или разместить точку внутри квадратных скобок:
[.]
. Про- демонстрируем это на примере проверки правильности введенной даты (листинг 7.1).
Листинг 7.1. Проверка правильности ввода даты
# -*- coding: utf-8 -*- import re # Подключаем модуль d = "29,12.2009" # Вместо точки указана запятая p = re.compile(r"^[0-3][0-9].[01][0-9].[12][09][0-9][0-9]$")
124
Часть I. Основы языка Python
# Символ "\" не указан перед точкой if p.search(d): print("Дата введена правильно") else: print("Дата введена неправильно")
# Так как точка означает любой символ,
# выведет: Дата введена правильно p = re.compile(r"^[0-3][0-9]\.[01][0-9]\.[12][09][0-9][0-9]$")
# Символ "\" указан перед точкой if p.search(d): print("Дата введена правильно") else: print("Дата введена неправильно")
# Так как перед точкой указан символ "\",
# выведет: Дата введена неправильно p = re.compile(r"^[0-3][0-9][.][01][0-9][.][12][09][0-9][0-9]$")
# Точка внутри квадратных скобок if p.search(d): print("Дата введена правильно") else: print("Дата введена неправильно")
# Выведет: Дата введена неправильно input()
В этом примере мы осуществляли привязку к началу и концу строки с помощью следующих метасимволов:
^
— привязка к началу строки или подстроки. Она зависит от флагов
M
(или
MULTILINE
) и
S
(или
DOTALL
);
$
— привязка к концу строки или подстроки. Она зависит от флагов
M
(или
MULTILINE
) и
S
(или
DOTALL
);
\A
— привязка к началу строки (не зависит от модификатора);
\Z
— привязка к концу строки (не зависит от модификатора).
Если в параметре
<Модификатор>
указан флаг
M
(или
MULTILINE
), то поиск производится в строке, состоящей из нескольких подстрок, разделенных символом новой строки (
\n
).
В этом случае символ
^
соответствует привязке к началу каждой подстроки, а символ
$
— позиции перед символом перевода строки:
>>> p = re.compile(r"^.+$") # Точка не соответствует \n
>>> p.findall("str1\nstr2\nstr3") # Ничего не найдено
[]
>>> p = re.compile(r"^.+$", re.S) # Теперь точка соответствует \n
>>> p.findall("str1\nstr2\nstr3") # Строка полностью соответствует
['str1\nstr2\nstr3']
>>> p = re.compile(r"^.+$", re.M) # Многострочный режим
>>> p.findall("str1\nstr2\nstr3") # Получили каждую подстроку
['str1', 'str2', 'str3']
Глава 7. Регулярные выражения
125
Привязку к началу и концу строки следует использовать, если строка должна полностью соответствовать регулярному выражению. Например, для проверки, содержит ли строка число (листинг 7.2).
Листинг 7.2. Проверка наличия целого числа в строке
# -*- coding: utf-8 -*- import re # Подключаем модуль p = re.compile(r"^[0-9]+$", re.S) if p.search("245"): print("Число") # Выведет: Число else: print("Не число") if p.search("Строка245"): print("Число") else: print("Не число") # Выведет: Не число input()
Если убрать привязку к началу и концу строки, то любая строка, содержащая хотя бы одну цифру, будет распознана как
Число
(листинг 7.3).
Листинг 7.3. Отсутствие привязки к началу или концу строки
# -*- coding: utf-8 -*- import re # Подключаем модуль p = re.compile(r"[0-9]+", re.S) if p.search("Строка245"): print("Число") # Выведет: Число else: print("Не число") input()
Кроме того, можно указать привязку только к началу или только к концу строки (листинг 7.4).
Листинг 7.4. Привязка к началу и концу строки
# -*- coding: utf-8 -*- import re # Подключаем модуль p = re.compile(r"[0-9]+$", re.S) if p.search("Строка245"): print("Есть число в конце строки") else: print("Нет числа в конце строки")
# Выведет: Есть число в конце строки p = re.compile(r"^[0-9]+", re.S) if p.search("Строка245"): print("Есть число в начале строки") else: print("Нет числа в начале строки")
# Выведет: Нет числа в начале строки input()
126
Часть I. Основы языка Python
Также поддерживаются два метасимвола, позволяющие указать привязку к началу или кон- цу слова:
\b
— привязка к началу слова (началом слова считается пробел или любой символ, не являющийся буквой, цифрой или знаком подчеркивания);
\B
— привязка к позиции, не являющейся началом слова.
Рассмотрим несколько примеров:
>>> p = re.compile(r"\bpython\b")
>>> print("Найдено" if p.search("python") else "Нет")
Найдено
>>> print("Найдено" if p.search("pythonware") else "Нет")
Нет
>>> p = re.compile(r"\Bth\B")
>>> print("Найдено" if p.search("python") else "Нет")
Найдено
>>> print("Найдено" if p.search("this") else "Нет")
Нет
В квадратных скобках
[]
можно указать символы, которые могут встречаться на этом месте в строке. Разрешается записать символы подряд или указать диапазон через дефис:
[09]
— соответствует числу 0 или 9;
[0-9]
— соответствует любому числу от 0 до 9;
[абв]
— соответствует буквам «а», «б» и «в»;
[а-г]
— соответствует буквам «а», «б», «в» и «г»;
[а-яё]
— соответствует любой букве от «а» до «я»;
[АБВ]
— соответствует буквам «А», «Б» и «В»;
[А-ЯЁ]
— соответствует любой букве от «А» до «Я»;
[а-яА-ЯёЁ]
— соответствует любой русской букве в любом регистре;
[0-9а-яА-ЯёЁa-zA-Z]
— любая цифра и любая буква независимо от регистра и языка.
В
НИМАНИЕ
!
Буква «ё» не входит в диапазон [а-я], а буква «Ё» — в диапазон [А-Я].
Значение в скобках инвертируется, если после первой скобки вставить символ
^
. Таким образом можно указать символы, которых не должно быть на этом месте в строке:
[^09]
— не цифра 0 или 9;
[^0-9]
— не цифра от 0 до 9;
[^а-яА-ЯёЁa-zA-Z]
— не буква.
Как вы уже знаете, точка теряет свое специальное значение, если ее заключить в квадрат- ные скобки. Кроме того, внутри квадратных скобок могут встретиться символы, которые имеют специальное значение (например,
^
и
-
). Символ
^
теряет свое специальное значение, если он не расположен сразу после открывающей квадратной скобки. Чтобы отменить спе- циальное значение символа
-
, его необходимо указать после всех символов, перед закры- вающей квадратной скобкой или сразу после открывающей квадратной скобки. Все специ- альные символы можно сделать обычными, если перед ними указать символ
\
Глава 7. Регулярные выражения
127
Метасимвол
|
позволяет сделать выбор между альтернативными значениями. Выражение n|m соответствует одному из символов: n
или m
:
>>> p = re.compile(r"красн((ая)|(ое))")
>>> print("Найдено" if p.search("красная") else "Нет")
Найдено
>>> print("Найдено" if p.search("красное") else "Нет")
Найдено
>>> print("Найдено" if p.search("красный") else "Нет")
Нет
Вместо указания символов можно использовать стандартные классы:
\d
— соответствует любой цифре. При указании флага
A
(
ASCII
) эквивалентно
[0-9]
;
\w
— соответствует любой букве, цифре или символу подчеркивания. При указании фла- га
A
(
ASCII
) эквивалентно
[a-zA-Z0-9_]
;
\s
— любой пробельный символ. При указании флага
A
(
ASCII
) эквивалентно
[\t\n\r\f\v]
;
\D
— не цифра. При указании флага
A
(
ASCII
) эквивалентно
[^0-9]
;
\W
— не буква, не цифра и не символ подчеркивания. При указании флага
A
(
ASCII
) экви- валентно
[^a-zA-Z0-9_]
;
\S
— не пробельный символ. При указании флага
A
(
ASCII
) эквивалентно
[^\t\n\r\f\v]
П
РИМЕЧАНИЕ
В Python 3 поддержка Unicode в регулярных выражениях установлена по умолчанию. При этом все классы трактуются гораздо шире. Так, класс \d соответствует не только десятич- ным цифрам, но и другим цифрам из кодировки Unicode, — например, дробям, класс \w включает не только латинские буквы, но и любые другие, а класс \s охватывает также не- разрывные пробелы. Поэтому на практике лучше явно указывать символы внутри квадрат- ных скобок, а не использовать классы.
Количество вхождений символа в строку задается с помощью квантификаторов:
{n}
— n
вхождений символа в строку. Например, шаблон r"^[0-9]{2}$"
соответствует двум вхождениям любой цифры;
{n,}
— n
или более вхождений символа в строку. Например, шаблон r"^[0-9]{2,}$"
соответствует двум и более вхождениям любой цифры;
{n,m}
— не менее n
и не более m
вхождений символа в строку. Числа указываются через запятую без пробела. Например, шаблон r"^[0-9]{2,4}$"
соответствует от двух до четырех вхождений любой цифры;
*
— ноль или большее число вхождений символа в строку. Эквивалентно комбинации
{0,}
;
+
— одно или большее число вхождений символа в строку. Эквивалентно комбинации
{1,}
;
?
— ни одного или одно вхождение символа в строку. Эквивалентно комбинации
{0,1}
Все квантификаторы являются «жадными». При поиске соответствия ищется самая длинная подстрока, соответствующая шаблону, и не учитываются более короткие соответствия. Рас- смотрим это на примере и получим содержимое всех тегов
вместе с тегами:
>>> s = "Text1Text2Text3"
>>> p = re.compile(r".*", re.S)
128
Часть I. Основы языка Python
>>> p.findall(s)
['Text1Text2Text3']
Вместо желаемого результата мы получили полностью строку. Чтобы ограничить «жад- ность», необходимо после квантификатора указать символ
?
:
>>> s = "Text1Text2Text3"
>>> p = re.compile(r".*?", re.S)
>>> p.findall(s)
['Text1', 'Text3']
Этот код вывел то, что мы искали. Если необходимо получить содержимое без тегов, то нужный фрагмент внутри шаблона следует разместить внутри круглых скобок:
>>> s = "Text1Text2Text3"
>>> p = re.compile(r"(.*?)", re.S)
>>> p.findall(s)
['Text1', 'Text3']
Круглые скобки часто используются для группировки фрагментов внутри шаблона. В этом случае не требуется, чтобы фрагмент запоминался и был доступен в результатах поиска.
Чтобы избежать захвата фрагмента, следует после открывающей круглой скобки разместить символы
?:
(вопросительный знак и двоеточие):
>>> s = "test text"
>>> p = re.compile(r"([a-z]+((st)|(xt)))", re.S)
>>> p.findall(s)
[('test', 'st', 'st', ''), ('text', 'xt', '', 'xt')]
>>> p = re.compile(r"([a-z]+(?:(?:st)|(?:xt)))", re.S)
>>> p.findall(s)
['test', 'text']
В первом примере мы получили список с двумя элементами. Каждый элемент списка явля- ется кортежем, содержащим четыре элемента. Все эти элементы соответствуют фрагмен- там, заключенным в шаблоне в круглые скобки. Первый элемент кортежа содержит фраг- мент, расположенный в первых круглых скобках, второй — во вторых круглых скобках и т. д. Три последних элемента кортежа являются лишними. Чтобы они не выводились в результатах, мы добавили символы
?:
после каждой открывающей круглой скобки. В ре- зультате список состоит только из фрагментов, полностью соответствующих регулярному выражению.
К найденному фрагменту в круглых скобках внутри шаблона можно обратиться с помощью механизма обратных ссылок. Для этого порядковый номер круглых скобок в шаблоне ука- зывается после слэша, например, так:
\1
. Нумерация скобок внутри шаблона начинается с
1
Для примера получим текст между одинаковыми парными тегами:
>>> s = "Text1Text2Text3Text4"
>>> p = re.compile(r"<([a-z]+)>(.*?)\1>", re.S | re.I)
>>> p.findall(s)
[('b', 'Text1'), ('I', 'Text3'), ('b', 'Text4')]
Фрагментам внутри круглых скобок можно дать имена, создав тем самым именованные фрагменты. Для этого после открывающей круглой скобки следует указать комбинацию символов
?P
. В качестве примера разберем e-mail на составные части:
Глава 7. Регулярные выражения
129
>>> email = "test@mail.ru"
>>> p = re.compile(r"""(?P
@ # Символ "@"
(?P
1 ... 8 9 10 11 12 13 14 15 ... 83
>>> r = p.search(email)
>>> r.group("name") # Название ящика 'test'
>>> r.group("host") # Домен 'mail.ru'
Чтобы внутри шаблона обратиться к именованным фрагментам, используется следующий синтаксис:
(?P=name)
. Для примера получим текст между одинаковыми парными тегами:
>>> s = "Text1Text2Text3"
>>> p = re.compile(r"<(?P
>>> p.findall(s)
[('b', 'Text1'), ('I', 'Text3')]
Кроме того, внутри круглых скобок могут быть расположены следующие конструкции:
(?#...)
— комментарий. Текст внутри круглых скобок игнорируется;
(?=...)
— положительный просмотр вперед. Выведем все слова, после которых распо- ложена запятая:
>>> s = "text1, text2, text3 text4"
>>> p = re.compile(r"\w+(?=[,])", re.S | re.I)
>>> p.findall(s)
['text1', 'text2']
(?!...)
— отрицательный просмотр вперед. Выведем все слова, после которых нет запятой:
>>> s = "text1, text2, text3 text4"
>>> p = re.compile(r"[a-z]+[0-9](?![,])", re.S | re.I)
>>> p.findall(s)
['text3', 'text4']
(?<=...)
— положительный просмотр назад. Выведем все слова, перед которыми распо- ложена запятая с пробелом:
>>> s = "text1, text2, text3 text4"
>>> p = re.compile(r"(?<=[,][ ])[a-z]+[0-9]", re.S | re.I)
>>> p.findall(s)
['text2', 'text3']
(?
— отрицательный просмотр назад. Выведем все слова, перед которыми распо- ложен пробел, но перед пробелом нет запятой:
>>> s = "text1, text2, text3 text4"
>>> p = re.compile(r"(?
>>> p.findall(s)
['text4']
(?(id или name)шаблон1|шаблон2)
— если группа с номером или названием найдена, то должно выполняться условие из параметра шаблон1
, в противном случае должно выпол-
130
Часть I. Основы языка Python няться условие из параметра шаблон2
. Выведем все слова, которые расположены внутри апострофов. Если перед словом нет апострофа, то в конце слова должна быть запятая:
>>> s = "text1 'text2' 'text3 text4, text5"
>>> p = re.compile(r"(')?([a-z]+[0-9])(?(1)'|,)", re.S | re.I)
>>> p.findall(s)
[("'", 'text2'), ('', 'text4')]
(?aiLmsux)
— позволяет установить опции регулярного выражения. Буквы a
, i
,
L
, m
, s
, u
и x
имеют такое же назначение, что и одноименные модификаторы в функции compile()
В
НИМАНИЕ
!
Начиная с Python 3.6, опции, задаваемые внутри регулярного выражения в круглых скобках, объявлены устаревшими и не рекомендованными к использованию. В будущих версиях
Python их поддержка будет удалена.
Рассмотрим небольшой пример. Предположим, необходимо получить все слова, располо- женные после дефиса, причем перед дефисом и после слов должны следовать пробельные символы:
>>> s = "-word1 -word2 -word3 -word4 -word5"
>>> re.findall(r"\s\-([a-z0-9]+)\s", s, re.S | re.I)
['word2', 'word4']
Как видно из примера, мы получили только два слова вместо пяти. Первое и последнее сло- ва не попали в результат, т. к. расположены в начале и в конце строки. Чтобы эти слова по- пали в результат, необходимо добавить альтернативный выбор
(^|\s)
— для начала строки и
(\s|$)
— для конца строки. Чтобы найденные выражения внутри круглых скобок не по- пали в результат, следует добавить символы
?:
после открывающей скобки:
>>> re.findall(r"(?:^|\s)\-([a-z0-9]+)(?:\s|$)", s, re.S | re.I)
['word1', 'word3', 'word5']
Здесь первое и последнее слова успешно попали в результат. Почему же слова word2
и word4 не попали в список совпадений — ведь перед дефисом есть пробел и после слова есть про- бел? Чтобы понять причину, рассмотрим поиск по шагам. Первое слово успешно попадает в результат, т. к. перед дефисом расположено начало строки и после слова есть пробел. По- сле поиска указатель перемещается, и строка для дальнейшего поиска примет следующий вид:
"-word1 <Указатель>-word2 -word3 -word4 -word5"
Обратите внимание на то, что перед фрагментом
-word2
больше нет пробела, и дефис не расположен в начале строки. Поэтому следующим совпадением окажется слово word3
, и указатель снова будет перемещен:
"-word1 -word2 -word3 <Указатель>-word4 -word5"
Опять перед фрагментом
-word4
нет пробела, и дефис не расположен в начале строки. По- этому следующим совпадением окажется слово word5
, и поиск будет завершен. Таким обра- зом, слова word2
и word4 не попадают в результат, поскольку пробел до фрагмента уже был использован в предыдущем поиске. Чтобы этого избежать, следует воспользоваться поло- жительным просмотром вперед
(?=...)
:
>>> re.findall(r"(?:^|\s)\-([a-z0-9]+)(?=\s|$)", s, re.S | re.I)
['word1', 'word2', 'word3', 'word4', 'word5']
Глава 7. Регулярные выражения
131
В этом примере мы заменили фрагмент
(?:\s|$)
на
(?=\s|$)
. Поэтому все слова успешно попали в список совпадений.
7.2. Поиск первого совпадения с шаблоном
Для поиска первого совпадения с шаблоном предназначены следующие функции и методы:
match()
— проверяет соответствие с началом строки. Формат метода: match(<Строка>[, <Начальная позиция>[, <Конечная позиция>]])
Если соответствие найдено, возвращается объект
Match
, в противном случае — значение
None
:
>>> import re
>>> p = re.compile(r"[0-9]+")
>>> print("Найдено" if p.match("str123") else "Нет")
Нет
>>> print("Найдено" if p.match("str123", 3) else "Нет")
Найдено
>>> print("Найдено" if p.match("123str") else "Нет")
Найдено
Вместо метода match()
можно воспользоваться функцией match()
. Формат функции: re.match(<Шаблон>, <Строка>[, <Модификатор>])
В параметре
<Шаблон>
указывается строка с регулярным выражением или скомпилиро- ванное регулярное выражение. В параметре
<Модификатор>
можно указать флаги, ис- пользуемые в функции compile()
. Если соответствие найдено, то возвращается объект
Match
, в противном случае — значение
None
:
>>> p = r"[0-9]+"
>>> print("Найдено" if re.match(p, "str123") else "Нет")
Нет
>>> print("Найдено" if re.match(p, "123str") else "Нет")
Найдено
>>> p = re.compile(r"[0-9]+")
>>> print("Найдено" if re.match(p, "123str") else "Нет")
Найдено
search()
— проверяет соответствие с любой частью строки. Формат метода: search(<Строка>[, <Начальная позиция>[, <Конечная позиция>]])
Если соответствие найдено, возвращается объект
Match
, в противном случае — значение
None
:
>>> p = re.compile(r"[0-9]+")
>>> print("Найдено" if p.search("str123") else "Нет")
Найдено
>>> print("Найдено" if p.search("123str") else "Нет")
Найдено
>>> print("Найдено" if p.search("123str", 3) else "Нет")
Нет
Вместо метода search()
можно воспользоваться функцией search()
. Формат функции: re.search(<Шаблон>, <Строка>[, <Модификатор>])
132
Часть I. Основы языка Python
В параметре
<Шаблон>
указывается строка с регулярным выражением или скомпилиро- ванное регулярное выражение. В параметре
<Модификатор>
можно указать флаги, ис- пользуемые в функции compile()
. Если соответствие найдено, возвращается объект
Match
, в противном случае — значение
None
:
>>> p = r"[0-9]+"
>>> print("Найдено" if re.search(p, "str123") else "Нет")
Найдено
>>> p = re.compile(r"[0-9]+")
>>> print("Найдено" if re.search(p, "str123") else "Нет")
Найдено
fullmatch()
— выполняет проверку, соответствует ли переданная строка регулярному выражению целиком. Формат метода: fullmatch(<Строка>[, <Начальная позиция>[, <Конечная позиция>]])
Если соответствие найдено, то возвращается объект
Match
, в противном случае — значе- ние
None
:
>>> p = re.compile("[Pp]ython")
>>> print("Найдено" if p.fullmatch("Python") else "Нет")
Найдено
>>> print("Найдено" if p.fullmatch("py") else "Нет")
Нет
>>> print("Найдено" if p.fullmatch("PythonWare") else "Нет")
Нет
>>> print("Найдено" if p.fullmatch("PythonWare", 0, 6) else "Нет")
Найдено
Вместо метода fullmatch()
можно воспользоваться функцией fullmatch()
. Формат функции: re.fullmatch(<Шаблон>, <Строка>[, <Модификатор>])
В параметре
<Шаблон>
указывается строка с регулярным выражением или скомпилиро- ванное регулярное выражение. В параметре
<Модификатор>
можно указать флаги, используемые в функции compile()
. Если строка полностью совпадает с шаблоном, воз- вращается объект
Match
, в противном случае — значение
None
:
>>> p = "[Pp]ython"
>>> print("Найдено" if re.fullmatch(p, "Python") else "Нет")
Найдено
>>> print("Найдено" if re.fullmatch(p, "py") else "Нет")
Нет
В качестве примера переделаем нашу программу суммирования произвольного количества целых чисел, введенных пользователем (см. листинг 4.12), таким образом, чтобы при вводе строки вместо числа программа не завершалась с фатальной ошибкой. Предусмотрим также возможность ввода отрицательных целых чисел (листинг 7.5).
Листинг 7.5. Суммирование произвольного количества чисел
# -*- coding: utf-8 -*- import re print("Введите слово 'stop' для получения результата") summa = 0
Глава 7. Регулярные выражения
133 p = re.compile(r"^[-]?[0-9]+$", re.S) while True: x = input("Введите число: ") if x == "stop": break # Выход из цикла if not p.search(x): print("Необходимо ввести число, а не строку!") continue # Переходим на следующую итерацию цикла x = int(x) # Преобразуем строку в число summa += x print("Сумма чисел равна:", summa) input()
Объект
Match
, возвращаемый методами (функциями) match()
, search()
и fullmatch()
,
име- ет следующие атрибуты и методы:
re
— ссылка на скомпилированный шаблон, указанный в методах (функциях) match()
, search()
и fullmatch()
. Через эту ссылку доступны следующие атрибуты:
• groups
— количество групп в шаблоне;
• groupindex
— словарь с названиями групп и их номерами;
• pattern
— исходная строка с регулярным выражением;
• flags
— комбинация флагов, заданных при создании регулярного выражения в функции compile()
, и флагов, указанных в самом регулярном выражении, в конст- рукции
(?aiLmsux)
;
string
— значение параметра
<Строка>
в методах (функциях) match()
, search()
и fullmatch()
;
pos
— значение параметра
<Начальная позиция>
в методах match()
, search()
и fullmatch()
;
endpos
— значение параметра
<Конечная позиция>
в методах match()
, search()
и fullmatch()
;
lastindex
— возвращает номер последней группы или значение
None
, если поиск завер- шился неудачей;
lastgroup
— возвращает название последней группы или значение
None
, если эта группа не имеет имени или поиск завершился неудачей:
>>> p = re.compile(r"(?P
>>> m = p.search("123456string 67890text")
>>> m
<_sre.SRE_Match object at 0x00FC9DE8>
>>> m.re.groups, m.re.groupindex
(2, {'num': 1, 'str': 2})
>>> p.groups, p.groupindex
(2, {'num': 1, 'str': 2})
>>> m.string
'123456string 67890text'
>>> m.lastindex, m.lastgroup
(2, 'str')
>>> m.pos, m.endpos
(0, 22)
134
Часть I. Основы языка Python
group([
— возвращает фрагменты, соответ- ствующие шаблону. Если параметр не задан или указано значение
0
, возвращается фрагмент, полностью соответствующий шаблону. Если указан номер или название груп- пы, возвращается фрагмент, совпадающий с этой группой. Через запятую можно указать несколько номеров или названий групп — в этом случае возвращается кортеж, содер- жащий фрагменты, что соответствует группам. Если нет группы с указанным номером или названием, то возбуждается исключение
IndexError
:
>>> p = re.compile(r"(?P
>>> m = p.search("123456string 67890text")
>>> m.group(), m.group(0) # Полное соответствие шаблону
('123456string', '123456string')
>>> m.group(1), m.group(2) # Обращение по индексу
('123456', 'string')
>>> m.group("num"), m.group("str") # Обращение по названию
('123456', 'string')
>>> m.group(1, 2), m.group("num", "str") # Несколько параметров
(('123456', 'string'), ('123456', 'string'))
groupdict([<Значение по умолчанию>])
— возвращает словарь, содержащий значения именованных групп. С помощью необязательного параметра можно указать значение, которое будет выводиться вместо значения
None для групп, не имеющих совпадений:
>>> p = re.compile(r"(?P
>>> m = p.search("123456")
>>> m.groupdict()
{'num': '123456', 'str': None}
>>> m.groupdict("")
{'num': '123456', 'str': ''}
groups([<Значение по умолчанию>])
— возвращает кортеж, содержащий значения всех групп. С помощью необязательного параметра можно указать значение, которое будет выводиться вместо значения
None для групп, не имеющих совпадений:
>>> p = re.compile(r"(?P
>>> m = p.search("123456")
>>> m.groups()
('123456', None)
>>> m.groups("")
('123456', '')
start([<Номер или название группы>])
— возвращает индекс начала фрагмента, соот- ветствующего заданной группе. Если параметр не указан, то фрагментом является пол- ное соответствие с шаблоном. Если соответствия нет, возвращается значение
-1
;
end([<Номер или название группы>])
— возвращает индекс конца фрагмента, соответ- ствующего заданной группе. Если параметр не указан, то фрагментом является полное соответствие с шаблоном. Если соответствия нет, возвращается значение
-1
;
span([<Номер или название группы>])
— возвращает кортеж, содержащий начальный и конечный индексы фрагмента, соответствующего заданной группе. Если параметр не указан, то фрагментом является полное соответствие с шаблоном. Если соответствия нет, возвращается значение
(–1, -1)
:
Глава 7. Регулярные выражения
135
>>> p = re.compile(r"(?P
>>> s = "str123456str"
>>> m = p.search(s)
>>> m.start(), m.end(), m.span()
(3, 12, (3, 12))
>>> m.start(1), m.end(1), m.start("num"), m.end("num")
(3, 9, 3, 9)
>>> m.start(2), m.end(2), m.start("str"), m.end("str")
(9, 12, 9, 12)
>>> m.span(1), m.span("num"), m.span(2), m.span("str")
((3, 9), (3, 9), (9, 12), (9, 12))
>>> s[m.start(1):m.end(1)], s[m.start(2):m.end(2)]
('123456', 'str')
expand(<Шаблон>)
— производит замену в строке. Внутри указанного шаблона можно использовать обратные ссылки:
\номер группы
,
\g<номер группы>
и
\g<название группы>
Для примера поменяем два тега местами:
>>> p = re.compile(r"<(?P
>>> m = p.search("
1 ... 9 10 11 12 13 14 15 16 ... 83
")
>>> m.expand(r"<\2><\1>") # \номер '
'
>>> m.expand(r"<\g<2>><\g<1>>") # \g<номер>
'
'
>>> m.expand(r"<\g
'
'
В качестве примера использования метода search()
проверим на соответствие шаблону введенный пользователем адрес электронной почты (листинг 7.6).
Листинг 7.6. Проверка e-mail на соответствие шаблону
# -*- coding: utf-8 -*- import re email = input("Введите e-mail: ") pe = r"^([a-z0-9_.-]+)@(([a-z0-9-]+\.)+[a-z]{2,6})$" p = re.compile(pe, re.I | re.S) m = p.search(email) if not m: print("E-mail не соответствует шаблону") else: print("E-mail", m.group(0), "соответствует шаблону") print("ящик:", m.group(1), "домен:", m.group(2)) input()
Результат выполнения (введенное пользователем значение выделено полужирным шриф- том):
Введите e-mail: user@mail.ru
E-mail user@mail.ru соответствует шаблону ящик: user домен: mail.ru
136
Часть I. Основы языка Python
7.3. Поиск всех совпадений с шаблоном
Для поиска всех совпадений с шаблоном предназначено несколько функций и методов.
Метод findall()
ищет все совпадения с шаблоном. Формат метода: findall(<Строка>[, <Начальная позиция>[, <Конечная позиция>]])
Если соответствия найдены, возвращается список с фрагментами, в противном случае возвращается пустой список. Если внутри шаблона есть более одной группы, то каждый элемент списка будет кортежем, а не строкой:
>>> import re
>>> p = re.compile(r"[0-9]+")
>>> p.findall("2007, 2008, 2009, 2010, 2011")
['2007', '2008', '2009', '2010', '2011']
>>> p = re.compile(r"[a-z]+")
>>> p.findall("2007, 2008, 2009, 2010, 2011")
[]
>>> t = r"(([0-9]{3})-([0-9]{2})-([0-9]{2}))"
>>> p = re.compile(t)
>>> p.findall("322-77-20, 528-22-98")
[('322-77-20', '322', '77', '20'),
('528-22-98', '528', '22', '98')]
Вместо метода findall()
можно воспользоваться функцией findall()
. Формат функции: re.findall(<Шаблон>, <Строка>[, <Модификатор>])
В параметре
<Шаблон>
указывается строка с регулярным выражением или скомпилиро- ванное регулярное выражение. В параметре
<Модификатор>
можно указать флаги, ис- пользуемые в функции compile()
:
>>> re.findall(r"[0-9]+", "1 2 3 4 5 6")
['1', '2', '3', '4', '5', '6']
>>> p = re.compile(r"[0-9]+")
>>> re.findall(p, "1 2 3 4 5 6")
['1', '2', '3', '4', '5', '6']
Метод finditer()
аналогичен методу findall()
, но возвращает итератор, а не список.
На каждой итерации цикла возвращается объект
Match
. Формат метода: finditer(<Строка>[, <Начальная позиция>[, <Конечная позиция>]])
Пример:
>>> p = re.compile(r"[0-9]+")
>>> for m in p.finditer("2007, 2008, 2009, 2010, 2011"): print(m.group(0), "start:", m.start(), "end:", m.end())
2007 start: 0 end: 4 2008 start: 6 end: 10 2009 start: 12 end: 16 2010 start: 18 end: 22 2011 start: 24 end: 28
Глава 7. Регулярные выражения
137
Вместо метода finditer()
можно воспользоваться функцией finditer()
. Ее формат: re.finditer(<Шаблон>, <Строка>[, <Модификатор>])
В параметре
<Шаблон>
указывается строка с регулярным выражением или скомпилиро- ванное регулярное выражение. В параметре
<Модификатор>
можно указать флаги, ис- пользуемые в функции compile()
. Получим содержимое между тегами:
>>> p = re.compile(r"(.+?)", re.I | re.S)
>>> s = "Text1Text2Text3"
>>> for m in re.finditer(p, s): print(m.group(1))
Text1
Text3 7.4. Замена в строке
Метод sub()
ищет все совпадения с шаблоном и заменяет их указанным значением. Если совпадения не найдены, возвращается исходная строка. Метод имеет следующий формат: sub(<Новый фрагмент или ссылка на функцию>, <Строка для замены>
[, <Максимальное количество замен>])
Внутри нового фрагмента можно использовать обратные ссылки
\номер группы
,
\g<номер группы>
и
\g<название группы>
. Для примера поменяем два тега местами:
>>> import re
>>> p = re.compile(r"<(?P
>>> p.sub(r"<\2><\1>", "
") # \номер '
'
>>> p.sub(r"<\g<2>><\g<1>>", "
") # \g<номер>
'
'
>>> p.sub(r"<\g
") # \g<название>
'
'
В качестве первого параметра можно указать ссылку на функцию. В эту функцию будет передаваться объект
Match
, соответствующий найденному фрагменту. Результат, возвра- щаемый этой функцией, служит фрагментом для замены. Для примера найдем все числа в строке и прибавим к ним число
10
(листинг 7.7).
Листинг 7.7. Поиск чисел в строке
# -*- coding: utf-8 -*- import re def repl(m):
""" Функция для замены. m — объект Match """ x = int(m.group(0)) x += 10 return "{0}".format(x) p = re.compile(r"[0-9]+")
# Заменяем все вхождения print(p.sub(repl, "2008, 2009, 2010, 2011"))
138
Часть I. Основы языка Python
# Заменяем только первые два вхождения print(p.sub(repl, "2008, 2009, 2010, 2011", 2)) input()
Результат выполнения:
2018, 2019, 2020, 2021 2018, 2019, 2010, 2011
В
НИМАНИЕ
!
Название функции указывается без круглых скобок.
Вместо метода sub()
можно воспользоваться функцией sub()
. Формат функции: re.sub(<Шаблон>, <Новый фрагмент или ссылка на функцию>,
<Строка для замены>[, <Максимальное количество замен>
[, flags=0]])
В качестве параметра
<Шаблон>
можно указать строку с регулярным выражением или ском- пилированное регулярное выражение. Для примера поменяем два тега местами, а также изменим регистр букв (листинг 7.8).
Листинг 7.8. Перестановка тегов с изменением регистра букв
# -*- coding: utf-8 -*- import re def repl(m):
""" Функция для замены. m — объект Match """ tag1 = m.group("tag1").upper() tag2 = m.group("tag2").upper() return "<{0}><{1}>".format(tag2, tag1) p = r"<(?P
1 ... 10 11 12 13 14 15 16 17 ... 83