파일에서 특정 줄을 삭제하기 위해 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

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

변수 filename 및 line_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.