Cómo abrir un archivo usando la instrucción abrir con

200

Estoy viendo cómo hacer la entrada y salida de archivos en Python. Escribí el siguiente código para leer una lista de nombres (uno por línea) de un archivo a otro archivo mientras verifico un nombre con los nombres en el archivo y anexo texto a las ocurrencias en el archivo. El código funciona ¿Se podría hacer mejor?

Quería usar la with open(...declaración para los archivos de entrada y salida, pero no puedo ver cómo podrían estar en el mismo bloque, lo que significa que necesitaría almacenar los nombres en una ubicación temporal.

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
fuente
"lo que significa que necesitaría almacenar los nombres en una ubicación temporal"? ¿Puedes explicar a qué te refieres con esto?
S.Lott
44
Tenga en cuenta que filter()es una función incorporada, por lo que probablemente debería elegir un nombre diferente para su función.
Tom
2
@Tom ¿una función en el espacio de nombres anula la función incorporada?
UpTide
2
@UpTide: Sí, Python opera en orden LEGB: local, adjunto, global, integrado (consulte stackoverflow.com/questions/291978/… ). Por lo tanto, si realiza una función global ( filter()), se encontrará antes del incorporadofilter()
Tom

Respuestas:

308

Python permite poner múltiples open()declaraciones en una sola with. Los separas por coma. Su código sería entonces:

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')

Y no, no gana nada poniendo un explícito returnal final de su función. Puede usar returnpara salir temprano, pero lo tenía al final, y la función saldrá sin él. (Por supuesto, con funciones que devuelven un valor, se usa returnpara especificar el valor a devolver).

El uso de varios open()elementos con withno era compatible con Python 2.5 cuando withse introdujo la declaración, o con Python 2.6, pero sí con Python 2.7 y Python 3.1 o posterior.

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 está escribiendo código que debe ejecutarse en Python 2.5, 2.6 o 3.0, anide las withdeclaraciones como las otras respuestas sugieren o use contextlib.nested.

steveha
fuente
28

Use bloques anidados como este,

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

Puedes anidar tu con bloques. Me gusta esto:

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)

Esto es mejor que su versión porque garantiza que outfilese cerrará incluso si su código encuentra excepciones. Obviamente, podría hacerlo con try / finally, pero withes la forma correcta de hacerlo.

O, como acabo de aprender, puede tener múltiples gestores de contexto en una declaración with como lo describe @steveha . Eso me parece una mejor opción que anidar.

Y para su última pregunta menor, la devolución no tiene ningún propósito real. Lo quitaría

David Heffernan
fuente
Muchas gracias. Lo intentaré y aceptaré tu respuesta si / cuando lo haga funcionar.
Disnami
Gracias de nuevo. Tengo que esperar siete minutos antes de que pueda aceptar.
Disnami
77
@Disnami asegúrate de aceptar la respuesta correcta (¡y no es esta!) ;-)
David Heffernan
1

A veces, es posible que desee abrir una cantidad variable de archivos y tratar a cada uno de la misma manera, puede hacerlo con 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())                   
hermano-bilo
fuente