Вернемся к перемещению больших файлов по дому: после загрузки больших файлов игровых программ вы можете воспользоваться предыдущим сценарием split.py, щелкнув на нем в окне Проводника Windows (Windows Explorer) и введя имена файлов. После разрезания просто скопируйте каждый файл фрагмента на отдельную дискету (или на более современный носитель), перейдите с дискетами к требуемому компьютеру и скопируйте файлы фрагментов с дискет. Затем щелкните на файле сценария из примера 6.6 (или запустите его другим способом), чтобы вновь объединить фрагменты.
Пример 6.6. PP4E\System\Filetools\join.py
#!/usr/bin/python
############################################################################## объединяет все файлы фрагментов, имеющиеся в каталоге и созданные с помощью сценария split.py,воссоздавая первоначальный файл.
По своему действию этот сценарий напоминает команду ‘cat fromdir/* > tofile’ в Unix, но данная реализация более переносимая и настраиваемая; сценарий экспортирует операцию объединения в виде функции, доступной для многократного использования. Зависит от порядка сортировки имен файлов, поэтому все они должны быть одинаковой длины. Сценарии разрезания/объединения можно дополнить возможностью вывода диалога с графическим интерфейсом tkinter, позволяющего выбирать файлы.
##############################################################################
import os, sys readsize = 1024
def join(fromdir, tofile):
output = open(tofile, ‘wb’) parts = os.listdir(fromdir) parts.sort()
for filename in parts:
filepath = os.path.join(fromdir, filename)
fileobj = open(filepath, ‘rb’) while True:
filebytes = fileobj.read(readsize) if not filebytes: break
output.write(filebytes)
fileobj.close() output.close()
if __name__ == ‘__main__’:
if len(sys.argv) == 2 and sys.argv[1] == ‘-help’:
print(‘Use: join.py [from-dir-name to-file-name]’) else:
if len(sys.argv) != 3: interactive = True fromdir = input(‘Directory containing part files? ‘) tofile = input(‘Name of file to be recreated? ‘) else:
interactive = False
fromdir, tofile = sys.argv[1:]
absfrom, absto = map(os.path.abspath, [fromdir, tofile]) print(‘Joining’, absfrom, ‘to make’, absto)
try:
join(fromdir, tofile)
except:
print(‘Error joining files:’)
print(sys.exc_info()[0], sys.exc_info()[1]) else:
print(‘Join complete: see’, absto)
if interactive: input(‘Press Enter key’) # пауза, если сценарий # запущен щелчком мыши
Ниже приводится пример объединения файлов фрагментов в Windows, созданных нами минуту назад. После выполнения сценария join вам
может потребоваться воспользоваться какими-нибудь другими утилитами, такими как zip, gzip или tar, чтобы распаковать архивный файл, если он поставлялся не исполняемым, но в любом случае загруженный файл будет готов к дальнейшему использованию:[XXI]
C:\temp> python C:\…\PP4E\System\Filetools\join.py -help
Use: join.py [from-dir-name to-file-name]
C:\temp> python C:\…\PP4E\System\Filetools\join.py pysplit mypy31.msi
Joining C:\temp\pysplit to make C:\temp\mypy31.msi
Join complete: see C:\temp\mypy31.msi
C:\temp> dir *.msi
…часть строк опущена…
02/21/2010 11:21 AM
06/27/2009 04:53 PM
2 File(s)
0 Dir(s)
C:\temp> fc /b mypy31.msi python-3.1.msi
Comparing files mypy31.msi and PYTHON-3.1.MSI FC: no differences encountered
Чтобы выбрать все части файла, присутствующие в каталоге, сценарий join использует функцию os.listdir и сортирует список имен файлов, чтобы расположить части в правильном порядке. Получается точная байт-в-байт копия исходного файла (что проверяется выше командой DOS fc; в Unix используйте команду cmp).
Конечно, часть этого процесса выполняется вручную (я еще не придумал, как запрограммировать этап «перехода с дискетами к другому компьютеру»), но с помощью сценариев split и join перемещение больших файлов становится быстрым и простым. Так как этот сценарий является также переносимым программным кодом Python, он выполняется на любой платформе, на которую может понадобиться перенести разрезанные файлы. Например, у меня дома есть компьютеры, работающие под управлением не только Windows, но и Linux; а так как сценарий выполняется на любой из платформ, у моих игроков не возникает проблем. Однако, прежде чем двинуться дальше, рассмотрим несколько особенностей реализации сценария join:
Чтение файлов блоками целиком
Прежде всего, обратите внимание, что сценарий работает с файлами в двоичном режиме, а также читает файлы фрагментов блоками размером в 1 Кбайт. Значение readsize (размер блоков, читаемых из входного файла) не имеет никакого отношения к chunksize в сценарии split.py (общий размер каждого выходного файла). Как было показано в главе 4, каждый файл фрагмента можно было бы прочесть сразу целиком: output.write(open(filepath, ‘rb’).read()). Недостаток такого приема в том, что при этом весь файл целиком загружается в оперативную память. Например, при чтении файла фрагмента размером 1.4 Мбайт в память целиком в ней будет создана строка размером 1.4 Мбайт, содержащая все байты файла. Поскольку сценарий split разрешает пользователям указывать еще более крупные размеры фрагментов, сценарий join ожидает худшего и читает содержимое файлов блоками ограниченного размера. Полная надежность была бы обеспечена, если бы сценарий split также читал исходный файл меньшими порциями, но на практике в этом нет необходимости (напомню, что в процессе выполнения программы интерпретатор автоматически утилизирует строки, на которые нет ни одной ссылки, поэтому данная реализация не так расточительна, как могло бы показаться).
Сортировка имен файлов
Если внимательно изучить реализацию сценария, можно заметить, что порядок объединения полностью зависит от порядка сортировки имен файлов в каталоге с файлами фрагментов. Так как сценарий объединения просто вызывает метод sort списка имен файлов, возвращаемого функцией os.listdir, он подразумевает, что при разрезании создаются файлы с именами одинаковой длины и имеющими один и тот же формат. Чтобы удовлетворить это требование, сценарий split использует выражение форматирования (‘part%04d’), которое добавляет незначащие нули и тем самым обеспечивает присутствие одинакового количества цифр (четырех) в именах файлов. Наличие ведущих нулей в маленьких числах гарантирует, что имена файлов фрагментов будут отсортированы правильно.
При желании можно было бы извлекать цифры из имен файлов, преобразовывать их в значения int и выполнять числовую сортровку, воспользовавшись аргументом keys метода sort списков, но в этом случае все равно необходимо, чтобы все имена файлов начинались с подстроки некоторого вида, и это не устранило бы зависимость между сценариями split и join. Однако поскольку эти сценарии задуманы как два этапа одного и того же процесса, какие-то зависимости между ними выглядят вполне оправданными.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011