파일에서 특정 줄을 삭제하기 위해 Python 사용


145

별명으로 가득 찬 텍스트 파일이 있다고 가정 해 봅시다. Python을 사용하여이 파일에서 특정 닉네임을 삭제하려면 어떻게해야합니까?


1
fileinput@ jf-sebastian 여기에 설명 된대로 시도 하십시오 . 간단한 for구문 으로 임시 파일을 통해 한 줄씩 작업 할 수있는 것으로 보입니다 .
Kevin

답변:


205

먼저 파일을 열고 파일에서 모든 줄을 가져옵니다. 그런 다음 파일을 쓰기 모드로 다시 열고 삭제하려는 줄을 제외하고 줄을 다시 쓰십시오.

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)

당신은 필요 strip("\n")파일이 개행 문자로 맨 마지막 종료하지 않는 경우 때문에 비교에서 개행 문자 line하지 않습니다 중 하나를.


2
왜 우리는 그것을 두 번 열고 닫아야합니까?
Ooker

3
@Ooker : 첫 번째 모드에서는 파일의 현재 줄을 읽기 때문에 "읽기 전용"이기 때문에 파일을 두 번 열어야합니다 (사이에 닫아야합니다). 그런 다음 파일을 닫고 "쓰기 모드"로 다시여십시오. 여기서 파일은 쓰기 가능하며 파일의 내용을 제거하려는 행으로 바꿉니다.
Devin

4
파이썬이 왜 한 줄로 이것을 할 수 없습니까?
Ooker

5
@Ooker, 줄을 읽을 때 줄을 따라 커서가 움직이는 것을 상상해보십시오. 해당 줄을 읽은 후에는 커서가지나갑니다. 파일에 쓰려고 할 때 커서가 현재있는 곳에 쓰십시오. 파일을 다시 열면 커서가 재설정됩니다.
Waddas

4
with 화합물을 사용하십시오!
Sceluswe

100

한 번만 열면이 문제에 대한 해결책 :

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

이 솔루션은 파일을 r / w 모드 ( "r +")로 열고 탐색을 사용하여 f- 포인터를 재설정 한 다음 잘라서 마지막 쓰기 후 모든 것을 제거합니다.


2
lockfile (fcntl)도 사용해야했기 때문에 이것은 매우 잘 작동했습니다. fcntl과 함께 fileinput을 사용하는 방법을 찾지 못했습니다.
Easyrider

1
이 솔루션의 부작용을 보는 것이 좋을 것입니다.
user1767754

3
나는 이것을하지 않을 것입니다. for루프에 오류가 발생 하면 부분적으로 덮어 쓴 파일이 생겨서 줄이 중복되거나 줄이 반으로 줄어 듭니다. 대신 에 f.truncate()바로 원할 수도 있습니다 f.seek(0). 그렇게하면 오류가 발생하면 불완전한 파일로 끝납니다. 그러나 실제 솔루션 (디스크 공간이있는 경우)은 임시 파일로 출력 한 다음 모든 것이 성공한 후에 원본 파일을 사용 os.replace()하거나 pathlib.Path(temp_filename).replace(original_filename)원본 파일 로 바꾸는 것입니다.
Boris

i.strip('\n') != "line you want to remove..."허용 된 답변에 언급 된대로 추가하면 내 문제를 완벽하게 해결할 수 있습니다. i나를 위해 아무것도하지 않았기 때문에
Mangohero1

31

목록에 모든 것을 저장하고 파일을 쓰기 위해 파일을 다시 여는 것이 아니라 가장 빠르고 빠른 옵션은 다른 곳에서 파일을 다시 쓰는 것입니다.

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)

그게 다야! 하나의 루프에서 하나만 동일한 작업을 수행 할 수 있습니다. 훨씬 빠를 것입니다.


