Работа с файлами
Оператор with
Заранее задать finally (например, закрыть открытый файл, даже если были исключения)
протокол контекстного менеджера
.__enter__() / .__exit__(exc_type, exc_val, exc_tb)
- →
>>> Working <class 'SyntaxError'> шhат? <traceback object at 0x7efe7082cd80> <<< Done
Обратите внимание на то, что третья строка (сообщение о необработанном исключении) может, формально говоря, появиться в любом месте после вывода «Working», потому что выводится на stderr, а все остальные сообщения — на stdout
Что будет, если передать False вместо True?
Далее см. contextlib.html
Байтовые структуры
Тип bytes — как str, только байты
- Ой, не совсем ☺:
- Часто прилетают пр работе с функциями из Си, сетевыми протоколами и др. данными, за которые Питон отвечать не хочет
- (в Python2 все такими было, кое-где может встретиться legacy)
Тип bytearray
Изменяемый bytes
Всё это нужно не очень часто, есть либо NumPy, либо array
бНОПНЯ
Как преобразовать из str в bytes и обратно?
- Понятие кодировки. Unicode, UTF* и прочее
Кодировка: соответствие некоторого числа конкретному символу (например, описанному в Unicode)
- Тысячи их
UTF-8: то ли один байт, то ли два (то ли три, то ли четыре)
- Однобайтные кодировки: без указания кодировки нельзя понять, что написано
1 >>> txt = "Вопрос" 2 >>> print(*map(hex, map(ord, txt))) 3 0x412 0x43e 0x43f 0x440 0x43e 0x441 4 >>> txt.encode() 5 b'\xd0\x92\xd0\xbe\xd0\xbf\xd1\x80\xd0\xbe\xd1\x81' 6 >>> sys.getdefaultencoding() 7 'utf-8' 8 >>> txt.encode('utf-8') 9 b'\xd0\x92\xd0\xbe\xd0\xbf\xd1\x80\xd0\xbe\xd1\x81' 10 >>> txt.encode('WINDOWS-1251') 11 b'\xc2\xee\xef\xf0\xee\xf1' 12 >>> txt.encode('KOI8-R') 13 b'\xf7\xcf\xd0\xd2\xcf\xd3' 14 >>> txt.encode('WINDOWS-1251').decode('KOI8-R') 15 'бНОПНЯ' 16
- Представление строк внутри Python: т. н. «питоний unicode» (двухбайтовый)
- Если в строке нет ни одного не-ASCII символа, она хранится побайтово, но это абсолютно прозрачно, кроме размера:
Просто файлы
open(), r/w/a и т. п.
.read()/.write()/.readline()
файл как итератор, .readlines()
.seek(),.tell()
текстовые и двоичные: "b"/"t"
файл и with, зачем нужно
Стандартный ввод-вывод
Как обычно, sys.stdin, sys.stdout и sys.stderr — это файлы стандартного ввода, вывода и вывода ошибок соответственно
К ним можно применять файловые операции (даже .seek(), но это только если соответствующий файл — это файл, а не терминал или конвейер (.seekable())
Например, sys.stdout.write(что_то_там) сконвертирует в строку и запишет на стандартный вывод всё что_то_там (не добавляя перевод строки, разумеется)
Это текстовые файлы
Двоичные аналоги — sys.stdin.buffer, sys.stdout.buffer и sys.stderr.buffer
Например, можно написать так: b = sys.stdin.buffer.readline(), при этом в b окажется объект типа bytes
Типизированные файлы
В просто файлы записываются только строки или байты. А если надо записать float, причём не в виде строки?
Сериализация
Чтение и запись объектов Python
pickle.dumps(obj) / pickle.dump(obj, file)
pickle.loads(bytes_object) / pickle.load(file)
1 >>> import pickle 2 >>> pickle.dumps(0x14131211) 3 b'\x80\x04\x95\x06\x00\x00\x00\x00\x00\x00\x00J\x11\x12\x13\x14.' 4 >>> pickle.dumps(0x14131211)[-5:] 5 b'\x11\x12\x13\x14.' 6 >>> du = pickle.dumps(123.123e20) 7 >>> du 8 b'\x80\x04\x95\n\x00\x00\x00\x00\x00\x00\x00GD\x84\xdb\x9b\xe5\x05\x1cP.' 9 >>> ud = pickle.loads(du) 10 >>> ud 11 1.23123e+22 12 >>> F = open("serialized", "bw") 13 >>> pickle.dump(100500, F) 14 >>> pickle.dump([1, "WER", None], F) 15 >>> pickle.dump(b"QWWER", F) 16 >>> F.close() 17 >>> F = open("serialized", "br") 18 >>> pickle.load(F) 19 100500 20 >>> pickle.load(F) 21 [1, 'WER', None] 22 >>> pickle.load(F) 23 b'QWWER' 24 >>> class C: 25 ... A = 3 26 ... 27 >>> c = C() 28 >>> du = pickle.dumps(c) 29 >>> ud = pickle.loads(du) 30 >>> ud 31 <__main__.C object at 0x7f0e60c502b0> 32 >>> ud.A 33 3 34 >>> ud = pickle.loads(du) 35 Traceback (most recent call last): 36 File "<stdin>", line 1, in <module> 37 AttributeError: Can't get attribute 'C' on <module '__main__' (<_frozen_importlib_external.SourceFileLoader object at 0x7f0e60c8bac0>)> 38 >>> class C: 39 ... E = 100500 40 ... 41 >>> ud = pickle.loads(du) 42 >>> ud 43 <__main__.C object at 0x7f0e60b50b20> 44 >>> ud.A 45 Traceback (most recent call last): 46 File "<stdin>", line 1, in <module> 47 AttributeError: 'C' object has no attribute 'A' 48 >>> ud.E 49 100500 50
небезопасно
Структуры типа Си
Но в чём же проблема просто взять и преобразовать объект в байты? В память слазить, наконец?
- Остроконечники и тупоконечники (порядок байтов в слове), sys.byteorder
- Выравнивание
Пример: заголовок PNG
1 import struct 2 import zlib 3 import sys 4 5 HEADER = "8B" 6 CHUNK = "!I4s" 7 CRC = "!I" 8 IHDR = "!IIBBBBB" 9 10 def readpack(fmt, fle): 11 return struct.unpack(fmt, fle.read(struct.calcsize(fmt))) 12 13 payload = b'' 14 with open(sys.argv[1], "br") as f: 15 png = readpack(HEADER, f) 16 print(*map(hex, png)) 17 while (chunk := readpack(CHUNK, f))[1] != b"IEND": 18 print(*chunk) 19 data = f.read(chunk[0]) 20 crc = readpack(CRC, f) 21 if chunk[1] == b"IHDR": 22 w, h, bpp, col, comp, filt, i = struct.unpack(IHDR, data) 23 print(f"{w}×{h}, {bpp=}, {col=}, {comp=}, {filt=}, {i=}") 24 elif chunk[1] == b"IDAT": 25 payload += data 26 27 print(len(payload), w, h, w*h) 28 payload = zlib.decompress(payload) 29 print(len(payload), w, h, w*h)
Базы данных и dict-like итерфейс
- Идея: интерфейс словаря (ключ:значение) + быстрый поиск под капотом
1 >>> import dbm 2 >>> F = dbm.open("data.db", "c") 3 >>> F["qwe"] = "rty" 4 >>> F["asd"] = "zxcv" 5 >>> F["qerw"] = "werw" 6 >>> F["123"] = "123" 7 >>> 8 >>> F["asd"] 9 b'zxcv' 10 >>> F[b"asd"] 11 b'zxcv' 12 >>> F["Ы"] = "Ы" 13 >>> F["Ы"] 14 b'\xd0\xab' 15 >>> F.close() 16 >>> F = dbm.open("data.db", "r") 17 >>> F["qerw"] 18 b'werw' 19 >>> k = F.firstkey() 20 >>> k 21 b'\xd0\xab' 22 >>> while k: 23 ... print(F[k]) 24 ... k = F.nextkey(k) 25 ... 26 b'\xd0\xab' 27 b'rty' 28 b'zxcv' 29 b'werw' 30 b'123' 31
Файлы с известной структурой
Д/З
Внимание:
ни input(), ни тем более eval(input()) в решениях данных задач использовать не получится, надо использовать файловые операции ввода-вывода.
Поскольку idle (и, возможно, другие IDE) подменяет стандартный ввод-вывод для того, чтобы красиво работать с окошком, программу надо запускать ручками из командной строки примерно так:
… python3 программа.py < тестовый_файл
Собственно, задание:
Прощёлкать примеры с файлами в Tutorial, а также примеры по pickle и struct
EJudge: ZipInfo 'Размер архива'
Написать программу, которой на стандартный ввод подаётся zip-архив в виде шестнадцатеричного дампа (последовательность шестнадцатеричных цифр, возможно, разделённых пробелами и переводами строки), а на выходе она показывает количество и суммарный объём хранящихся в нём файлов, если их распаковать.
504b03040a0000000000d6a07c5100000000000000000000000002001c00 6f2f5554090003a483c25fab83c25f75780b000104f501000004f5010000 504b03040a00000000000ea77c5100000000000000000000000004001c00 6f2f312f55540900034c8fc25f568fc25f75780b000104f501000004f501 0000504b03040a0000000000d8a07c510000000000000000000000000600 1c006f2f312f352f5554090003a783c25fab83c25f75780b000104f50100 0004f5010000504b03040a00000000000da77c5100000000000000000000 000006001c006f2f312f322f5554090003498fc25f568fc25f75780b0001 04f501000004f5010000504b03040a00000000000da77c514b8325172100 0000210000000a001c006f2f312f322f646174655554090003498fc25f56 8fc25f75780b000104f501000004f5010000d0a1d0b120d0bdd0bed18f20 32382032303a35363a3235204d534b20323032300a504b03040a00000000 0066a67c5100000000000000000000000008001c006f2f312f322f332f55 54090003108ec25f3f8ec25f75780b000104f501000004f5010000504b03 040a00000000000aa77c51ba7488890b0000000b0000000b001c006f2f31 2f322f332f63616c5554090003438fc25f568fc25f75780b000104f50100 0004f5010000323032302d31312d32380a504b03040a0000000000d6a07c 510000000000000000000000000a001c006f2f312f322f332f342f555409 0003a483c25fab83c25f75780b000104f501000004f5010000504b03040a 00000000000ea77c5100000000000000000000000008001c006f2f312f6e 6f6e6555540900034c8fc25f568fc25f75780b000104f501000004f50100 00504b01021e030a0000000000d6a07c5100000000000000000000000002 0018000000000000001000ed41000000006f2f5554050003a483c25f7578 0b000104f501000004f5010000504b01021e030a00000000000ea77c5100 0000000000000000000000040018000000000000001000ed413c0000006f 2f312f55540500034c8fc25f75780b000104f501000004f5010000504b01 021e030a0000000000d8a07c510000000000000000000000000600180000 00000000001000ed417a0000006f2f312f352f5554050003a783c25f7578 0b000104f501000004f5010000504b01021e030a00000000000da77c5100 0000000000000000000000060018000000000000001000ed41ba0000006f 2f312f322f5554050003498fc25f75780b000104f501000004f501000050 4b01021e030a00000000000da77c514b83251721000000210000000a0018 000000000001000000a481fa0000006f2f312f322f646174655554050003 498fc25f75780b000104f501000004f5010000504b01021e030a00000000 0066a67c51000000000000000000000000080018000000000000001000ed 415f0100006f2f312f322f332f5554050003108ec25f75780b000104f501 000004f5010000504b01021e030a00000000000aa77c51ba7488890b0000 000b0000000b0018000000000001000000a481a10100006f2f312f322f33 2f63616c5554050003438fc25f75780b000104f501000004f5010000504b 01021e030a0000000000d6a07c510000000000000000000000000a001800 0000000000001000ed41f10100006f2f312f322f332f342f5554050003a4 83c25f75780b000104f501000004f5010000504b01021e030a0000000000 0ea77c51000000000000000000000000080018000000000000000000a481 350200006f2f312f6e6f6e6555540500034c8fc25f75780b000104f50100 0004f5010000504b05060000000009000900b7020000770200000000
3 44
EJudge: BnopnyAnna 'Неудачная перекодировка'
Наши любимые Лев Николаевич и Анна Аркадьевна вновь пустились в приключения! На этот раз некто взял случайный фрагмент из 75 подряд идущих строк файла, и попытался их перекодировать. Но не точно из кодировки utf-8, а из случайной (набор разрешённых кодировок см. ниже). Кодировка, в которую происходило перекодирование, тоже была выбрана случайно из того же набора. Операция прошла успешно! Возможно, не один раз, а два или даже три (но не больше). Получилась редкостная бНОПНЯ. Вывести первую строку исходного фрагмента, в предположении, что обратная перекодировка также возможна.
- Набор допустимых кодировок:
- cp1026 cp1140 cp1256 cp273 cp437 cp500 cp775 cp850 cp852 cp855 cp857 cp860 cp861 cp862 cp863 cp865 cp866 gb18030 hp_roman8 iso8859_10 iso8859_11 iso8859_13 iso8859_14 iso8859_15 iso8859_16 iso8859_2 iso8859_4 iso8859_5 iso8859_9 koi8_r mac_cyrillic mac_greek mac_latin2 mac_roman utf_8
- Ввод делать со стандартного ввода (не из файла)
Дополнение. Пока во временнЫе рамки укладывается только решение, в котором на любом этапе перекодировки участвуют только такие пары, которые одновременно пригодны и для исходного файла, т. е. для первого этапа. Поскольку совершенно не факт, что на втором этапе не появятся новые варианты, в этом году будем решать задачу с этим дополнительным допущением.
…содержимое файла…
она. - Не говорите, пожалуйста, со мной про оперу, вы ничего не понимаете в
- Это довольно сложная задача
- Набор допустимых кодировок: