usando Python para eliminar una línea específica en un archivo

145

Digamos que tengo un archivo de texto lleno de apodos. ¿Cómo puedo eliminar un apodo específico de este archivo, usando Python?

Agria
fuente
1
Intente fileinputcomo lo describe @ jf-sebastian aquí . Parece que le permite trabajar línea por línea, a través de un archivo temporal, todo con una forsintaxis simple .
Kevin

Respuestas:

205

Primero, abra el archivo y obtenga todas sus líneas del archivo. Luego, vuelva a abrir el archivo en modo de escritura y escriba sus líneas, excepto la línea que desea eliminar:

with open("yourfile.txt", "r") as f:
    lines = f.readlines()
with open("yourfile.txt", "w") as f:
    for line in lines:
        if line.strip("\n") != "nickname_to_delete":
            f.write(line)

Necesita strip("\n")el carácter de nueva línea en la comparación porque si su archivo no termina con un carácter de nueva línea, el último linetampoco.

houbysoft
fuente
2
¿Por qué tenemos que abrirlo y cerrarlo dos veces?
Ooker
3
@Ooker: debe abrir el archivo dos veces (y cerrarlo en el medio) porque en el primer modo es "solo lectura" porque solo está leyendo en las líneas actuales del archivo. Luego lo cierra y lo vuelve a abrir en "modo de escritura", donde el archivo se puede escribir y reemplaza el contenido del archivo sin la línea que desea eliminar.
Devin
44
¿Por qué Python no nos permite hacer esto en una línea?
Ooker
55
@Ooker, cuando lea una línea, intente imaginar un cursor moviéndose a lo largo de la línea mientras se lee. Una vez que la línea ha sido leída, el cursor ya está pasado. Cuando intentas escribir en el archivo, escribes dónde está el cursor actualmente. Al volver a abrir el archivo, restablece el cursor.
Waddas
44
Utilice el con compuesto!
Sceluswe
101

Solución a este problema con solo una apertura:

with open("target.txt", "r+") as f:
    d = f.readlines()
    f.seek(0)
    for i in d:
        if i != "line you want to remove...":
            f.write(i)
    f.truncate()

Esta solución abre el archivo en modo r / w ("r +") y hace uso de buscar para restablecer el puntero f y luego truncar para eliminar todo después de la última escritura.

Lother
fuente
2
Esto funcionó muy bien para mí, ya que también tuve que usar lockfile (fcntl). No pude encontrar ninguna manera de usar fileinput junto con fcntl.
Easyrider
1
Sería bueno ver algunos efectos secundarios de esta solución.
user1767754
3
Yo no haría esto. Si obtiene un error en el forbucle, terminará con un archivo parcialmente sobrescrito, con líneas duplicadas o una mitad cortada. Es posible que desee f.truncate()justo después f.seek(0)en su lugar. De esa forma, si obtiene un error, terminará con un archivo incompleto. Pero la solución real (si tiene espacio en el disco) es generar un archivo temporal y luego usarlo os.replace()o pathlib.Path(temp_filename).replace(original_filename)intercambiarlo con el original después de que todo haya tenido éxito.
Boris
Podría agregar i.strip('\n') != "line you want to remove..."como se menciona en la respuesta aceptada, eso resolvería perfectamente mi problema. Porque simplemente ino hizo nada por mí
Mangohero1
31

En mi opinión, la mejor y más rápida opción, en lugar de almacenar todo en una lista y volver a abrir el archivo para escribirlo, es volver a escribir el archivo en otro lugar.

with open("yourfile.txt", "r") as input:
    with open("newfile.txt", "w") as output: 
        for line in input:
            if line.strip("\n") != "nickname_to_delete":
                output.write(line)

¡Eso es! En un bucle y solo uno puede hacer lo mismo. Será mucho más rápido

Bernabé
fuente
En lugar de usar el bucle normal for, podemos hacer uso de Generator Expression. De esta manera, el programa no cargará todas las líneas del archivo a la memoria, lo que no es una buena idea en el caso de archivos grandes. Solo tendrá una sola línea en la memoria a la vez. Con la expresión del generador para el bucle se verá así,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde
44
@ShriShinde Tampoco está leyendo el archivo en la memoria al recorrer el objeto del archivo, por lo que esta solución funciona de manera idéntica a su sugerencia.
Steinar Lima
Es posible que desee eliminar el archivo original y cambiar el nombre del segundo archivo al nombre del archivo original, que con Python en un sistema operativo Linux se vería así,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Max
66
os.replace(nuevo en python v 3.3) es más multiplataforma que una llamada al sistema mv.
7yl4r
Simple y genial
JuBaer AD
27

