Сканеры файлов

skanery fajlov Инструменты для работы с файлами и каталогами

Прежде чем закончить обзор инструментов для работы с файлами, реализуем более практичную задачу и проиллюстрируем кое-что из того, что мы уже видели. В отличие от некоторых языков командной оболочки, в Python нет неявной процедуры циклического сканирования файла, но написать такую универсальную процедуру, пригодную для многократного использования, несложно. Модуль в примере 4.1 определяет универсальную процедуру сканирования файлов, которая просто применяет переданную в нее функцию к каждой строке внешнего файла.

Пример 4.1. PP4E\System\Filetools\scanfile.py

def scanner(name, function):

file = open(name, ‘r’) # создать объект файла

while True:

line = file.readline() # вызов методов файла

if not line: break # до конца файла

function(line) # вызвать объект функции

file.close()

Функции scanner безразлично, какая функция обработки строк в нее передана, чем и определяется ее универсальность: она готова применить любую функцию одного аргумента, уже существующую или которая может появиться в будущем, ко всем строкам в текстовом файле. Если реализацию этого модуля поместить в каталог, входящий в путь поиска модулей, им можно будет воспользоваться всякий раз, когда потребуется выполнить построчный обход файл. В примере 4.2 приводится клиентский сценарий, выполняющий простое преобразование строк.

Пример 4.2. PP4E\System\Filetools\commands.py

#!/usr/local/bin/python

from sys import argv

from scanfile import scanner

class UnknownCommand(Exception): pass

def processLine(line): # определить функцию,

if line[0] == ‘*’: # применяемую к каждой строке

print(“Ms.”, line[1:-1])

elif line[0] == ‘+’:

print(“Mr.”, line[1:-1]) # отбросить первый и последний символы

else:

raise UnknownCommand(line) # возбудить исключение

filename = ‘data.txt’

if len(argv) == 2: filename = argv[1] # аргумент командной строки с именем scanner(filename, processLine) # файла запускает сканер

Для текстового файла hillbillies.txt:

*Granny

+Jethro

*Elly May

+”Uncle Jed”

наш сценарий commands.py вернет следующие результаты:

C:\\PP4E\System\Filetools> python commands.py hillbillies.txt

Ms. Granny Mr. Jethro Ms. Elly May Mr. Uncle Jed

Все работает, тем не менее существует множество альтернативных способов реализации обоих примеров, и какие-то из них могут предлагать более удачные решения. Например, процессор команд, представленный в примере 4.2, можно было бы реализовать, как показано ниже. Преимущества этой реализации становятся более очевидными с ростом обрабатываемых вариантов — управляемый данными подход может оказаться короче и проще в сопровождении, чем длинная инструкция if с избыточными, по сути, действиями (если вам когда-нибудь потребуется изменить способ вывода строк, в следующей реализации вы сможете сделать это, изменив всего одну строку):

commands = {‘*’: ‘Ms.’, ‘+’: ‘Mr.’} # данные изменять проще, чем код?

def processLine(line):

try:

print(commands[line[0]], line[1:-1])

except KeyError:

raise UnknownCommand(line)

Сканер также можно было бы улучшить. Как правило, перемещение обработки из программного кода Python во встроенные инструменты приводит к увеличению скорости. Например, если скорость имеет большое значение, сканер файлов можно было бы сделать быстрее, заменив в примере 4.1 вызов функции readline итератором объекта файла (в эффективности которого вы уже имели возможность убедиться):

def scanner(name, function):

for line in open(name, ‘r’): # построчное сканирование

function(line) # вызов объекта функции

Еще больших чудес в примере 4.1 можно достичь с помощью таких инструментов итераций, как встроенная функция map, генераторы списков и выражения-генераторы. Ниже приводится минималистская версия. Цикл for замещается вызовом функции map или генератором, и Python сам закрывает файл на этапе сборки мусора или при выходе из сценария (в процессе обработки во всех реализациях создается список результатов, однако такое неэкономное расходование ресурсов вполне допустимо, за исключением очень больших файлов):

def scanner(name, function): list(map(function, open(name, ‘r’)))

def scanner(name, function):

[function(line) for line in open(name, ‘r’)]

def scanner(name, function):

list(function(line) for line in open(name, ‘r’))

Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011

Оцените статью
Секреты программирования
Добавить комментарий