среда, 13 июля 2011 г.

Python: модули fnmatch и glob. Перевод документации с комментариями и примерами

Модуль fnmatch - соответствие шаблонам имен файлов UNIX.
Модуль предоставляет поддержку подстановок в стиле оболочки UNIX, которые не являются тем же самым что и регулярные выражения, описанные в модуле re. Специальными символами являются:

Шаблон Значение
* Совпадает любое количество (включая ноль) символов
? Совпадает любой одиночный символ
[foo] Совпадает любой символ из заданной последовательности
[!foo] Совпадает любой символ не входящий в заданную последовательность

fnmatch.fnmatch(filename, pattern)

Проверяет соответствие строки filename строке шаблону pattern, возвращает True в случае соответствия. Если операционная система чуствительна к регистру, то оба параметра будут приведены к нижнему или верхнему регистру перед тем как произойдет сравнение. Если необходимо сравнение учитываающее регистр, то необходимо воспользоваться fnmatchcase().
Замечание: на самом деле преобразование регистра происходит всегда.

Следующий пример выведет на экран все имена файлов в текущей директории с расширением .txt:
import fnmatch, os

for file in os.listdir('.'):
    if fnmatch.fnmatch(file, '*.txt'):
        print file
fnmatch.fnmatchcase(filename, pattern)
Проверяет соответствие строки filename строке шаблону pattern учитывая регистр символов.
fnmatch.filter(names, pattern)
Возвращает список элементов в names которые соответствуют шаблону pattern. Это тоже самое что и [n for n in names if fnmatch(n, pattern)], но выполняется более эффективно.
Новое в версии 2.2

fnmatch.translate(pattern)
Возвращает преобразованный в регулярное выражение шаблон pattern

Не смотря на то что речь идет об использовании имен файлов, данный механизм можно использовать шире, например:

# -*- coding: utf-8 -*-
import fnmatch

# список строк, представляющих собой какой-либо набор данных
data=['report_may', 'Report_june', 'report December', 'article_12', 'Article_54']

print '\n*** 1 ***'
for i in data:
    print fnmatch.fnmatch(i, 'report*'),i

print '\n*** 2 ***'
for i in data:
    print fnmatch.fnmatchcase(i, 'report*'),i

print '\n*** 3 ***'
print fnmatch.filter(data, 'art*')

print '\n*** 4 ***'
print fnmatch.translate('[a-d]?pat*.txt')
Результат:
*** 1 ***
True report_may
True Report_june
True report December
False article_12
False Article_54

*** 2 ***
True report_may
False Report_june
True report December
False article_12
False Article_54

*** 3 ***
['article_12', 'Article_54']

*** 4 ***
[a-d].pat.*\.txt$
Модуль glob - Расширенный вариант fnmatch
Модуль находит все совпадения имен файлов и заданого шаблона в соответствии с правилами оболочки Unix. Для использования тильды ~ и переменных окружения необходимо использовать os.path.expanduser() и os.path.expandvars().

glob.glob(pathname)
Возвращает список путей или имен файлов которые соответствуют шаблону pathname, который может быть строкой содержащей спецификацию пути. Если Pathname является путем, то он может быть как абсолютным так и относительным. Битые символьные ссылки так же включаются в результат. Например:
# -*- coding: utf-8 -*-
import glob, os
# Меняем текущую директорию
print os.chdir('C:/')
print '\n*** 1 ***'
# Используем в шаблоне абсолютные пути
print glob.glob('C:/Windows/System32/*.exe')

print '\n*** 2 ***'
# Используем в шаблоне относительные пути
print glob.glob('.\\Windows\\*.exe')

print '\n*** 3 ***'
os.chdir('C:\Windows\system32')
# Если шаблон является объектом Unicode то и результат будет такого же типа
# Не используем в шаблоне пути, поэтому будет использоваться текущая директория
print glob.glob(u'*.com')

print '\n*** 4 ***'
# Использование символа ~
print glob.glob(os.path.expanduser('~')+os.sep+'*.log')
Вывод результатов сокращен:
*** 1 ***
[C:/windows/system32\\accwiz.exe', 'C:/windows/system32\\actmovie.exe', ... , 'C:/windows/system32\\wupdmgr.exe', 'C:/windows/system32\\xcopy.exe']

*** 2 ***
['C:\\Windows\\ALCMTR.EXE', 'C:\\Windows\\ALCWZRD.EXE', ... , 'C:\\Windows\\winhelp.exe', 'C:\\Windows\\winhlp32.exe']

*** 3 ***
[u'chcp.com', u'command.com', ... , u'tree.com', u'win.com']

*** 4 ***
['C:\\Documents and Settings\\mer\\ntuser.dat.LOG', 'C:\\Documents and Settings\\mer\\setup.log']
glob.iglob(pathname)
Возвращает итератор, который производит такие же значения как и glob() без одновременного их сохранения.
Т.о. использование iglob выгоднее в случае если нет необходимости держать в памяти весь результирующий список или когда список большого размера.
Пример:
>>> glob.glob('*.txt')
['1 (0).txt', '1 (1).txt', '1 (2).txt', '1 (3).txt', '1 (4).txt', '1 (5).txt']
>>> it=glob.iglob('*.txt')
>>> it
<generator object iglob at 0x00F498A0>
>>> it.next()
'1 (0).txt'
>>> it.next()
'1 (1).txt'
>>> it.next()
'1 (2).txt'
>>> it.next()
'1 (3).txt'
>>> it.next()
'1 (4).txt'
>>> it.next()
'1 (5).txt'
>>> it.next()
Traceback (most recent call last):
  File "", line 1, in
StopIteration
>>>for item in glob.iglob('*.txt'):
...print item
...
1 (0).txt
1 (1).txt
1 (2).txt
1 (3).txt
1 (4).txt
1 (5).txt

4 комментария:

  1. как сделать, чтобы glob.glob нашел одновременно несколько масок, например *.gif, *.png, *.jpg ???

    ОтветитьУдалить
  2. Нужно применять регулярные выражения. Даже если написать так: '*.[gpj][inp][fg]' , то под шаблон попадут нежелательные файлы. например *.gpg
    Если не хочется использовать регулярки, то можно и средствами Питона все сделать:

    [x for x in filenames if any([x.endswith(y) for y in ('jpg', 'gif', 'png')])]

    ОтветитьУдалить
    Ответы
    1. а если я нашла файлы, соответствующие шаблону, но они сохранились в список и представляют из себя строки, а мне нужно их прочесть и изменить их содержимое. как это сделать?

      Удалить
    2. Предположим что вы получили искомый список spisok. Далее в цикле проходите по этому списку, открываете файл с нужным именем на его изменение(!!) и проводите все необходимые операции.
      for filename in spisok:
      with open(filename, 'r+') as f:
      # здесь вы изменяете свой файл методом f.write()

      А вообще читайте книги по python для того что бы его изучить и понимать что вы делаете. Например автор Лутц

      Удалить