Esta es una "bifurcación" de la respuesta de @Lother (que creo que debería considerarse la respuesta correcta).


Para un archivo como este:

$ cat file.txt 
1: october rust
2: november rain
3: december snow

Este tenedor de la solución de Lother funciona bien:

#!/usr/bin/python3.4

with open("file.txt","r+") as f:
    new_f = f.readlines()
    f.seek(0)
    for line in new_f:
        if "snow" not in line:
            f.write(line)
    f.truncate()

Mejoras:

  • with open, que descartan el uso de f.close()
  • más claro if/elsepara evaluar si la cadena no está presente en la línea actual
ivanleoncz
fuente
Si se requiere f.seek (0)?
yifan
@yifan sí. De lo contrario, en lugar de sobrescribir el archivo, lo agregará a sí mismo (sin las líneas que está excluyendo).
Boris
5

El problema con la lectura de líneas en el primer paso y la realización de cambios (eliminación de líneas específicas) en el segundo paso es que si el tamaño de los archivos es enorme, se quedará sin RAM. En cambio, un mejor enfoque es leer líneas, una por una, y escribirlas en un archivo separado, eliminando las que no necesita. He ejecutado este enfoque con archivos tan grandes como 12-50 GB, y el uso de RAM se mantiene casi constante. Solo los ciclos de CPU muestran el procesamiento en progreso.

Kingz
fuente
2

Me gustó el enfoque de entrada de archivos como se explica en esta respuesta: Eliminar una línea de un archivo de texto (python)

Digamos, por ejemplo, que tengo un archivo que tiene líneas vacías y quiero eliminar las líneas vacías, así es como lo resolví:

import fileinput
import sys
for line_number, line in enumerate(fileinput.input('file1.txt', inplace=1)):
    if len(line) > 1:
            sys.stdout.write(line)

Nota: Las líneas vacías en mi caso tenían longitud 1

Profundo
fuente
2

Si usa Linux, puede probar el siguiente enfoque.
Supongamos que tiene un archivo de texto llamado animal.txt:

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

Eliminar la primera línea:

>>> import subprocess
>>> subprocess.call(['sed','-i','/.*dog.*/d','animal.txt']) 

luego

$ cat animal.txt
pig
cat
monkey
elephant
Ren
fuente
77
Esta solución no es independiente del sistema operativo, y dado que OP no especificó un sistema operativo, no hay razón para publicar una respuesta específica de Linux.
Steinar Lima
2
¡Cualquiera que sugiera el uso de subprocesos para cualquier cosa que pueda hacerse con solo Python recibe un voto negativo! Y +1 a @SteinarLima ... Estoy de acuerdo
Jamie Lindsey
2

Creo que si lees el archivo en una lista, entonces puedes iterar sobre la lista para buscar el apodo del que deseas deshacerte. Puede hacerlo de manera muy eficiente sin crear archivos adicionales, pero tendrá que volver a escribir el resultado en el archivo fuente.

Así es como podría hacer esto:

import, os, csv # and other imports you need
nicknames_to_delete = ['Nick', 'Stephen', 'Mark']

Supongo que nicknames.csvcontiene datos como:

Nick
Maria
James
Chris
Mario
Stephen
Isabella
Ahmed
Julia
Mark
...

Luego cargue el archivo en la lista:

 nicknames = None
 with open("nicknames.csv") as sourceFile:
     nicknames = sourceFile.read().splitlines()

A continuación, repita la lista para que coincida con las entradas que desea eliminar:

for nick in nicknames_to_delete:
     try:
         if nick in nicknames:
             nicknames.pop(nicknames.index(nick))
         else:
             print(nick + " is not found in the file")
     except ValueError:
         pass

Por último, escriba el resultado en el archivo:

with open("nicknames.csv", "a") as nicknamesFile:
    nicknamesFile.seek(0)
    nicknamesFile.truncate()
    nicknamesWriter = csv.writer(nicknamesFile)
    for name in nicknames:
        nicknamesWriter.writeRow([str(name)])
nicknamesFile.close()
Un malik
fuente
1

En general, no puedes; tienes que volver a escribir todo el archivo (al menos desde el punto de cambio hasta el final).

En algunos casos específicos, puede hacerlo mejor que esto:

