Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Comment peut-on vérifier qu'un fichier est un fichier texte ?

+10 votes

Comment peut-on vérifier qu'un fichier est effectivement un fichier texte ?

demandé 19-Mai-2015 par DoubleNain (1,656 points)

Peut-être des pistes de réflexion :

En particulier avec Python 3

try:
    with open(filename, "r") as f:
        for l in f:
             process_line(l)
except UnicodDecodeError:
    pass # Fond non-text data

+1 pour le module binaryornot, çà a le gros avantage de rester en pur python contrairement a libmagic (tout comme la réponse de la réponse de @Tiger-222).

(En plus, ça a été écrit par une rockstar python)

6 Réponses

+5 votes
 
Meilleure réponse

Après plusieurs pistes de reflexion, je pense que la solution la plus optimale sans être parfaite pour répondre à la question initiale est celle utilisant le module Binaryornot même si la solution proposée par Tiger-222 est proche de celle proposée par le module dédié.

https://pypi.python.org/pypi/binaryornot/0.2.0

répondu 21-Mai-2015 par DoubleNain (1,656 points)

Le code que je propose est utilisé dans ce module aussi, du coup oui, c'est la meilleure solution.

+4 votes

Via le module mimetypes?
https://docs.python.org/2/library/mimetypes.html
Je ne vois pas franchement d'autres choix que de comparer mimetypes.guess_type() a ta liste de mimetypes pre-etablie

répondu 19-Mai-2015 par ashgan (698 points)

Et si l'extension ne correspond pas ou qu'elle est absente ? mimetype semble se baser beaucoup là dessus (sur Windows du moins).

Je suis intéressé par une réponse à cette question car j'ai également la problématique.

Si y'a un manque, mimetypes te permet d'y remedier (cf init et knownfiles), mais comme dit plus bas, ca fait pas le taff dans le cadre de la question

+6 votes

Le module mimetype ne vérifie que l'extension, pas le contenu.

La lib libmagic se base sur le contenu, il y a un binding python (que je n'ai amais utilisé)

répondu 19-Mai-2015 par jc (2,674 points)

Bien vu pour le module mimetypes, j'ai mal lu la doc.

Bon par contre si tu bosses sous Windows, l'utilisation de libmagic peut être compliquée.

La, sur le github, les mecs conseillent d'utiliser cygwin :/

+3 votes

Perso je dirais :

You can't detect the codepage, you need to be told it. You can analyse
the bytes and guess it, but that can give some bizarre (sometimes
amusing) results.

Donc si on te renseigne pas d'avantage sur les fichiers, et que tu ne peux pas te baser sur les extensions, ça va être très light de trouver une solution...
Car en réalité, tout fichier se décline en fichier binaire, qu'il soit un fichier texte ou non, donc faut faire de l'IA pour résoudre le problème mais ça peut être assez hasardeux parfois ^^

répondu 19-Mai-2015 par boblinux (3,092 points)

Je plussoie ceci.

La problématique soulevée ici est similaire à vouloir détecter l'encoding d'un fichier, ça sera toujours empirique.

+3 votes

À part loader le fichier et vérifier le type des lignes avec isinstance(<ma_ligne>,str) j'ai un peu de mal à voir. Mais de toute façon j'imagine que le load force le type à str, donc...

répondu 19-Mai-2015 par furankun (1,418 points)
+7 votes

Il y a une autre solution possible :

textchars = bytearray([7,8,9,10,12,13,27]) + bytearray(range(0x20, 0x100))
is_plaintext = lambda bytes: not bool(bytes.translate(None, textchars))

Tu l'utilises tel que :

>>> is_plaintext(open('/usr/bin/python', 'rb').read(1024))
False
>>> is_plaintext(open('/etc/apache2/httpd.conf', 'rb').read(1024))
True

Voilà un exemple complet :

def is_text(fname):
    ''' Returns:
            True if it seems to be a plain text file.
            False if it is not a plain text file.
            None if the file could not be opened (inexistant or wrong rights).
    '''
    textchars = bytearray([7,8,9,10,12,13,27]) + bytearray(range(0x20, 0x100))
    is_plaintext = lambda bytes: not bool(bytes.translate(None, textchars))
    with open(fname, 'rb') as fileh:
        return is_plaintext(fileh.read(1024))
    return None


if __name__ == '__main__':
    print(is_text('/usr/bin/python'))
    print(is_text('/usr/share/dh-python/dh_pypy'))

Source : How can I detect if a file is binary (non-text) in python?

répondu 20-Mai-2015 par Tiger-222 (498 points)
edité 21-Mai-2015 par Tiger-222

Cette solution me paraît pas mal du tout pour l'instant.

Ca a un petit côté magique, je trouve.

Je veux bien une explication sur celle là ! :o

De ce que j'ai capté
- read(1024) lit les 1024 premiers éléments du fichiers
- textchars contient tous les caractères qui peuvent constituer un texte
- bytes.translate(None, textchars) retourne une string où tous les caractères contenus dans textchars ont été supprimés -> si c'est un texte, la string est vide. En passant, je trouve le nom de cette fonction complètement contre-intuitif.
- not bool teste la string et inverse le résultat -> si la string est vide, on obtient True

not bool teste la string et inverse le résultat -> si la string est vide, on obtient True

D'un autre côté, si c'est vide, ça peut être True ou False, impossible de dire quel type de fichier il s'agit. On sait juste que c'est un fichier.
C'est comme demander si zéro est positif ou négatif, tu peux dire ce que tu veux, voire les deux ;)

C'est pas moi qui le dit hein:

>>> type(bytes('text').translate(None, bytes('tex')))
<type 'str'>

De mon côté ça marche:

>>> not bool(bytes('text').translate(None, bytes('te')))
False
>>> not bool(bytes('text').translate(None, bytes('tex')))
True

Suivant la logique du truc, si la string est vide c'est que c'est un texte, pas autre chose. Si le read(1024) ne t'envoie rien par contre c'est un autre problème.

À la vache, j'ai rien pigé à ton commentaire en fait. J'ai lu trop vite et pas concentré. Tu as tout à fait raison, pardonne-moi :)

...