일반적인 for 루프를 사용하는 대신 Generator Expression 을 사용할 수 있습니다. 이렇게하면 프로그램이 파일에서 메모리로 모든 행을로드하지 않으므로 큰 파일의 경우 좋지 않습니다. 한 번에 메모리에 한 줄만 있습니다. 생성기 표현식을 사용하면 다음과 같이 보일 것입니다.(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde

4
@ShriShinde 파일 객체를 반복 할 때 파일을 메모리로 읽지 않으므로이 솔루션은 제안과 동일하게 작동합니다.
Steinar Lima

원본 파일을 삭제하고 두 번째 파일의 이름을 원본 파일 이름으로 바꾸고 싶을 수도 있습니다. Linux OS에서 Python을 사용하는 경우 다음과 같습니다.subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Max

6
os.replace(python v 3.3의 새로운 기능)은에 대한 시스템 호출보다 크로스 플랫폼 mv입니다.
7yl4r

간단하고 훌륭합니다.
JuBaer AD

27

이것은 @Lother 의 답변 에서 나온 "포크"입니다 (정답이라고 생각합니다).


다음과 같은 파일의 경우 :

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

Lother의 솔루션에서 나온이 포크는 잘 작동합니다.

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

개량:

  • with open의 사용법을 버리는 f.close()
  • if/else문자열이 현재 줄에 없는지 평가하기 위해 더 명확 합니다.

f.seek (0)이 필요한 경우?
yifan

@yifan 예. 그렇지 않으면 파일을 덮어 쓰지 않고 파일을 제외 할 줄없이 파일 자체에 추가합니다.
Boris

5

첫 번째 패스에서 행을 읽고 두 번째 패스에서 변경 (특정 행 삭제)하는 문제는 파일 크기가 크면 RAM이 부족하다는 것입니다. 대신, 더 나은 방법은 줄을 하나씩 읽고 별도의 파일에 작성하여 필요없는 줄을 제거하는 것입니다. 12-50GB의 파일 로이 접근법을 실행했으며 RAM 사용량은 거의 일정합니다. CPU 주기만 처리가 진행 중임을 보여줍니다.


2

이 답변에서 설명한 파일 입력 방식이 마음에 들었습니다 . 텍스트 파일에서 줄 삭제 (파이썬)

예를 들어 빈 줄이 들어있는 파일이 있고 빈 줄을 제거하고 싶다고 가정 해 보겠습니다.

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

참고 : 필자의 경우 빈 줄의 길이는 1입니다.


2

Linux를 사용하는 경우 다음 접근 방식을 시도 할 수 있습니다.
다음과 같은 이름의 텍스트 파일이 있다고 가정하십시오 animal.txt.

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

첫 번째 줄을 삭제하십시오.

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

그때

$ cat animal.txt
pig
cat
monkey
elephant

7
이 솔루션은 OS에 구애받지 않으며 OP가 운영 체제를 지정하지 않았기 때문에 Linux 전용 답변 imo를 게시 할 이유가 없습니다.
Steinar Lima

2
파이썬으로 수행 할 수있는 작업에 하위 프로세스를 사용하도록 제안하는 사람은 공감대를 얻습니다! @SteinarLima에게 +1 ... 동의합니다
Jamie Lindsey

2

파일을 목록으로 읽으면 목록에서 반복하여 제거하려는 닉네임을 찾을 수 있다고 생각합니다. 추가 파일을 만들지 않고도 훨씬 효율적으로 수행 할 수 있지만 결과를 소스 파일에 다시 써야합니다.

이 작업을 수행하는 방법은 다음과 같습니다.

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

다음 nicknames.csv과 같은 데이터가 있다고 가정합니다 .

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

그런 다음 파일을 목록에로드하십시오.

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

다음으로 목록을 반복하여 입력과 일치하도록 삭제하십시오.

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

마지막으로 결과를 파일에 다시 씁니다.

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

1

일반적으로, 당신은 할 수 없습니다; 전체 파일을 다시 작성해야합니다 (적어도 변경 시점에서 끝까지).

어떤 경우에는 이것보다 더 잘 할 수 있습니다-

모든 데이터 요소의 길이가 동일하고 특정 순서가없는 경우 제거하려는 요소의 오프셋을 알고 있으면 삭제할 항목 위에 마지막 항목을 복사하고 마지막 항목 전에 파일을자를 수 있습니다 ;

또는 데이터 청크를 '이것은 잘못된 데이터입니다. 건너 뛰십시오'값으로 덮어 쓰거나 저장된 데이터 요소에 '이 항목이 삭제되었습니다'플래그를 유지하여 파일을 수정하지 않고 삭제 된 것으로 표시 할 수 있습니다.

이것은 짧은 문서 (100KB 미만의 문서)에는 과잉 일 수 있습니다.


1

아마, 당신은 이미 정답을 얻었지만 여기에 내 것이 있습니다. 필터링되지 않은 데이터를 수집하기 위해 목록을 사용하는 대신 ( readlines()메서드가 수행 하는 방식) 두 개의 파일을 사용합니다. 하나는 기본 데이터를 유지하기위한 것이고, 두 번째는 특정 문자열을 삭제할 때 데이터를 필터링하기위한 것입니다. 코드는 다음과 같습니다.

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

이 정보가 도움이 되길 바랍니다. :)


0

파일 줄을 목록에 저장 한 다음 삭제하려는 줄을 목록에서 제거하고 나머지 줄을 새 파일에 씁니다.

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)

답을 할 때 왜 답되는지에 대해 설명 하는 것이 좋습니다 .
Stephen Rauch 1

파일이 줄 바꿈으로 끝나지 않으면 제거하려는 단어가 포함되어 있어도이 코드는 마지막 줄을 제거하지 않습니다.
Boris

0

파일에서 / 일부 라인을 제거하는 다른 방법이 있습니다.

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

0

나는 fileinput과 'inplace'방법을 사용하는이 방법을 좋아합니다.

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

그것은 다른 답변보다 조금 덜 경박하고 충분히 빠릅니다.


0

re라이브러리를 사용할 수 있습니다

전체 txt 파일을로드 할 수 있다고 가정합니다. 그런 다음 원치 않는 닉네임 목록을 정의한 다음 빈 문자열 ""로 대체하십시오.

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

-1

줄 번호 로 파일의 특정 줄을 삭제하려면 :

변수 filenameline_to_delete파일 이름 과 삭제할 행 번호로 바꾸십시오.

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

출력 예 :

Deleted line: 3

dict을 작성할 필요가 없습니다. 사용for nb, line in enumerate(f.readlines())
Dionys

-3

파일의 내용을 가져와 줄 바꿈으로 튜플로 나눕니다. 그런 다음 튜플의 줄 번호에 액세스하고 결과 튜플에 가입 한 다음 파일을 덮어 씁니다.


6
(1) 당신은 의미합니까 tuple(f.read().split('\n'))?? (2) "튜플의 행 번호에 액세스하십시오"및 "결과 튜플에 참여하십시오"는 다소 신비한 소리입니다. 실제 파이썬 코드는 더 이해하기 쉽습니다.
John Machin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.