2010-10-30 16 views
90

j'ouvrir les fichiers qui étaient dans le même répertoire que le script Python en cours d'exécution en utilisant simplement une commande commeComment ouvrir de manière fiable un fichier dans le même répertoire comme un script Python

open("Some file.txt", "r") 

Cependant, je a découvert que lorsque le script était exécuté dans Windows en double-cliquant dessus, il essayait d'ouvrir le fichier à partir du mauvais répertoire.

Depuis que j'ai utilisé une commande de la forme

open(os.path.join(sys.path[0], "Some file.txt"), "r") 

chaque fois que je voulais ouvrir un fichier. Cela fonctionne pour mon usage particulier, mais je ne suis pas sûr si sys.path[0] pourrait échouer dans un autre cas d'utilisation. Donc, ma question est: Quelle est la meilleure et la plus fiable façon d'ouvrir un fichier qui se trouve dans le même répertoire que le script Python en cours d'exécution?

Voici ce que je suis en mesure de comprendre à ce jour:

  • os.getcwd() et os.path.abspath('') retourner le « répertoire de travail », pas le répertoire de script.

  • os.path.dirname(sys.argv[0]) et os.path.dirname(__file__) renvoient le chemin utilisé pour appeler le script, qui peut être relatif ou même vide (si le script est dans le fichier cwd). En outre, __file__ n'existe pas lorsque le script est exécuté dans IDLE ou PythonWin.

  • sys.path[0] et os.path.abspath(os.path.dirname(sys.argv[0])) semblent retourner le répertoire du script. Je ne suis pas sûr s'il y a une différence entre ces deux-là.

Edit:

Je viens de réaliser que ce que je veux faire serait mieux décrit comme « ouvrir un fichier dans le même répertoire que le module contenant ». En d'autres termes, si j'importe un module que j'ai écrit dans un autre répertoire, et que ce module ouvre un fichier, je veux qu'il recherche le fichier dans le répertoire du module. Je ne pense pas que tout ce que j'ai trouvé est capable de le faire ...

Répondre

108

J'utilise toujours:

__location__ = os.path.realpath(
    os.path.join(os.getcwd(), os.path.dirname(__file__))) 

L'appel join() Ajoute le répertoire de travail en cours, mais la documentation dit que si un chemin est absolu, tous les autres chemins qui restent sont supprimés. Par conséquent, getcwd() est supprimé lorsque dirname(__file__) renvoie un chemin absolu.

De plus, l'appel realpath résout les liens symboliques, le cas échéant. Cela évite les problèmes lors du déploiement avec setuptools sur les systèmes Linux (les scripts sont symétriquement /usr/bin/ - au moins sur Debian).

Vous pouvez l'utiliser ce qui suit pour ouvrir des fichiers dans le même dossier:

f = open(os.path.join(__location__, 'bundled-resource.jpg')); 
# ... 

Je l'utiliser pour regrouper les ressources avec plusieurs applications Django sur Windows et Linux et il fonctionne comme un charme!

+2

Si '__file__' ne peut pas être utilisé, alors utilisez' sys.argv [0] 'au lieu de' dirname (__ file __) '. Le reste devrait fonctionner comme prévu. J'aime utiliser '__file__' parce que dans le code de la bibliothèque,' sys.argv [0] 'pourrait ne pas pointer du tout sur votre code, surtout s'il est importé via un script tiers. –

+1

Le problème avec ceci est que cela variera si le fichier en cours d'exécution provient directement de l'interrupteur ou s'il est importé. Voir ma réponse pour les différences entre __file__ et sys.argv [0] – Zimm3r

+0

Alors est-il correct de dire que la variation décrite dans la réponse de Zimm3r est adressée en utilisant 'realpath (join (getcwd(), dirname (__ fichier__)))) décrit ici? – pianoJames

4

que je le ferais de cette façon:

from os.path import abspath, exists 

f_path = abspath("fooabar.txt") 

if exists(f_path): 
    with open(f_path) as f: 
     print f.read() 

Le code ci-dessus construit un chemin absolu du fichier en utilisant abspath et équivaut à utiliser normpath(join(os.getcwd(), path)) [qui est des pydocs]. Il vérifie ensuite si ce fichier contient effectivement exists, puis utilise un gestionnaire de contexte pour l'ouvrir afin que vous n'ayez pas à vous rappeler d'appeler de près le gestionnaire de fichiers. À mon humble avis, le faire de cette façon vous fera économiser beaucoup de douleur à long terme.

+0

