usando Python para excluir uma linha específica em um arquivo

145

Digamos que eu tenho um arquivo de texto cheio de apelidos. Como posso excluir um apelido específico desse arquivo usando o Python?

SourD
fonte
1
Tente fileinputcomo descrito por @ jf-sebastian aqui . Parece permitir que você trabalhe linha por linha, através de um arquivo temporário, tudo com uma forsintaxe simples .
Kevin

Respostas:

205

Primeiro, abra o arquivo e obtenha todas as suas linhas. Em seguida, reabra o arquivo no modo de gravação e escreva suas linhas de volta, exceto a linha que você deseja excluir:

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)

Você precisa strip("\n")do caractere de nova linha na comparação, porque se o arquivo não terminar com um caractere de nova linha, o último linetambém não.

houbysoft
fonte
2
por que precisamos abrir e fechá-lo duas vezes?
Ooker
3
@ Ooker: Você precisa abrir o arquivo duas vezes (e fechá-lo no meio) porque no primeiro modo é "somente leitura" porque você está apenas lendo as linhas atuais do arquivo. Em seguida, feche-o e abra-o novamente no "modo de gravação", onde o arquivo é gravável e você substitui o conteúdo do arquivo sem a linha que deseja remover.
Devin
4
Por que o Python não nos permite fazer isso em uma linha?
Ooker
5
@Ooker, Ao ler uma linha, tente imaginar um cursor se movendo ao longo da linha enquanto é lido. Uma vez que essa linha tenha sido lida, o cursor passou para ela. Ao tentar gravar no arquivo, você escreve onde está o cursor atualmente. Ao reabrir o arquivo, você redefine o cursor.
Waddas
4
Use o composto!
precisa saber é o seguinte
101

Solução para esse problema com apenas uma única abertura:

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 solução abre o arquivo no modo r / w ("r +") e utiliza o recurso de redefinição do ponteiro f e truncar para remover tudo após a última gravação.

Lother
fonte
2
Isso funcionou muito bem para mim, pois eu também tinha que usar o lockfile (fcntl). Não consegui encontrar nenhuma maneira de usar o fileinput junto com o fcntl.
Easyrider
1
Seria bom ver alguns efeitos colaterais desta solução.
user1767754
3
Eu não faria isso. Se ocorrer um erro no forloop, você terminará com um arquivo parcialmente substituído, com linhas duplicadas ou uma linha cortada pela metade. Você pode querer f.truncate()logo depois f.seek(0). Dessa forma, se você receber um erro, acabará com um arquivo incompleto. Mas a solução real (se você tiver espaço em disco) é gerar um arquivo temporário e usá-lo os.replace()ou pathlib.Path(temp_filename).replace(original_filename)trocá-lo pelo original depois que tudo tiver sido bem-sucedido.
224 de Boris
Você pode adicionar i.strip('\n') != "line you want to remove..."como mencionado na resposta aceita, que resolveria perfeitamente meu problema. Porque simplesmente inão fez nada por mim
Mangohero1 06/04
31

A melhor e mais rápida opção, em vez de armazenar tudo em uma lista e reabrir o arquivo para gravá-lo, é na minha opinião reescrever o arquivo em outro 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)

É isso aí! Em um loop e apenas um, você pode fazer a mesma coisa. Será muito mais rápido.

Barnabe
fonte
Em vez de usar o normal para loop, podemos fazer uso da expressão Generator. Dessa forma, o programa não carrega todas as linhas do arquivo para a memória, o que não é uma boa ideia no caso de arquivos grandes. Ele terá apenas uma linha na memória de cada vez. Com a expressão do gerador, o loop será parecido com,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde 25/02
4
@ShriShinde Você também não está lendo o arquivo na memória ao fazer um loop sobre o objeto de arquivo, portanto esta solução funciona da mesma maneira que a sua sugestão.
Steinar Lima 27/02
Você pode querer excluir o arquivo original e mudar o nome do segundo arquivo ao nome do arquivo original, que com Python em um sistema operacional Linux seria algo como isto,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Max
6
os.replace(novo no python v 3.3) é mais multiplataforma do que uma chamada de sistema mv.
7yl4r
Simples e ótimo.
JuBaer AD
27

Esta é uma "bifurcação" da resposta do @Lother (que eu acredito que deve ser considerada a resposta certa).


Para um arquivo como este:

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

Este garfo da solução Lother funciona bem:

#!/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()

Melhorias:

  • with open, que descartam o uso de f.close()
  • mais claro if/elsepara avaliar se a string não está presente na linha atual
ivanleoncz
fonte
Se f.seek (0) for necessário?
yifan
@yifan yes. Caso contrário, em vez de substituir o arquivo, você o anexará (sem as linhas excluídas).
Boris
5

