텍스트 파일을 수정하는 방법?


175

Python을 사용하고 있으며 파일을 삭제하거나 복사하지 않고 텍스트 파일에 문자열을 삽입하고 싶습니다. 어떻게해야합니까?


1
Alex Martelli 가이 답변을 참조 할 수 있습니다 .
Alok



@ 다른 게시물 어쨌든 텍스트 파일의 지정된 위치에 줄삽입 하는 복제물 이며 분명히 여기에 명확한 답변이 있습니다. 왜 다른 방법 대신 여기에 답변을 추가하지 않습니까? 수락 된 답변은 좋은 질문에 대한 요구 사항 이 아닙니다 .
Bhargav Rao

@BhargavRao 투표가 취소되었습니다. 그래도 그 사본을 찾았을 것입니다!
애니 메논

답변:


134

불행히도 파일을 다시 쓰지 않고 파일 중간에 삽입하는 방법은 없습니다. 이전 포스터에서 알 수 있듯이, 파일을 추가하거나 찾기를 사용하여 파일의 일부를 덮어 쓸 수 있지만 처음이나 중간에 물건을 추가하려면 다시 써야합니다.

이것은 파이썬이 아닌 운영 체제입니다. 모든 언어에서 동일합니다.

내가 일반적으로하는 일은 파일에서 읽고 수정하고 myfile.txt.tmp라는 새 파일 또는 이와 비슷한 파일에 쓰는 것입니다. 파일이 너무 커서 전체 파일을 메모리로 읽는 것보다 낫습니다. 임시 파일이 완성되면 원본 파일과 동일하게 이름을 바꿉니다.

파일 쓰기가 중단되거나 어떤 이유로 든 중단 된 경우에도 원본 파일은 그대로 유지되므로이 방법을 사용하는 것이 안전합니다.


3
awk / sed와 같은 유닉스 도구는 코드에서 비슷한 작업을 수행합니까?
Manish Gill

이것이 모든 언어에서 동일하다는 것은 사실이 아닙니다. ActionScript에서 : fileStream.openAsync (filename, FileMode.UPDATE); 그런 다음 원하는 파일의 아무 곳으로나 가서 무엇이든 변경할 수 있습니다.
AndrewBenjamin

2
@AndrewBenjamin ActionScript에서 어떤 시스템 호출을 수행하는지 알고 있습니까? openAsync가 파일을 읽고 호출 후에 새 파일을 쓸 가능성이 있습니까?
AlexLordThorsen

@Rawrgulmuffins는하지 않습니다. 그러나 몇 GB의 파일 크기를 처리하는 데 사용했기 때문에 전체 파일을 메모리로 읽지 않는다는 것을 알고 있습니다. C # 스트림 라이터로 쓰는 것과 같다고 생각합니다. 필자는 파이썬을 대규모 개발 및 파일 조작이 아닌 작은 일을 빠르게 수행하는 도구로 본다.
AndrewBenjamin

4
@AndrewBenjamin, 사용자는 파일을 탐색하고 변경하는 것에 대해 묻지 않습니다 (내가 아는 모든 언어가 그렇게 할 수 있습니다). 그는 파일에 이미있는 것을 단순히 바꾸거나 덮어 쓰는 것과는 다른 텍스트 삽입에 대해 묻고 있습니다. 실제 응용 프로그램에서는 다를 수 있지만 ActionScript API 에서 찾을 수있는 항목은 이와 관련하여 다른 언어와 다르게 동작한다는 것을 나타냅니다.
eestrada

104

당신이하고 싶은 일에 달려 있습니다. 추가하려면 "a"로 열 수 있습니다.

 with open("foo.txt", "a") as f:
     f.write("new line\n")

무언가를 미리 표현하려면 먼저 파일에서 읽어야합니다.

with open("foo.txt", "r+") as f:
     old = f.read() # read everything in the file
     f.seek(0) # rewind
     f.write("new line\n" + old) # write the new line before

9
그냥 작은 추가는 사용하는 with당신이 "에서 추가 할 필요가 파이썬 2.5에서 문 미래 가져 오기는 with_statement을". 그 외에는 with명령문으로 파일을 여는 것이 수동 닫기보다 읽기 쉽고 오류가 덜 발생합니다.
Alexander Kojevnikov

2
arg를 fileinput사용할 때 더티 open / read / modify / write / replace 루틴을 처리 하는 helper lib를 고려할 수 있습니다 inline=True. 예를 들면 다음과 같습니다. stackoverflow.com/a/2363893/47390
mikegreenberg