Ceci ne répond pas à la question de l'annonceur. dln385 a spécifiquement dit que 'os.path.abspath' ne résout pas les chemins vers les fichiers dans le même dossier que le script si le script n'est pas dans le répertoire courant. –

+0

AH! J'ai supposé que l'utilisateur exécutait ce script dans le même répertoire que le fichier qu'ils voulaient lire, * NOT * dans le module dir de quelque chose dans leur PYTHONPATH. Cela m'apprendra à faire des suppositions ... – dcolish

12

Ok voici ce que je fais

sys.argv est toujours ce que vous tapez dans le terminal ou l'utiliser comme le chemin du fichier lors de l'exécution avec python.exe ou pythonw.exe

Par exemple, vous pouvez exécutez le fichier text.py de plusieurs façons, ils vous donnent chacun une réponse différente, ils vous donnent toujours le chemin que python a été tapé.

C:\Documents and Settings\Admin>python test.py 
    sys.argv[0]: test.py 
    C:\Documents and Settings\Admin>python "C:\Documents and Settings\Admin\test.py" 
    sys.argv[0]: C:\Documents and Settings\Admin\test.py 

Ok alors que vous pouvez obtenir le nom du fichier, une grande grosse affaire, maintenant pour obtenir le répertoire de l'application, vous pouvez savoir utiliser os.path, en particulier abspath et dirname

import sys, os 
    print os.path.dirname(os.path.abspath(sys.argv[0])) 

Cela affichera cette :

C:\Documents and Settings\Admin\ 

il sera toujours sortie ce peu importe si vous tapez test.py python ou python "C: \ Documents and Settings \ admin \ test.py"

Le problème avec l'utilisation __file__ Tenez compte de ces deux fichiers test.py

import sys 
import os 

def paths(): 
     print "__file__: %s" % __file__ 
     print "sys.argv: %s" % sys.argv[0] 

     a_f = os.path.abspath(__file__) 
     a_s = os.path.abspath(sys.argv[0]) 

     print "abs __file__: %s" % a_f 
     print "abs sys.argv: %s" % a_s 

if __name__ == "__main__": 
    paths() 

import_test.py

import test 
import sys 

test.paths() 

print "--------" 
print __file__ 
print sys.argv[0] 

Sortie de "test.py python"

C:\Documents and Settings\Admin>python test.py 
__file__: test.py 
sys.argv: test.py 
abs __file__: C:\Documents and Settings\Admin\test.py 
abs sys.argv: C:\Documents and Settings\Admin\test.py 

Sortie de "python test_import.py"

C:\Documents and Settings\Admin>python test_import.py 
__file__: C:\Documents and Settings\Admin\test.pyc 
sys.argv: test_import.py 
abs __file__: C:\Documents and Settings\Admin\test.pyc 
abs sys.argv: C:\Documents and Settings\Admin\test_import.py 
-------- 
test_import.py 
test_import.py 

Comme vous pouvez le voir fichier vous donne toujours le fichier python, il est exécuté à partir, alors que sys.argv [0] vous donne le fichier que vous avez exécuté l'interprète de toujours. Selon vos besoins, vous devrez choisir celui qui correspond le mieux à vos besoins.

+1

Ceci est une preuve élaborée que l'implémentation reflète la documentation. '__file__' est supposé" toujours vous donner le chemin vers le fichier en cours ", et' sys.argv [0] 'est supposé * donner" toujours le chemin du script qui a initié le processus ". Dans tous les cas, l'utilisation de '__file__' dans le script invoqué vous donne toujours des résultats précis. –

+0

Je le faisais juste remarquer et donner quelque chose pour le montrer. – Zimm3r

+0

Si vous avez la référence à '__file__' au plus haut niveau du script, cela fonctionnera comme prévu. –

18

Voici un extrait de la documentation Python:

Comme initialisé au démarrage du programme, le premier élément de cette liste, chemin [0], est le répertoire contenant le script qui a été utilisé pour invoquer l'interpréteur Python. Si le répertoire de script n'est pas disponible (par exemple si l'interpréteur est invoqué de manière interactive ou si le script est lu depuis l'entrée standard), le chemin [0] est la chaîne vide, qui indique à Python de rechercher les modules dans le répertoire courant. Notez que le répertoire de script est inséré avant les entrées insérées à la suite de PYTHONPATH.

sys.path [0] est ce que vous recherchez.

+1

Et pour le chemin complet du fichier: 'os.path.join (sys.path [0], 'file.txt')'. Cela devrait gérer les espaces et les barres obliques correctement sur tous les systèmes. – P1h3r1e3d13

+0

Cette réponse à la première question, pas celle après l'EDIT. – mcoolive