O problema com a leitura de linhas na primeira passagem e a alteração (exclusão de linhas específicas) na segunda passagem é que, se o tamanho dos arquivos for grande, a memória ficará sem RAM. Em vez disso, uma abordagem melhor é ler as linhas, uma a uma, e gravá-las em um arquivo separado, eliminando as que você não precisa. Eu executei essa abordagem com arquivos de 12 a 50 GB e o uso da RAM permanece quase constante. Somente os ciclos da CPU mostram o processamento em andamento.

Kingz
fonte
2

Gostei da abordagem fileinput, conforme explicado nesta resposta: Excluindo uma linha de um arquivo de texto (python)

Digamos, por exemplo, que eu tenho um arquivo com linhas vazias e quero remover linhas vazias, eis como eu o resolvi:

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: As linhas vazias no meu caso tinham comprimento 1

Deep
fonte
2

Se você usa Linux, pode tentar a seguinte abordagem.
Suponha que você tenha um arquivo de texto chamado animal.txt:

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

Exclua a primeira linha:

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

então

$ cat animal.txt
pig
cat
monkey
elephant
Ren
fonte
7
Esta solução não é independente do sistema operacional e, como o OP não especificou um sistema operacional, não há motivo para postar uma resposta específica do Linux.
Steinar Lima 27/02
2
Qualquer um que sugerir o uso de subprocessos para qualquer coisa que possa ser feita apenas com python recebe um voto negativo! E +1 para @SteinarLima ... eu concordo #
Jamie Lindsey
2

Eu acho que se você ler o arquivo em uma lista, faça o que você pode percorrer na lista para procurar o apelido do qual deseja se livrar. Você pode fazer isso de maneira muito eficiente sem criar arquivos adicionais, mas precisará gravar o resultado novamente no arquivo de origem.

Aqui está como eu poderia fazer isso:

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

Estou assumindo que nicknames.csvcontém dados como:

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

Em seguida, carregue o arquivo na lista:

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

Em seguida, itere na lista para corresponder às suas entradas a serem excluídas:

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 fim, escreva o resultado novamente no arquivo:

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()
A Malik
fonte
1

Em geral, você não pode; você precisa escrever o arquivo inteiro novamente (pelo menos do ponto de alteração até o final).

Em alguns casos específicos, você pode fazer melhor que isso -

se todos os seus elementos de dados tiverem o mesmo comprimento e não em uma ordem específica, e você souber o deslocamento daquele que deseja se livrar, copie o último item sobre o que será excluído e trunque o arquivo antes do último item ;

ou você pode simplesmente sobrescrever o bloco de dados com um valor 'são dados incorretos, ignorar' ou manter um sinalizador 'este item foi excluído' nos elementos de dados salvos, para que você possa marcá-lo como excluído sem modificar o arquivo.

Provavelmente isso é um exagero para documentos curtos (algo abaixo de 100 KB?).

Hugh Bothwell
fonte
1

Provavelmente, você já obteve uma resposta correta, mas aqui está a minha. Em vez de usar uma lista para coletar dados não filtrados (que readlines()método faz), eu uso dois arquivos. Um é para reter os dados principais e o segundo é para filtrar os dados quando você exclui uma sequência específica. Aqui está um 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 você ache isso útil! :)

andrii1986
fonte
0

Salve as linhas do arquivo em uma lista, remova da lista a linha que deseja excluir e grave as linhas restantes em um novo arquivo

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
fonte
Ao dar uma resposta, é preferível dar uma explicação sobre POR QUE sua resposta é essa.
Stephen Rauch
Se o seu arquivo não terminar com uma nova linha, esse código não removerá a última linha, mesmo que contenha a palavra que você deseja remover.
Boris
0

Aqui está outro método para remover uma / algumas linhas de um arquivo:

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
fonte
0

Eu gosto desse método usando fileinput e o método 'inplace':

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

É um pouco menos prolixo do que as outras respostas e é rápido o suficiente para

Ru887321
fonte
0

Você pode usar a rebiblioteca

Supondo que você seja capaz de carregar seu arquivo txt completo. Você define uma lista de apelidos indesejados e os substitui por uma sequência vazia "".

# 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
fonte
-1

Para excluir uma linha específica de um arquivo pelo seu número de linha :

Substitua variáveis filename e line_to_delete pelo nome do seu arquivo e o número da linha que você deseja excluir.

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

Exemplo de saída :

Deleted line: 3
Aram Maliachi
fonte
não há necessidade de construir um ditado, basta usarfor nb, line in enumerate(f.readlines())
Dionys
-3

Pegue o conteúdo do arquivo, divida-o por nova linha em uma tupla. Em seguida, acesse o número da linha da sua tupla, junte a tupla de resultado e substitua no arquivo.

Nikhil
fonte
6
(1) você quer dizer tuple(f.read().split('\n'))? (2) "acesse o número da linha da sua tupla" e "junte-se à tupla de resultado" soa bastante misterioso; código Python real pode ser mais compreensível.
precisa