Математический практикум по Питону.
Показано как преобразовывать, искать подстроки и тому подобное. В частности, рассматриваются регулярные выражения. Показано как формируется список токенов и их значений. Методы замены подстрок и разбиения строки на подстроки. Рассматривается и чтение текстового файла с таблицей (цен акции за некий период).
Это предварительная версия! Любые замечания приветствуются.
# В фигурные скобки подставляются значения аргументов метода format.
"первое число {}, а теперь второе {}".format(4,2)
# первый аргумент вместо первой пары фигурных скобок, а второй вместо второй.
'первое число 4, а теперь второе 2'
u = "первое число {}, а теперь второе {}"
type(u)
str
v = u.format(4,2)
v # Выведем значение переменной, т.е. строку.
'первое число 4, а теперь второе 2'
type(v)
str
print(v) # Напечатаем значение.
первое число 4, а теперь второе 2
Конечно можно подставить любой объект. Например строчку.
"{} получила {} баллов за контрольную.".format('Маша', 15)
'Маша получила 15 баллов за контрольную.'
Можно указать порядковый номер аргумента в фигурных скобоках явно.
"или иначе, для начала второе число {1}, а теперь первое число {0}".format(4,2)
# Позволяет менять порядок вывода аргументов.
'или иначе, для начала второе число 2, а теперь первое число 4'
# Номер в фигурных скобрах можно повторять.
"или даже повторять {0}, {1}, {0}.".format(4, 2)
'или даже повторять 4, 2, 4.'
"или даже повторять {0}, {1:.2f}, {0:0.4}.".format(34564.123456, 2321.123456)
'или даже повторять 34564.123456, 2321.12, 3.456e+04.'
'{:.90f}'.format(2.5)
'2.500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
'{:5.2f}'.format(2.5)
' 2.50'
'{:5.2f}'.format(12.5) # float
'12.50'
'{:9.2e}'.format(12.5)
' 1.25e+01'
round(2.6)
3
round( 1.2345678, 3)
1.235
round( 1.2345678, ndigits=3)
1.235
Есть и более прдвинутый подход, где вместо номера указываем именной аргумент.
# Она подставляется автоматически в строке,
'sss {a} ff'.format(a=77) # т.е. там, где она заключена в фигурные скобки.
'sss 77 ff'
a = 55 # Происвоем переменной.
'sss {a} ff'.format() # Имя a должно быть в format.
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-19-d2843b6f1e3b> in <module> 1 a = 55 # Происвоем переменной. ----> 2 'sss {a} ff'.format() # Имя a должно быть в format. KeyError: 'a'
'sss {a} ff'.format(a=a)
'sss 55 ff'
'sss {b} ff'.format(b=a)
'sss 55 ff'
'sss {a} ff'.format(a)
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-22-6d7ffef552fc> in <module> ----> 1 'sss {a} ff'.format(a) KeyError: 'a'
Можно и саму переменную использовать в скобках (метода format тогда не будет использоваться ). Так, можно оперировать не порядковым номером, а именем переменной.
# Сначала присваиваем некой переменной значение.
a = 77 # Далее будем её использовать.
# Обращаю внимание на f перед строкой.
f'sss {a} ff' # {a} будет заменено на значение переменой a.
'sss 77 ff'
type(f'aa')
str
'sss {a} ff' # А вот что будет без f до строки.
'sss {a} ff'
'dff'.zfill(5)
'00dff'
day = 7
f'{day}'.zfill(2)
'07'
s = 'sss'
s.center(8, '0')
'00sss000'
'sss'.center
<function str.center>
'sss'.center
<function str.center>
# Центрируем текст.
s = 'center'.center(10) # В скобке указывается длина итоговой строки.
s
' center '
len(s) # Длина строки.
10
s[2:-2] # Проверим что это так.
'center'
# Если трока больше требуемой величины.
s = 'center'.center(5) # то ничего не делается.
s
'center'
len(s)
6
# Пробелы превичной строки не учитываются,
l = 'q '.center(8) # т.е. пробелы как обычные символы.
l
' q '
len(l)
8
'Заявление'.center(80)
' Заявление '
# Выравненная на левый край.
t = ' qq qq'.ljust(10)
t, len( t )
(' qq qq ', 10)
# Выравненная на правый край.
t = ' qq qq'.rjust(10)
t, len( t )
(' qq qq', 10)
'Сидоров Иван Иванович'.rjust(80)
' Сидоров Иван Иванович'
ss = '*\tАня\n*\tКатя'
print(ss)
* Аня * Катя
ss
'*\tАня\n*\tКатя'
len(ss)
12
tt = ss.expandtabs(4) # Заменяем каждый символ табуляции на определенное количество пробелов.
tt
'* Аня\n* Катя'
len(tt) # Каждая табуляция 4 символа.
16
print(tt)
* Аня * Катя
grades = 'Имя\tБаллы\nАня\t25\nКатя\t27'
print( grades )
Имя Баллы Аня 25 Катя 27
print(grades.expandtabs(16))
Имя Баллы Аня 25 Катя 27
Упр. Сделать таблицу (center len и тому подобное) с двумя колонками. Первая выровнена по левой границе, а вторая по центру. Причем название колонок центрированы. Для упрощения ширину колонок можно считать известной. Например:
# Имя Баллы
#Сергей 85
#Анна 91
#Андрей 88
Замена
'студентка пришла на спецкурс.'.replace('студентка', 'Маша')
'Маша пришла на спецкурс.'
'студентка пришла на спецкурс.'.replace(['студентка'], ['Маша'])
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-54-a33aae781b04> in <module> ----> 1 'студентка пришла на спецкурс.'.replace(['студентка'], ['Маша']) TypeError: replace() argument 1 must be str, not list
Числа
Напомню
'45'.isdigit(), 'a45'.isdigit()
(True, False)
s = '45 ' #'a45 '
s = s.strip()
if s.isdigit():
print('num ~ ', int(s))
num ~ 45
Дата и время
from dateutil.parser import parse
d = parse("2015-01-13T16:12:04.000Z")
Открытие файла
Считаем данные из файла.
fdata = open("MTLRP_201101_211031.txt")
txt = fdata.read()
txt[:400]# Выведем только часть данных.
'<TICKER>,<PER>,<DATE>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>\nMTLRP,W,20201102,000000,62.0000000,67.0000000,61.8000000,66.3500000,924730\nMTLRP,W,20201109,000000,66.8500000,69.5000000,65.9500000,69.1500000,2514560\nMTLRP,W,20201116,000000,69.7500000,72.1500000,68.0000000,70.4500000,3263630\nMTLRP,W,20201123,000000,70.4000000,71.5000000,69.6500000,70.4000000,1355960\nMTLRP,W,20201130,000000,70.6500000'
# Двигает каретку
fdata.read()
# Поэтому теперь пусто.
''
После работы с файлом его следует закрыть
fdata.close()
Дабы не забыть его закрыть принято использовать следующую конструкцию.
# Открываем файл и присваиваем переменной fdata
with open("MTLRP_201101_211031.txt") as fdata:
txt = fdata.read()
# Файл автоматически будет закрыт.
Если файла не существует, то будет ошибка
fdata = open("file.txt")
--------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) <ipython-input-64-87070ca35852> in <module> ----> 1 fdata = open("file.txt") FileNotFoundError: [Errno 2] No such file or directory: 'file.txt'
with open("file.txt") as fdata:
txt = fdata.read()
--------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) <ipython-input-65-e22d86469d5a> in <module> ----> 1 with open("file.txt") as fdata: 2 txt = fdata.read() FileNotFoundError: [Errno 2] No such file or directory: 'file.txt'
Можно проверить его существование.
import os.path
os.path.isfile('file.txt')
False
fname = 'file.txt'
if not os.path.isfile( fname ):
print("Файла не существует")
Файла не существует
os.path.isfile("MTLRP_201101_211031.txt")
True
Обработка текстовых данных
# Разобъем по строкам.
lines = txt.split('\n')
lines[:5]
['<TICKER>,<PER>,<DATE>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>', 'MTLRP,W,20201102,000000,62.0000000,67.0000000,61.8000000,66.3500000,924730', 'MTLRP,W,20201109,000000,66.8500000,69.5000000,65.9500000,69.1500000,2514560', 'MTLRP,W,20201116,000000,69.7500000,72.1500000,68.0000000,70.4500000,3263630', 'MTLRP,W,20201123,000000,70.4000000,71.5000000,69.6500000,70.4000000,1355960']
# Это название столбцов.
lines[0]
'<TICKER>,<PER>,<DATE>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>'
# Сами данные это остальные строчки.
print(lines[1])
print("...")
print(lines[-2])
print(lines[-1])# Пустая строка
MTLRP,W,20201102,000000,62.0000000,67.0000000,61.8000000,66.3500000,924730 ... MTLRP,W,20211025,000000,312.1500000,319.9000000,282.3500000,296.5000000,9174930
Считать файл построчно
with open("MTLRP_201101_211031.txt") as fdata:
for l in fdata:
lines.append(l)
Или даже так:
with open("MTLRP_201101_211031.txt") as fdata:
lines = fdata.readlines()
ll = lines[1] # Строка данных.
ll # Нужно разбить поля.
'MTLRP,W,20201102,000000,62.0000000,67.0000000,61.8000000,66.3500000,924730\n'
fields = ll.split(',')
fields # Отдельные поля
['MTLRP', 'W', '20201102', '000000', '62.0000000', '67.0000000', '61.8000000', '66.3500000', '924730\n']
date = fields[2]
price = fields[4]
print('дата', date)
print('цена', price)
дата 20201102 цена 62.0000000
year = date[:4]
month = date[4:6]
day = date[7:9]
print(f"{year}/{month}/{day}")
2020/11/2
year + 1
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-79-68ecbd02a92b> in <module> ----> 1 year + 1 TypeError: must be str, not int
year = int(year)
month = int(month)
day = int(day)
import datetime as dt
dt.date( year = year, month = month, day = day)
datetime.date(2020, 11, 2)
dates = []
prices = []
for ll in lines[1:]:
fields = ll.split(',')
if len(fields) < 9: # какие то левые строки
break
date = fields[2]
price = fields[4]
# Можно в одну строку
year, month, day = int(date[:4]), int(date[4:6]), int(date[6:8])
date = dt.date( year = year, month = month, day = day)
dates.append(date)
prices.append(price)
prices[:5]
['62.0000000', '66.8500000', '69.7500000', '70.4000000', '70.6500000']
Можно исправить первоначальный код, а можно написать генератор.
prices = [ float(p) for p in prices]
prices[:5]
[62.0, 66.85, 69.75, 70.4, 70.65]
Точки и значения вы сформировали. Теперь будем строить график. Для этого нужна ещё одна вспомогательная библиотека.
Построение графиков
Библиотека matplotlib применяется для вывода графиков. Точнее её подмодуль (подбиблиотека) pyplot.
# Длинное название к отмеченной подбиблиотеки (pyplot)
import matplotlib.pyplot as plt # обозначим plt.
#Следуюшща строка позволяет выводить данные прям на данной странице в блоках. Иначе будет "всплывать" отдельное окно.
#%matplotlib inline
Отмечу, что последняя строчка относится к система jupyter, а не самому языку python.
Отрисуем нужную функцию. Используем для этого 100 точек. После загрузки библитеки в систему, график по сформированным ранее данным стротися так:
plt.plot( prices ) # Значений достаточно.
[<matplotlib.lines.Line2D at 0x7f2d2b943748>]
Но можно конечно добавить отсчеты по оси x и разукрасить график.
# plot строит график по-точечно.
plt.plot( dates, prices, 'g.-') # g -- green (зеленая), . и - задают стиль точки и прямой.
[<matplotlib.lines.Line2D at 0x7f2d2b8fdbe0>]
Даже дата правильно отобразилась!
# b - синий, * -- звездочка в каждой точке. -- соединение пунктиром.
plt.plot( dates, prices, 'b*--')
[<matplotlib.lines.Line2D at 0x7f2d2b878780>]
# Цвета есть такие:
# 'b' -- синий | 'c' -- бирюзовый | 'k' -- черный
# 'g' -- зеленый | 'm' -- пурпурный | 'w' -- белый
# 'r' -- красный | 'y' -- желтый |
# стили прямых такие:
# '-' -- сплошная | '-.' -- точка-дефис
# '--' -- пунктирная | ':' -- по-точечно
# А стили точек:
# Различные размеры:
# '.', ',' и 'o' -- точек
# 'v', '^', '<' и '>' -- треугольников
# '1', '2', '3' и '4' -- уголка.
# Специфика:
# 's' -- квадрат | 'h' -- гексагон-1 | 'x' -- знак x | '|' -- вертикальные
# 'p' -- пентагон | 'H' -- гексагон-2 | 'D' -- ромб | '_' -- горизонтальные
# '*' -- звезда | '+' -- знак плюс | 'd' -- ромб утонченный |
plt.plot( dates, prices, 'g.-')
plt.xlabel( "Дата" ) # Задаем название для оси абсцисс.
plt.ylabel( "Цена") # Задаем название для оси ординат.
Text(0, 0.5, 'Цена')
Упр. Вычислить и нарисовать скользящее среднее по нескольким дням. Обычное среднее, т.е. сумма деленная на количество. Если подать команду (plot) в той же ячейке, то будет обе кривые на графике.
Запись в файл
Текстовые данные можно и в файл обратно записать.
cols = ['date', 'Val']
len(dates), len(prices)
(52, 52)
Если делать через правильную конструкцию:
with open('./output.txt', 'wt') as fwrite:#wt -- write text
fwrite.write( ';'.join(cols) + '\n' )
for i in range(len(dates)):
fwrite.write( str(dates[i]) + ';' + str(prices[i]) + '\n')
В Линукс проверяем так:
cat ./output.txt
date;Val 2020-11-02;62.0 2020-11-09;66.85 2020-11-16;69.75 2020-11-23;70.4 2020-11-30;70.65 2020-12-07;70.35 2020-12-14;77.45 2020-12-21;72.25 2020-12-28;78.65 2021-01-04;77.2 2021-01-11;77.7 2021-01-18;78.3 2021-01-25;74.5 2021-02-01;74.1 2021-02-08;74.55 2021-02-15;77.65 2021-02-22;77.35 2021-03-01;76.65 2021-03-08;77.45 2021-03-15;77.05 2021-03-22;73.75 2021-03-29;70.95 2021-04-05;73.05 2021-04-12;72.45 2021-04-19;80.2 2021-04-26;79.95 2021-05-03;77.5 2021-05-10;80.5 2021-05-17;85.0 2021-05-24;114.0 2021-05-31;107.5 2021-06-07;119.0 2021-06-14;119.4 2021-06-21;113.55 2021-06-28;114.0 2021-07-05;117.2 2021-07-12;115.6 2021-07-19;126.0 2021-07-26;125.25 2021-08-02;130.4 2021-08-09;136.45 2021-08-16;146.0 2021-08-23;159.0 2021-08-30;221.75 2021-09-06;228.9 2021-09-13;256.95 2021-09-20;256.15 2021-09-27;240.95 2021-10-04;258.95 2021-10-11;278.0 2021-10-18;310.2 2021-10-25;312.15
В более сложных, не табличных, текстовых файлах.
Отсечение
'a ggg ggg'.partition(' ') #rpartition
('a', ' ', 'ggg ggg')
tt = 'с 12 час 23 мин по 14 час 31 мин'
ss = tt.partition('по')
ss
('с 12 час 23 мин ', 'по', ' 14 час 31 мин')
# Тогда получится, что
ss[0] # это время с
'с 12 час 23 мин '
ss[0].strip()
'с 12 час 23 мин'
Префикс и суфикс
Истина, если начинается с нужной строки.
'aa'.startswith('a'), 'abcde'.startswith('bc')
(True, False)
'abcde'.startswith('bc', 1) # можно указать номер символа с которого начинать сравнение.
True
'часов'.startswith('час'), 'час'.startswith('час'), 'час'.startswith('часов')
(True, True, False)
Истина, если завершается нужной строкой.
'sdefgh'.endswith('gh'), 'asdfg'.endswith('df')
(True, False)
'asdfg'.endswith('df', 0, -1)
True
'12 часов'.endswith('часов'), '12 минут'.endswith('часов')
(True, False)
ss[0]
'с 12 час 23 мин '
ss[0].startswith('с ')
True
ss[0][2:].strip()
'12 час 23 мин'
ss[0][2:].strip().endswith('мин')
True
ss[2] # это время по.
' 14 час 31 мин'
ss[2].strip()
'14 час 31 мин'
import re
s = 'некий текст.. еще немного текста... и вот завершающий текст.'
len(s)
60
match = re.search('текст', s)
match
<_sre.SRE_Match object; span=(6, 11), match='текст'>
match.span()
(6, 11)
match.pos, match.endpos
(0, 60)
Нам нужно вот это
match.start(), match.end()
(6, 11)
s[match.start():match.end()]
'текст'
Перемещение по всем совпадениям
Следующий текст ищется просто сдвигом строки
match = re.search('текст', s[match.end():] )
match
<_sre.SRE_Match object; span=(15, 20), match='текст'>
При отсутствии искомой подстроки возвращается None.
match = re.search('текст', s[-2:] )
match
ss = s # ss нам будет нужна для сдвига строки.
match = re.search('текст', ss)
while match != None:
print( match.start(), match.end() )
ss = ss[match.end():]
match = re.search('текст', ss )
6 11 15 20 23 28
ss = s
match = re.search('текст', ss)
res = ""
while match != None:
res = res + ss[:match.start()] + 'ТЕКСТ'
ss = ss[match.end():]
match = re.search('текст', ss )
res = res + ss # Не забываем про конец строки.
res
'некий ТЕКСТ.. еще немного ТЕКСТа... и вот завершающий ТЕКСТ.'
i = 0 # Конечно можно и перемещая индекс.
match = re.search('текст', s[i:])
while match != None:
print( match.start(), match.end() )
i += match.end()
match = re.search('текст', s[i:] )
6 11 15 20 23 28
Так можно сделать замену текста.
Есть и итератор
res = ""
last = 0
for match in re.finditer('текст', s):
res = res + s[last:match.start()] + 'ТЕКСТ'
last = match.end()
res = res + s[last:] # Не забываем про конец строки.
res
'некий ТЕКСТ.. еще немного ТЕКСТа... и вот завершающий ТЕКСТ.'
re.sub('текст', "TEXT", s)
'некий TEXT.. еще немного TEXTа... и вот завершающий TEXT.'
Можно даже указать количество замен.
re.sub('текст', "TEXT", s, count = 2)
'некий TEXT.. еще немного TEXTа... и вот завершающий текст.'
Опциональные буквы
s = "This text is about the difference between color and colour."
? -- Позволяет указать, что предыдущий символ опционален, т.е. он может быть, а может и не быть.
match = re.search('colou?r', s)
s[match.start():match.end()]
'color'
match2 = re.search('colou?r', s[match.end():])
s[match.end():] [match2.start():match2.end()]
'colour'
for match in re.finditer('colou?r', s):
print( s[match.start():match.end()] )
color colour
Когда одного не хватает
+ -- говорит о том, что один или много.
s = "Как дела?)))"
match = re.search('\)+', s)
s[match.start():match.end()]
')))'
А если вспомнить, что ставят и :
s = "Как дела?)))\nНорм:)"
print(s)
Как дела?))) Норм:)
for match in re.finditer(':?\)+', s):
print( s[match.start():match.end()] )
))) :)
Подмена на мета символы
ss = s
match = re.search(':?\)+', ss)
res = ""
while match != None:
res = res + ss[:match.start()] + '\U0001F603'#\U unicode, utf8
ss = ss[match.end():]
match = re.search(':?\)+', ss )
print(res)
Как дела?😃 Норм😃
newres = re.sub(':?\)+', '\U0001F603', s)
print(newres)
Как дела?😃 Норм😃
newres == res
True
Можно искать символы по их стандартизованному названию.
print('\N{SMILING FACE WITH OPEN MOUTH}')
😃
print('\N{SMILING FACE WITH OPEN MOUTH AND SMILING EYES}' + '\N{SMIRKING FACE}')
😄😏
ss = s
match = re.search(':?\)+', ss)
res = []
src = []
while match != None:
src = src + [ss[:match.start()]] + [ss[match.start():match.end()]]
res = res + [ss[:match.start()]] + ['\U0001F603']
ss = ss[match.end():]
match = re.search(':?\)+', ss )
print(res)
print(src)
['Как дела?', '😃', '\nНорм', '😃'] ['Как дела?', ')))', '\nНорм', ':)']
Несколько вариантов для буквы
Когда несколько вариантов для буквы. В данном случае М и Ж.
s = 'Некий текст ... Пол: М\n... ещё текст... Пол: Ж... текст'
# Перечислим два возможных варианта буквы.
for match in re.finditer('Пол: [МЖ]', s):# Запятой между ними нет!
print( s[match.start():match.end()] )
Пол: М Пол: Ж
В итоге нашли оба варианта нашлись.
Возможно сколько угодно... хоть 0
s = 'Некий текст ... Пол: М\n... ещё текст... Пол: Ж... текст Пол:М завершим'
re.search('Пол: [МЖ]', s)
# С другим количеством пробелов не сработает.
* -- 0 или более повторений...
for match in re.finditer('Пол: *[МЖ]', s):# Запятой между ними нет!
print( s[match.start():match.end()] )
Пол: М Пол: Ж Пол:М
Все подстроки найдены, даже с нулевым количеством пробелов.
Разбиение строки на подстроки
s = "Маша Катя Даша Лена"
s.split(' ')
['Маша', 'Катя', '', 'Даша', '', '', '', '', 'Лена']
Так не совсем правильно:
re.split(" *", s)
/data/conda/anaconda3/envs/py36/lib/python3.6/re.py:212: FutureWarning: split() requires a non-empty pattern match. return _compile(pattern, flags).split(string, maxsplit)
['Маша', 'Катя', 'Даша', 'Лена']
Упр. В чем ошибка в прошлой ячейке? Как исправить регулярное выражение?
Разные целые слов слов
А если нужно целые слова вместо букв?
s = 'Некий текст ... Пол: муж\n... ещё текст... Пол: жен... текст Пол:муж завершим'
match = re.search('Пол: *муж|жен', s)
s[match.start():match.end()]
'Пол: муж'
for match in re.finditer('Пол: *(муж|жен)', s):
print( s[match.start():match.end()] )
Пол: муж Пол: жен Пол:муж
s = "С январе и октябре"
re.search('январь|февраль', s)
В русском языке нужно убрать окончание:
s = "С январе и октябре"
match = re.search('январ|феврал', s)
s[match.start():match.end()]
'январ'
Система старается сделать строчку как можно более длинной. Укажем ей возможность какого угодно количества букв (\w) в конце.
s = "С январе и октябре"
match = re.search('январ[\w]*|феврал', s)
s[match.start():match.end()]
'январе'
Но можно конечно указать конкретные.
s = "С январе и октябре"
match = re.search('(январ|феврал)[ьяе]', s)
s[match.start():match.end()]
'январе'
На самом деле как мы увидим далее можно применять и для регулярных выражений, т.е. рекурсивно.
Поиск чисел
Что если нужно найти числа? Целые числа. Что есть корректно записанное целое число? Это символ из 1 до 9, а далее символы 0-9 в любом количестве.
s = 'некий 1234.. еще немного 567... ошибка 045..и вот завершающий 89'
for match in re.finditer('[1-9]+[0-9]*', s):
print( s[match.start():match.end()] )
1234 567 45 89
Но сработает так
s = 'ошибка 045..'
match = re.search('[1-9]+[0-9]*', s)
s[match.start():match.end()]
'45'
Нужно указать что перед найденной строкой не было цифр... Для этого есть мета символ ^
s = 'ошибка 045..'
match = re.search('[^0-9][1-9]+[0-9]*', s)
s = 'некий 1234.. еще немного 567... ошибка 045..и вот завершающий 89'
ss = s
match = re.search('[^0-9]?[1-9]+[0-9]*', ss)
res = []
src = []
while match != None:
src = src + [ss[:match.start()]] + [ss[match.start():match.end()]]
res = res + [ss[:match.start()]] + ['<NUM>']
ss = ss[match.end():]
match = re.search('[^0-9][1-9]+[0-9]*', ss )
print(res)
print(src)
['некий', '<NUM>', '.. еще немного', '<NUM>', '... ошибка 045..и вот завершающий', '<NUM>'] ['некий', ' 1234', '.. еще немного', ' 567', '... ошибка 045..и вот завершающий', ' 89']
s = '1234.. еще немного 567... ошибка 045..и вот завершающий 89'
ss = s
match = re.search('([^0-9][1-9]+[0-9]*)|(^[1-9]+)', ss)#! тут |[0-9]*
res = []
src = []
while match != None:
src = src + [ss[:match.start()]] + [ss[match.start():match.end()]]
res = res + [ss[:match.start()]] + ['<NUM>']
ss = ss[match.end():]
match = re.search('[^0-9][1-9]+', ss )#[0-9]*
print(res)
print(src)
['', '<NUM>', '.. еще немного', '<NUM>', '... ошибка 045..и вот завершающий', '<NUM>'] ['', '1234', '.. еще немного', ' 567', '... ошибка 045..и вот завершающий', ' 89']
s = "Вермя 10:23:45 текст"
match = re.search('[0-9][0-9]:[0-9][0-9]:[0-9][0-9]', s)
s[match.start():match.end()]
'10:23:45'
Время найдено, но его все-равно нужно парзисть.
match = re.search('([0-9][0-9]):([0-9][0-9]):([0-9][0-9])', s)
s[match.start():match.end()]
'10:23:45'
Но, теперь вырезаны отдельные найденные части.
match.groups()
('10', '23', '45')
match.group()
'10:23:45'
match.group(1)
# Вывела часы.
'10'
hours = match.group(1)
mins = match.group(2)
secs = match.group(3)
hours, mins, secs
('10', '23', '45')
# Очень важно указать r!
re.sub( '([0-9][0-9]):([0-9][0-9]):([0-9][0-9])', r'\1 часов и \2 минут', s)
# \1 \2 указывают на номер группы
'Вермя 10 часов и 23 минут текст'
match = re.search('(?P<часы>[0-9][0-9]):(?P<минуты>[0-9][0-9]):(?P<секунды>[0-9][0-9])', s)
s[match.start():match.end()]
'10:23:45'
Теперь можно и так:
match.groupdict()
{'часы': '10', 'минуты': '23', 'секунды': '45'}
match.group('часы')
'10'
# Очень важно указать r!
re.sub( '(?P<часы>[0-9][0-9]):(?P<минуты>[0-9][0-9]):(?P<секунды>[0-9][0-9])', r'\g<часы> часов и \g<минуты> минут', s)
# \1 \2 указывают на номер группы
'Вермя 10 часов и 23 минут текст'
Как их много
s
'Вермя 10:23:45 текст'
rtime = '(?P<часы>[0-9][0-9]):(?P<минуты>[0-9][0-9]):(?P<секунды>[0-9][0-9])'
re.findall( rtime, 'd' +s+'ab')
[('10', '23', '45')]
'(?P<часы>[0-9][0-9]):(?P<минуты>[0-9][0-9]):(?P<секунды>[0-9][0-9])|(?P<words>[abcd]+)'
'(?P<часы>[0-9][0-9]):(?P<минуты>[0-9][0-9]):(?P<секунды>[0-9][0-9])|(?P<words>[abcd]+)'
re.findall(rtime+'|(?P<words>[abcd]+)', 'd' +s+'ab')
[('', '', '', 'd'), ('10', '23', '45', ''), ('', '', '', 'ab')]
re.findall("a*","abcdabfga")
['a', '', '', '', 'a', '', '', '', 'a', '']
rall = rtime+'|(?P<words>[abcd]+)'
rall
'(?P<часы>[0-9][0-9]):(?P<минуты>[0-9][0-9]):(?P<секунды>[0-9][0-9])|(?P<words>[abcd]+)'
Упр. Как извлечь название групп? Должно получится ['часы', 'минуты', 'секунды', 'words']
???
Собственным циклом
s = '23+56*8-6/3+8'
ss = s
rnum = '(?P<NUM>[1-9]+[0-9]*)'
rops = '(?P<OP>[+-/*//])'
rr = rnum + '|' + rops
match = re.search( rr, ss)
res = []
src = []
k=0
while match != None:
k+=1
src = src + [ss[:match.start()]] + [ss[match.start():match.end()]]
res = res + [ss[:match.start()]]
ans = match.groups()
if ans[0]:
res += ['<NUM>']
elif ans[1]:
res += ['<OP>']
ss = ss[match.end():]
match = re.search(rr, ss )
print(res)
print(src)
['', '<NUM>', '', '<OP>', '', '<NUM>', '', '<OP>', '', '<NUM>', '', '<OP>', '', '<NUM>', '', '<OP>', '', '<NUM>', '', '<OP>', '', '<NUM>'] ['', '23', '', '+', '', '56', '', '*', '', '8', '', '-', '', '6', '', '/', '', '3', '', '+', '', '8']
res = [i for i in res if len(i)]# Уберем пустые строки.
res
['<NUM>', '<OP>', '<NUM>', '<OP>', '<NUM>', '<OP>', '<NUM>', '<OP>', '<NUM>', '<OP>', '<NUM>']
src = [i for i in src if len(i)]
src
['23', '+', '56', '*', '8', '-', '6', '/', '3', '+', '8']
len(src), len(res)
(11, 11)
i = 0
while i < len(res):
if res[i] == '<OP>':
if src[i] == '*':
v = float(src[i-1]) * float(src[i+1])
elif src[i] == '/':
v = float(src[i-1]) / float(src[i+1])
else:
i += 1
continue
src[i-1:i+2] = [v]
res[i-1:i+2] = ['<NUM>']
i -= 2
i += 1
src
['23', '+', 448.0, '-', 2.0, '+', '8']
i = 0
while i < len(res):
if res[i] == '<OP>':
if src[i] == '+':
v = float(src[i-1]) + float(src[i+1])
elif src[i] == '-':
v = float(src[i-1]) - float(src[i+1])
else:
i += 1
continue
src[i-1:i+2] = [v]
res[i-1:i+2] = ['<NUM>']
i -= 2
i += 1
src
[477.0]
23+56*8-6/3+8
477.0
Методом
s
'23+56*8-6/3+8'
tkns = re.findall(rr, '23+7-5+99-56*2-7+21/7+6-2*3')
tkns
[('23', ''), ('', '+'), ('7', ''), ('', '-'), ('5', ''), ('', '+'), ('99', ''), ('', '-'), ('56', ''), ('', '*'), ('2', ''), ('', '-'), ('7', ''), ('', '+'), ('21', ''), ('', '/'), ('7', ''), ('', '+'), ('6', ''), ('', '-'), ('2', ''), ('', '*'), ('3', '')]
ops = []
nums = []
while len(tkns) or len(ops):
print("?", ops, nums)
val = None
if len(tkns):
val = tkns.pop(0)
if val[1]=='':
nums.append(val[0])
continue
elif val[1] in ['*','/']:
ops.append(val[1])
continue
elif val[1] in ['+','-']:
pass
while len(ops):
print('<<', ops, nums)
val2 = ops.pop()
##print(val2)
v1 = float(nums.pop())
v2 = float(nums.pop())
if val2 == '/':
vv = v2/v1
elif val2 == '*':
vv = v1*v2
elif val2 == '+':
vv = v2+v1
elif val2 == '-':
vv = v2-v1
nums.append(vv)
print('>>', ops, nums)
if val != None:
ops.append(val[1])
? [] [] ? [] ['23'] ? ['+'] ['23'] ? ['+'] ['23', '7'] << ['+'] ['23', '7'] >> [] [30.0] ? ['-'] [30.0] ? ['-'] [30.0, '5'] << ['-'] [30.0, '5'] >> [] [25.0] ? ['+'] [25.0] ? ['+'] [25.0, '99'] << ['+'] [25.0, '99'] >> [] [124.0] ? ['-'] [124.0] ? ['-'] [124.0, '56'] ? ['-', '*'] [124.0, '56'] ? ['-', '*'] [124.0, '56', '2'] << ['-', '*'] [124.0, '56', '2'] >> ['-'] [124.0, 112.0] << ['-'] [124.0, 112.0] >> [] [12.0] ? ['-'] [12.0] ? ['-'] [12.0, '7'] << ['-'] [12.0, '7'] >> [] [5.0] ? ['+'] [5.0] ? ['+'] [5.0, '21'] ? ['+', '/'] [5.0, '21'] ? ['+', '/'] [5.0, '21', '7'] << ['+', '/'] [5.0, '21', '7'] >> ['+'] [5.0, 3.0] << ['+'] [5.0, 3.0] >> [] [8.0] ? ['+'] [8.0] ? ['+'] [8.0, '6'] << ['+'] [8.0, '6'] >> [] [14.0] ? ['-'] [14.0] ? ['-'] [14.0, '2'] ? ['-', '*'] [14.0, '2'] ? ['-', '*'] [14.0, '2', '3'] << ['-', '*'] [14.0, '2', '3'] >> ['-'] [14.0, 6.0] << ['-'] [14.0, 6.0] >> [] [8.0]
ops, nums
([], [8.0])
23+56*2-7+21/7+6
137.0
23+7-5+99-56*2-7+21/7+6-2*3
8.0