Различия между версиями 7 и 8
Версия 7 от 2021-11-28 23:19:59
Размер: 10295
Редактор: FrBrGeorge
Комментарий:
Версия 8 от 2021-11-30 17:07:45
Размер: 10758
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 265: Строка 265:
'''Внимание''' ни `input()`, ни тем более `eval(input())` в решениях данных задач использовать не получится, надо использовать [[#stdio|файловые операции ввода-вывода]]. '''Внимание''':
 1.
ни `input()`, ни тем более `eval(input())` в решениях данных задач использовать не получится, надо использовать [[#stdio|файловые операции ввода-вывода]].
 1. Поскольку `idle` (и, возможно, другие IDE) подменяет стандартный ввод-вывод для того, чтобы красиво работать с окошком, программу надо запускать ручками из командной строки примерно так:
  {{{
  … python3 программа.py < тестовый_файл
  }}}
Собственно, задание:

Работа с файлами

Оператор with

  • Заранее задать finally (например, закрыть открытый файл, даже если были исключения)

  • протокол контекстного менеджера

    • .__enter__() / .__exit__(exc_type, exc_val, exc_tb)

         1 class CM:
         2     def __init__(self, val = None):
         3         self.val = val
         4 
         5     def __enter__(self):
         6         print(">>>")
         7 
         8     def __exit__(self, *args):
         9         print("<<<",*(c for c in args if c))
        10         return self.val
        11 
        12 with CM(True) as f:
        13     print("Working")
        14     raise SyntaxError("шhат?")
        15 
        16 print("Done")
      
      >>>
      Working
      <class 'SyntaxError'> шhат? <traceback object at 0x7efe7082cd80>
      <<<
      Done
      • Обратите внимание на то, что третья строка (сообщение о необработанном исключении) может, формально говоря, появиться в любом месте после вывода «Working», потому что выводится на stderr, а все остальные сообщения — на stdout

      • Что будет, если передать False вместо True?

Далее см. contextlib.html

Байтовые структуры

  • Тип bytes — как str, только байты

    • Ой, не совсем ☺:
         1 >>> a = b'weqrtwert'
         2 >>> a[4]
         3 116
         4 
      
    • Часто прилетают пр работе с функциями из Си, сетевыми протоколами и др. данными, за которые Питон отвечать не хочет
    • (в Python2 все такими было, кое-где может встретиться legacy)
  • Тип bytearray

    • Изменяемый bytes

         1 >>> b = bytearray(range(65,72))
         2 >>> b
         3 bytearray(b'ABCDEFG')
         4 >>> b[3]
         5 68
         6 >>> b[2] ^= 0o40
         7 >>> b
         8 bytearray(b'ABcDEFG')
         9 
      

Всё это нужно не очень часто, есть либо 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 символа, она хранится побайтово, но это абсолютно прозрачно, кроме размера:
           1 >>> sys.getsizeof("qwe")
           2 52
           3 >>> sys.getsizeof("qwer")
           4 53
           5 >>> sys.getsizeof("qwert")
           6 54
           7 >>> sys.getsizeof("qwertЫ")
           8 86
           9 >>> sys.getsizeof("qwertЫs")
          10 88
          11 >>> sys.getsizeof("qwertЫsf")
          12 90
          13 
        
  • codecs

Просто файлы

В tutorial

  • 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

    • 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 
      
    • Pickling Class Instances

    • <!> небезопасно

Структуры типа Си

Но в чём же проблема просто взять и преобразовать объект в байты? В память слазить, наконец?

struct

  • Format Characters

  • Остроконечники и тупоконечники (порядок байтов в слове), 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 итерфейс

dbm

  • Идея: интерфейс словаря (ключ:значение) + быстрый поиск под капотом
       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 
    

Файлы с известной структурой

  • Тысячи их, часть поддерживают файловый протокол, часть — нет
  • Пример: gzip

  • Пример: zipfile, zipapp

Д/З

Внимание:

  1. ни input(), ни тем более eval(input()) в решениях данных задач использовать не получится, надо использовать файловые операции ввода-вывода.

  2. Поскольку idle (и, возможно, другие IDE) подменяет стандартный ввод-вывод для того, чтобы красиво работать с окошком, программу надо запускать ручками из командной строки примерно так:

    •   … python3 программа.py < тестовый_файл 

Собственно, задание:

  1. Прощёлкать примеры с файлами в Tutorial, а также примеры по pickle и struct

  2. EJudge: ZipInfo 'Размер архива'

    Написать программу, которой на стандартный ввод подаётся zip-архив в виде шестнадцатеричного дампа (последовательность шестнадцатеричных цифр, возможно, разделённых пробелами и переводами строки), а на выходе она показывает количество и суммарный объём хранящихся в нём файлов, если их распаковать.

    Input:

    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
    Output:

    3 44
  3. 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
    • Ввод делать со стандартного ввода (не из файла)
    • Дополнение. Пока во временнЫе рамки укладывается только решение, в котором на любом этапе перекодировки участвуют только такие пары, которые одновременно пригодны и для исходного файла, т. е. для первого этапа. Поскольку совершенно не факт, что на втором этапе не появятся новые варианты, в этом году будем решать задачу с этим дополнительным допущением.

    Input:

    …содержимое файла…
    Output:

    она. - Не говорите, пожалуйста, со мной про оперу, вы ничего не понимаете  в
    • Это довольно сложная задача

LecturesCMC/PythonIntro2021/11_Files (последним исправлял пользователь FrBrGeorge 2021-11-30 17:07:45)