Определяем тип файла в Python

Работаем с помощью magic-последовательностей. Определение происходит с помощью libmagic.so (библиотека для C, заголовочный файл - magic.h, почитайте также "man libmagic"). Используем библиотеку с помощью ctypes.cdll.

Вот такой вот класс:


import ctypes

class CMagic:
   __mgc = None
   __cookie = None

   def __init__(self):
     #Загружаем libmagic.so
     self.__mgc = ctypes.cdll.LoadLibrary("libmagic.so")
     #Создаем новый cookie (требуется для
     #работы с magic-последовательностями)
     #0x10 | 0x400 = MAGIC_MIME (константа
     #декларируется в magic.h)
     self.__cookie = self.__mgc.magic_open(0x10 | 0x400)
     #Загружаем в __cookie
     #/etc/file/magic.mime (т.к. указано None)
     self.__mgc.magic_load(self.__cookie, None)

   def __del__(self):
     #Закрываем __cookie
     self.__mgc.magic_close(self.__cookie)

   def getMType(self,filename):
     #Получаем информацию о файле
     result = self.__mgc.magic_file(self.__cookie,filename)
     #magic_file возвращает const char*,
     #mimetype.value - это строка по указателю
     mimetype = ctypes.c_char_p(result)
     rez = mimetype.value
     return rez

   def is_video(self,filename):
     #Проверяем, является ли файл
     #- видео файлом
     mime = self.getMType(filename)
     if mime.count("video"):
       return 1
     return 0

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

jetxee комментирует...

ИМХО, если уж завязываться на платформозависимые вещи, то можно было сделать проще используя file и popen2:
import popen2, string
cout,cin=popen2.popen2('file file1.txt file2.txt')
lines=cout.readlines()
d=dict([ [ i.strip() for i in l.split(':',1) ] for l in lines ])

получив словарик
{'file1.txt': 'ASCII text', 'file2.txt': 'ASCII text'}

verzhak комментирует...

В корне своем решения у нас одинаковые - и там, и там libmagic.so пользуется (у вас - в утилите file).

С точки зрения простоты кода - вы выиграли :-)

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

jetxee комментирует...

Мой вариант использования libmagic через вызов file не только компактнее, но менее хрупок и легче модифицируем.

Например, код в вашем примере у меня не заработает, а бросит исключение OSError, потому что в _моей_ системе, библиотека называется libmagic.so.1, а не libmagic.so… Обращение к file эти тонкости маскирует. Всё же python — это не си и не си++. Опять же, если нужно получать типы в формате mime-type, то с file достаточно добавить "-i". Нужен специальный magic-file — тоже просто. Минус моего варианта — надо квотировать пробелы и спецсимволы в именах файлов.

По поводу запуска новых процессов, то в unix они достаточно лёгкие, и это традиционный способ программирования. Кроме того, можно сразу просить определить типы нескольких файлов — так что запусков нового процесса нужно меньше, чем количество файлов.

Кстати, вот маленький тест:
Music$ time find . -iname '*.mp3' -print0 | xargs -0 -n 1 file -i >/dev/null

real 0m24.198s
user 0m7.356s
sys 0m1.304s
Music$ time find . -iname '*.mp3' -print0 | xargs -0 -n 500 file -i >/dev/null

real 0m22.774s
user 0m6.888s
sys 0m0.380s

— определяется тип для 1050 файлов, разница между 1050 запусками file и 3 запусками пренебрежимо мала по сравнению с временем, которое ему нужно, чтобы заглянуть в каждый из 1050 файлов, и может быть объяснена кэшированием файловой системы. Время работы find в одиночку 0.978s. Кстати, любопытно, сколько времени на это уйдёт, если использовать libmagic напрямую. Я предполагаю, что на таком наборе файлов разница не превысит 15%.

TI_Eugene комментирует...

pyxdg вам поможет