Tôi đang sử dụng Python và muốn chèn một chuỗi vào tệp văn bản mà không xóa hoặc sao chép tệp. Làm thế nào tôi có thể làm điều đó?
Tôi đang sử dụng Python và muốn chèn một chuỗi vào tệp văn bản mà không xóa hoặc sao chép tệp. Làm thế nào tôi có thể làm điều đó?
Câu trả lời:
Thật không may, không có cách nào để chèn vào giữa một tập tin mà không viết lại nó. Như các áp phích trước đã chỉ ra, bạn có thể thêm vào một tệp hoặc ghi đè lên một phần của tệp bằng cách sử dụng tìm kiếm nhưng nếu bạn muốn thêm nội dung ở đầu hoặc giữa, bạn sẽ phải viết lại.
Đây là một điều hệ điều hành, không phải là một điều Python. Nó là như nhau trong tất cả các ngôn ngữ.
Những gì tôi thường làm là đọc từ tệp, thực hiện các sửa đổi và ghi nó ra một tệp mới gọi là myfile.txt.tmp hoặc đại loại như thế. Điều này tốt hơn là đọc toàn bộ tệp vào bộ nhớ vì tệp có thể quá lớn cho điều đó. Khi tập tin tạm thời hoàn thành, tôi đổi tên nó giống như tập tin gốc.
Đây là một cách tốt, an toàn để làm điều đó bởi vì nếu tệp viết bị treo hoặc hủy bỏ vì bất kỳ lý do gì, bạn vẫn có tệp gốc chưa được xử lý.
Phụ thuộc vào những gì bạn muốn làm. Để nối thêm, bạn có thể mở nó bằng "a":
with open("foo.txt", "a") as f:
f.write("new line\n")
Nếu bạn muốn trả trước một cái gì đó bạn phải đọc từ tệp trước:
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
with
câu lệnh trong Python 2.5, bạn cần thêm "từ lần nhập tương lai with_statement". Ngoài ra, việc mở các tệp bằng with
câu lệnh chắc chắn dễ đọc hơn và ít bị lỗi hơn so với đóng thủ công.
fileinput
lib của người trợ giúp với việc xử lý thói quen mở / đọc / sửa đổi / ghi / thay thế một cách độc đáo khi sử dụng inline=True
arg. Ví dụ ở đây: stackoverflow.com/a/2363893/47390
f.Close()
Các fileinput
mô-đun của thư viện chuẩn của Python sẽ viết lại một inplace tập tin nếu bạn sử dụng inplace tham số = 1:
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
Viết lại một tập tin tại chỗ thường được thực hiện bằng cách lưu bản sao cũ với một tên đã sửa đổi. Unix folks thêm một ~
để đánh dấu cái cũ. Windows folks làm tất cả mọi thứ - thêm .bak hoặc .old - hoặc đổi tên hoàn toàn tệp hoặc đặt ~ ở phía trước tên.
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()
Thay vì shutil
, bạn có thể sử dụng như sau.
import os
os.rename( aFile, aFile+"~" )
os.rename(aFile, aFile + "~")
sẽ sửa đổi tên của tệp nguồn, không tạo bản sao.
Mô-đun mmap của Python sẽ cho phép bạn chèn vào một tệp. Mẫu sau đây cho thấy cách nó có thể được thực hiện trong Unix (Windows mmap có thể khác). Lưu ý rằng điều này không xử lý tất cả các điều kiện lỗi và bạn có thể bị hỏng hoặc mất tệp gốc. Ngoài ra, điều này sẽ không xử lý chuỗi unicode.
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()
Cũng có thể thực hiện việc này mà không cần mmap với các tệp được mở ở chế độ 'r +', nhưng sẽ không thuận tiện và kém hiệu quả hơn khi bạn phải đọc và lưu tạm thời nội dung của tệp từ vị trí chèn vào EOF - có thể là rất lớn.
Như Adam đã đề cập, bạn phải xem xét các giới hạn hệ thống của mình trước khi quyết định tiếp cận xem bạn có đủ bộ nhớ để đọc tất cả vào bộ nhớ thay thế các phần của nó và viết lại không.
Nếu bạn đang xử lý một tệp nhỏ hoặc không có vấn đề về bộ nhớ, điều này có thể giúp:
Tùy chọn 1) Đọc toàn bộ tệp vào bộ nhớ, thực hiện thay thế regex trên toàn bộ hoặc một phần của dòng và thay thế nó bằng dòng đó cộng với dòng bổ sung. Bạn sẽ cần đảm bảo rằng 'đường giữa' là duy nhất trong tệp hoặc nếu bạn có dấu thời gian trên mỗi dòng thì điều này sẽ rất đáng tin cậy.
# 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()
Tùy chọn 2) Tìm ra đường giữa và thay thế nó bằng đường đó cộng với đường phụ.
# 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()
Đã viết một lớp nhỏ để làm điều này sạch sẽ.
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()
Sau đó, bạn có thể sử dụng nó theo cách này:
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
Nếu bạn biết một số unix, bạn có thể thử như sau:
Ghi chú: $ có nghĩa là dấu nhắc lệnh
Giả sử bạn có tệp my_data.txt với nội dung như vậy:
$ cat my_data.txt
This is a data file
with all of my data in it.
Sau đó, sử dụng os
mô-đun, bạn có thể sử dụng các sed
lệnh thông thường
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)
Nếu bạn không biết về sed, hãy kiểm tra nó, nó cực kỳ hữu ích.