3
파일을 닫는 것을 잊지 마십시오. f.Close()
D.Rosado

5
내가 사용하는 스타일은 아니지만 D.Rosado, with 스타일을 사용할 때 수동으로 닫을 필요는 없다고 생각합니다. with는 생성 한 리소스를 추적합니다.
Chris

4
당신은 하지 않습니다 수동으로 가까운 파일이 필요합니다. 이것이 "with"를 사용하는 요점입니다. (실제로 파이썬은 파일 객체가 가비지 수집되는 즉시 이것을 수행합니다. CPython에서는 바인딩 된 이름이 범위를 벗어날 때 발생하지만 다른 구현은 그렇지 않으며 CPython은 언젠가는 그 일을 중단 할 수 있습니다 따라서 "with"를 권장합니다)
Jürgen A. Erhard

71

fileinputinplace = 1 매개 변수를 사용하면 Python 표준 라이브러리 의 모듈이 파일을 그대로 다시 작성합니다.

import sys
import fileinput

# replace all occurrences of 'sit' with 'SIT' and insert a line after the 5th
for i, line in enumerate(fileinput.input('lorem_ipsum.txt', inplace=1)):
    sys.stdout.write(line.replace('sit', 'SIT'))  # replace 'sit' and write
    if i == 4: sys.stdout.write('\n')  # write a blank line after the 5th line

1
이것은 python3에서 어떻게 작동합니까? 방금 파이썬과 같은 코드가있는 앱을 python3으로 포팅 했으며이 코드를 전혀 작동시키지 못했습니다. 'line'변수는 바이트 유형이므로 유니 코드로 디코딩 한 다음 수정 한 다음 바이트로 다시 인코딩하려고했지만 제대로 작동하지 않습니다. 머리 꼭대기에서 기억할 수없는 예외가 발생했습니다. python3에서 fileinput inplace = 1을 사용하는 사람들이 성공합니까?
robru

1
@Robru : 여기에 Python 3 코드가 있습니다
jfs

13
그러나 중요한 문제는 중요하지 않은 파일에서 먼저 테스트 했습니까?
Paula Livingstone

33

파일을 제자리에 다시 쓰려면 종종 기존 사본을 수정 된 이름으로 저장해야합니다. 유닉스 사람들 ~은 오래된 것을 표시하기 위해를 추가합니다 . Windows 사용자는 .bak 또는 .old를 추가하거나 파일 이름을 바꾸거나 이름 앞에 ~를 두는 모든 종류의 작업을 수행합니다.

import shutil
shutil.move( afile, afile+"~" )

destination= open( aFile, "w" )
source= open( aFile+"~", "r" )
for line in source:
    destination.write( line )
    if <some condition>:
        destination.write( >some additional line> + "\n" )
source.close()
destination.close()

대신 shutil다음을 사용할 수 있습니다.

import os
os.rename( aFile, aFile+"~" )

1
좋아 보인다 .readlines ()가 소스를 반복하는 것보다 더 나은지 궁금하십니까?
bozdoz

2
@bozdoz : readlines가 전체 파일을 읽으므로 반복이 더 좋습니다. 큰 파일에는 적합하지 않습니다. 물론 현지화 된 방식으로 수정을 수행 할 수 있다고 가정합니다. 때로는 할 수 없거나 코드가 훨씬 복잡해집니다.
Jürgen A. Erhard 2016 년

@ S.Lott : os.rename(aFile, aFile + "~")사본을 만들지 않고 소스 파일의 이름을 수정합니다.
Patapoom

14

파이썬의 mmap 모듈을 사용하면 파일에 삽입 할 수 있습니다. 다음 샘플은 Unix에서 수행 할 수있는 방법을 보여줍니다 (Windows mmap은 다를 수 있음). 모든 오류 조건을 처리하는 것은 아니며 원본 파일이 손상되거나 손실 될 수 있습니다. 또한 유니 코드 문자열을 처리하지 않습니다.

import os
from mmap import mmap

def insert(filename, str, pos):
    if len(str) < 1:
        # nothing to insert
        return

    f = open(filename, 'r+')
    m = mmap(f.fileno(), os.path.getsize(filename))
    origSize = m.size()

    # or this could be an error
    if pos > origSize:
        pos = origSize
    elif pos < 0:
        pos = 0

    m.resize(origSize + len(str))
    m[pos+len(str):] = m[pos:origSize]
    m[pos:pos+len(str)] = str
    m.close()
    f.close()

