Comment ouvrir un fichier à l'aide de l'instruction open with

200

Je regarde comment faire l'entrée et la sortie d'un fichier en Python. J'ai écrit le code suivant pour lire une liste de noms (un par ligne) d'un fichier dans un autre fichier tout en vérifiant un nom par rapport aux noms du fichier et en ajoutant du texte aux occurrences du fichier. Le code fonctionne. Pourrait-on faire mieux?

Je voulais utiliser l' with open(...instruction pour les fichiers d'entrée et de sortie, mais je ne vois pas comment ils pourraient être dans le même bloc, ce qui signifie que je devrais stocker les noms dans un emplacement temporaire.

def filter(txt, oldfile, newfile):
    '''\
    Read a list of names from a file line by line into an output file.
    If a line begins with a particular name, insert a string of text
    after the name before appending the line to the output file.
    '''

    outfile = open(newfile, 'w')
    with open(oldfile, 'r', encoding='utf-8') as infile:
        for line in infile:
            if line.startswith(txt):
                line = line[0:len(txt)] + ' - Truly a great person!\n'
            outfile.write(line)

    outfile.close()
    return # Do I gain anything by including this?

# input the name you want to check against
text = input('Please enter the name of a great person: ')    
letsgo = filter(text,'Spanish', 'Spanish2')
Disnami
la source
"ce qui signifie que je devrais stocker les noms dans un emplacement temporaire"? Pouvez-vous expliquer ce que vous entendez par là?
S.Lott
4
Notez qu'il filter()s'agit d' une fonction intégrée et que vous devriez donc probablement choisir un nom différent pour votre fonction.
Tom
2
@Tom une fonction dans l'espace de noms remplace-t-elle la fonction intégrée?
UpTide
2
@UpTide: Oui, Python fonctionne dans l'ordre LEGB - Local, Enclosing, Global, Built-in (voir stackoverflow.com/questions/291978/… ). Donc, si vous créez une fonction globale ( filter()), elle sera trouvée avant la fonction intégréefilter()
Tom

Réponses:

308

Python permet de mettre plusieurs open()instructions en une seule with. Vous les séparez par des virgules. Votre code serait alors:

def filter(txt, oldfile, newfile):
    '''\
    Read a list of names from a file line by line into an output file.
    If a line begins with a particular name, insert a string of text
    after the name before appending the line to the output file.
    '''

    with open(newfile, 'w') as outfile, open(oldfile, 'r', encoding='utf-8') as infile:
        for line in infile:
            if line.startswith(txt):
                line = line[0:len(txt)] + ' - Truly a great person!\n'
            outfile.write(line)

# input the name you want to check against
text = input('Please enter the name of a great person: ')    
letsgo = filter(text,'Spanish', 'Spanish2')

Et non, vous ne gagnez rien en mettant un explicite returnà la fin de votre fonction. Vous pouvez utiliser returnpour quitter tôt, mais vous l'aviez à la fin, et la fonction se terminera sans elle. (Bien sûr, avec les fonctions qui renvoient une valeur, vous utilisez la returnpour spécifier la valeur à renvoyer.)

L'utilisation de plusieurs open()éléments avec withn'était pas prise en charge dans Python 2.5 lors de l' withintroduction de l' instruction, ou dans Python 2.6, mais elle est prise en charge dans Python 2.7 et Python 3.1 ou plus récent.

http://docs.python.org/reference/compound_stmts.html#the-with-statement http://docs.python.org/release/3.1/reference/compound_stmts.html#the-with-statement

Si vous écrivez du code qui doit s'exécuter en Python 2.5, 2.6 ou 3.0, imbriquez les withinstructions comme les autres réponses suggérées ou utilisez contextlib.nested.

steveha
la source
28

Utilisez des blocs imbriqués comme celui-ci,

with open(newfile, 'w') as outfile:
    with open(oldfile, 'r', encoding='utf-8') as infile:
        # your logic goes right here
RanRag
la source
12

Vous pouvez imbriquer vos blocs. Comme ça:

with open(newfile, 'w') as outfile:
    with open(oldfile, 'r', encoding='utf-8') as infile:
        for line in infile:
            if line.startswith(txt):
                line = line[0:len(txt)] + ' - Truly a great person!\n'
            outfile.write(line)

C'est mieux que votre version car vous garantissez qu'elle outfilesera fermée même si votre code rencontre des exceptions. Évidemment, vous pouvez le faire avec try / finalement, mais withc'est la bonne façon de le faire.

Ou, comme je viens de l'apprendre, vous pouvez avoir plusieurs gestionnaires de contexte dans une instruction with comme décrit par @steveha . Cela me semble être une meilleure option que l'imbrication.

Et pour votre dernière question mineure, le retour ne sert à rien. Je l'enlèverais.

David Heffernan
la source
Merci beaucoup. Je vais l'essayer et accepter votre réponse si / quand je le ferai fonctionner.
Disnami
Merci encore. Je dois attendre sept minutes avant de pouvoir accepter.
Disnami
7
@Disnami assurez-vous d'accepter la bonne réponse (et ce n'est pas celle-ci!) ;-)
David Heffernan
1

Parfois, vous voudrez peut-être ouvrir une quantité variable de fichiers et les traiter chacun de la même manière, vous pouvez le faire avec contextlib

from contextlib import ExitStack
filenames = [file1.txt, file2.txt, file3.txt]

with open('outfile.txt', 'a') as outfile:
    with ExitStack() as stack:
        file_pointers = [stack.enter_context(open(file, 'r')) for file in filenames]                
            for fp in file_pointers:
                outfile.write(fp.read())                   
frère-bilo
la source