si todos sus elementos de datos tienen la misma longitud y no están en un orden específico, y conoce el desplazamiento del que desea eliminar, puede copiar el último elemento sobre el que se va a eliminar y truncar el archivo antes del último elemento ;

o simplemente podría sobrescribir el fragmento de datos con un valor de "estos son datos incorrectos, omitirlo" o mantener una marca de "este elemento ha sido eliminado" en los elementos de datos guardados de modo que pueda marcarlo como eliminado sin modificar el archivo.

Probablemente esto sea excesivo para documentos cortos (¿algo menor a 100 KB?).

Hugh Bothwell
fuente
1

Probablemente, ya obtuviste una respuesta correcta, pero aquí está la mía. En lugar de usar una lista para recopilar datos sin filtrar (qué readlines()método hace), utilizo dos archivos. Uno es para mantener los datos principales, y el segundo es para filtrar los datos cuando elimina una cadena específica. Aquí hay un código:

main_file = open('data_base.txt').read()    # your main dataBase file
filter_file = open('filter_base.txt', 'w')
filter_file.write(main_file)
filter_file.close()
main_file = open('data_base.txt', 'w')
for line in open('filter_base'):
    if 'your data to delete' not in line:    # remove a specific string
        main_file.write(line)                # put all strings back to your db except deleted
    else: pass
main_file.close()

¡Espero que encuentres esto útil! :)

andrii1986
fuente
0

Guarde las líneas del archivo en una lista, luego elimine de la lista la línea que desea eliminar y escriba las líneas restantes en un nuevo archivo

with open("file_name.txt", "r") as f:
    lines = f.readlines() 
    lines.remove("Line you want to delete\n")
    with open("new_file.txt", "w") as new_f:
        for line in lines:        
            new_f.write(line)
Henrique Andrade
fuente
Al dar una respuesta, es preferible dar una explicación de POR QUÉ su respuesta es la correcta.
Stephen Rauch
Si su archivo no termina con una nueva línea, este código no eliminará la última línea, incluso si contiene una palabra que desea eliminar.
Boris
0

Aquí hay otro método para eliminar una / alguna línea (s) de un archivo:

src_file = zzzz.txt
f = open(src_file, "r")
contents = f.readlines()
f.close()

contents.pop(idx) # remove the line item from list, by line number, starts from 0

f = open(src_file, "w")
contents = "".join(contents)
f.write(contents)
f.close()
ungalcrys
fuente
0

Me gusta este método usando fileinput y el método 'in situ':

import fileinput
for line in fileinput.input(fname, inplace =1):
    line = line.strip()
    if not 'UnwantedWord' in line:
        print(line)

Es un poco menos prolijo que las otras respuestas y es lo suficientemente rápido para

Ru887321
fuente
0

Puedes usar la rebiblioteca

Suponiendo que pueda cargar su archivo txt completo. Luego define una lista de apodos no deseados y luego los sustituye con una cadena vacía "".

# Delete unwanted characters
import re

# Read, then decode for py2 compat.
path_to_file = 'data/nicknames.txt'
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')

# Define unwanted nicknames and substitute them
unwanted_nickname_list = ['SourDough']
text = re.sub("|".join(unwanted_nickname_list), "", text)
mrk
fuente
-1

Para eliminar una línea específica de un archivo por su número de línea :

Reemplace las variables filename y line_to_delete con el nombre de su archivo y el número de línea que desea eliminar.

filename = 'foo.txt'
line_to_delete = 3
initial_line = 1
file_lines = {}

with open(filename) as f:
    content = f.readlines() 

for line in content:
    file_lines[initial_line] = line.strip()
    initial_line += 1

f = open(filename, "w")
for line_number, line_content in file_lines.items():
    if line_number != line_to_delete:
        f.write('{}\n'.format(line_content))

f.close()
print('Deleted line: {}'.format(line_to_delete))

Salida de ejemplo :

Deleted line: 3
Aram Maliachi
fuente
no hay necesidad de construir un dict, solo usefor nb, line in enumerate(f.readlines())
Dionys
-3

Tome el contenido del archivo, divídalo por una nueva línea en una tupla. Luego, acceda al número de línea de su tupla, únase a su tupla de resultado y sobrescriba el archivo.

Nikhil
fuente
66
(1) ¿quieres decir tuple(f.read().split('\n'))? (2) "acceder al número de línea de su tupla" y "unirse a su tupla resultante" suena bastante misterioso; El código real de Python podría ser más comprensible.
John Machin