'r +'모드에서 열린 파일을 사용하여 mmap없이이 작업을 수행 할 수도 있지만 삽입 위치에서 EOF에 이르기까지 파일의 내용을 읽고 임시 저장해야하므로 덜 편리하고 덜 효율적입니다. 거대하다.


14

Adam이 언급했듯이 시스템 메모리의 일부를 교체하고 다시 쓸 수있는 충분한 메모리가 있는지에 대한 접근 방식을 결정하기 전에 시스템 제한을 고려해야합니다.

작은 파일을 다루거나 메모리 문제가없는 경우 다음과 같은 도움이 될 수 있습니다.

옵션 1) 전체 파일을 메모리로 읽고 행의 전체 또는 일부에 정규식 대체를 수행하고 해당 행에 추가 행을 추가하십시오. '중간 라인'이 파일에서 고유한지 확인해야하거나 각 라인에 타임 스탬프가있는 경우 이는 매우 안정적이어야합니다.

# open file with r+b (allow write and binary mode)
f = open("file.log", 'r+b')   
# read entire content of file into memory
f_content = f.read()
# basically match middle line and replace it with itself and the extra line
f_content = re.sub(r'(middle line)', r'\1\nnew line', f_content)
# return pointer to top of file so we can re-write the content with replaced string
f.seek(0)
# clear file content 
f.truncate()
# re-write the content with the updated content
f.write(f_content)
# close file
f.close()

옵션 2) 가운데 줄을 찾아 해당 줄과 추가 줄로 바꿉니다.

# open file with r+b (allow write and binary mode)
f = open("file.log" , 'r+b')   
# get array of lines
f_content = f.readlines()
# get middle line
middle_line = len(f_content)/2
# overwrite middle line
f_content[middle_line] += "\nnew line"
# return pointer to top of file so we can re-write the content with replaced string
f.seek(0)
# clear file content 
f.truncate()
# re-write the content with the updated content
f.write(''.join(f_content))
# close file
f.close()

2

이것을 깨끗하게하기 위해 작은 수업을 썼습니다.

import tempfile

class FileModifierError(Exception):
    pass

class FileModifier(object):

    def __init__(self, fname):
        self.__write_dict = {}
        self.__filename = fname
        self.__tempfile = tempfile.TemporaryFile()
        with open(fname, 'rb') as fp:
            for line in fp:
                self.__tempfile.write(line)
        self.__tempfile.seek(0)

    def write(self, s, line_number = 'END'):
        if line_number != 'END' and not isinstance(line_number, (int, float)):
            raise FileModifierError("Line number %s is not a valid number" % line_number)
        try:
            self.__write_dict[line_number].append(s)
        except KeyError:
            self.__write_dict[line_number] = [s]

    def writeline(self, s, line_number = 'END'):
        self.write('%s\n' % s, line_number)

    def writelines(self, s, line_number = 'END'):
        for ln in s:
            self.writeline(s, line_number)

    def __popline(self, index, fp):
        try:
            ilines = self.__write_dict.pop(index)
            for line in ilines:
                fp.write(line)
        except KeyError:
            pass

    def close(self):
        self.__exit__(None, None, None)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        with open(self.__filename,'w') as fp:
            for index, line in enumerate(self.__tempfile.readlines()):
                self.__popline(index, fp)
                fp.write(line)
            for index in sorted(self.__write_dict):
                for line in self.__write_dict[index]:
                    fp.write(line)
        self.__tempfile.close()

그런 다음 다음과 같이 사용할 수 있습니다.

with FileModifier(filename) as fp:
    fp.writeline("String 1", 0)
    fp.writeline("String 2", 20)
    fp.writeline("String 3")  # To write at the end of the file

이것은 개인적으로 작동하지 않으며 파일에 텍스트를 추가하지만 모든 것을 먼저 제거합니다!
Bret Hawker

실제로 이것은 전혀 작동하지 않습니다. 부끄러운 데, 좋은 생각 같았 기 때문입니다.
Mario Krušelj 2016 년

0

유닉스를 알고 있다면 다음을 시도해보십시오.

참고 : $는 명령 프롬프트를 의미합니다.

다음과 같은 내용의 my_data.txt 파일이 있다고 가정하십시오.

$ cat my_data.txt
This is a data file
with all of my data in it.

그런 다음 os모듈을 사용하여 일반적인 sed명령을 사용할 수 있습니다

import os

# Identifiers used are:
my_data_file = "my_data.txt"
command = "sed -i 's/all/none/' my_data.txt"

# Execute the command
os.system(command)

sed를 모르면 확인하십시오. 매우 유용합니다.


3
전혀 파이썬적인 것이 아닙니다
DarkSuniuM
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.