Bir metin dosyası nasıl değiştirilir?


175

Python kullanıyorum ve dosyayı silmeden veya kopyalamadan bir metin dosyasına bir dize eklemek istiyorum. Bunu nasıl yapabilirim?


1
Bu cevaba Alex Martelli tarafından başvurabilirsiniz .
Alok



@Ani diğer yazı , yine de bir Metin Dosyasının Belirtilen Konumuna Satır Ekleme'nin kopyası ve kesinlikle burada açık ve net cevaplar var, Neden cevabınızı buraya başka yol yerine eklemiyorsunuz ? Kabul edilen cevap iyi bir soru için şart değildir .
Bhargav Rao

@BhargavRao Oy geri çekildi. Yine de bu yinelenen bulmalıydım!
Ani Menon

Yanıtlar:


134

Ne yazık ki bir dosyayı yeniden yazmadan ortasına yerleştirmenin bir yolu yoktur. Önceki posterlerin de belirttiği gibi, bir dosyaya ekleyebilir veya aramanın bir kısmını üzerine yazabilirsiniz, ancak başında veya ortasında bir şeyler eklemek istiyorsanız, yeniden yazmanız gerekir.

Bu bir işletim sistemi şeyidir, Python değil. Tüm dillerde aynıdır.

Genellikle yaptığım dosyadan okumak, değişiklikleri yapmak ve myfile.txt.tmp adlı yeni bir dosyaya ya da buna benzer bir dosyaya yazmak. Bu, tüm dosyayı belleğe okumaktan daha iyidir çünkü dosya bunun için çok büyük olabilir. Geçici dosya tamamlandığında, orijinal dosya ile aynı şekilde yeniden adlandırıyorum.

Bu, dosya yazma işlemi herhangi bir nedenle çökerse veya iptal edilirse, el değmemiş orijinal dosyanızın olması nedeniyle bunu yapmanın iyi ve güvenli bir yoludur.


3
Awk / sed gibi unix araçları kodlarında benzer bir şey yapar mı?
Manish Gill

Bunun tüm dillerde aynı olduğu doğru değil. ActionScript'te: fileStream.openAsync (dosya adı, FileMode.UPDATE); Sonra istediğim dosyada herhangi bir yere gidebilir ve herhangi bir şeyi değiştirebilirim.
AndrewBenjamin

2
@AndrewBenjamin ActionScript'in hangi sistem çağrılarını yaptığını biliyor musunuz? Çağrıdan sonra openAsync'in dosyayı okuma ve yeni bir tane yazma olasılığı var mı?
AlexLordThorsen

@Rawrgulmuffins yapmam. Ancak, birkaç GB dosya boyutlarını işlemek için kullandığımdan, tüm dosyayı belleğe okumadığını biliyorum. C # streamwriter ile yazmakla aynı olduğundan şüpheleniyorum. Python'u büyük ölçekli geliştirme ve dosya manipülasyonu yerine küçük işleri hızlı bir şekilde yapma aracı olarak görüyorum.
AndrewBenjamin

4
@AndrewBenjamin, kullanıcı dosyada arama yapmak ve değiştirmek istemiyor (bildiğim her dil bunu yapabilir); dosyada bulunanları değiştirmek / üzerine yazmaktan farklı olan metin eklemeyi soruyor. Belki pratik uygulamada farklıdır, ancak ActionScript API'sinde bulabileceğim hiçbir şey , bu konuda diğer dillerden farklı davrandığını gösterir.
eestrada

104

Ne yapmak istediğine bağlı. Eklemek için "a" ile açabilirsiniz:

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

Öncelikle dosyadan okumak zorunda olduğunuz bir şeyi önceden eklemek istiyorsanız:

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
Sadece küçük bir ek, withPython 2.5 deyimini kullanmak için " gelecekteki ithalat ile_statement gelen" eklemeniz gerekir . Bunun dışında, withifadeyle dosyaları açmak kesinlikle manuel olarak okunmaktan daha okunaklı ve daha az hataya açıktır.
Alexander Kojevnikov

2
fileinputArg kullanırken kirli lib / okuma / değiştirme / yazma / değiştirme rutinini güzel bir şekilde ele alan yardımcı lib'i düşünebilirsiniz inline=True. Burada örnek: stackoverflow.com/a/2363893/47390
mikegreenberg

3
Sadece dosyayı kapatmayı unutma. f.Close()
D.Rosado

5
Kullandığım bir stil değil, D.Rosado, ancak stili ile kullanırken, elle kapatmanız gerektiğini düşünmüyorum. With ile oluşturduğu kaynak takip edilir.
Chris

4
Sen yok manuel yakın dosyanın gerekir. Burada "ile" kullanmanın bütün mesele bu. (Peki, aslında, Python dosya nesnesi çöp toplanır toplanmaz bunu yapar; bu, CPython'da kendisine bağlı ad kapsam dışına çıktığında olur ... ancak diğer uygulamalar olmaz ve CPython bunu bir gün durdurabilir , yani "ile" önerilir)
Jürgen A. Erhard

71

fileinputEğer INPLACE = 1 parametresini kullanırsanız Python standart kütüphanenin modül dosya INPLACE tekrar sunacağına:

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
Bunun python3'te nasıl çalışması bekleniyor? Ben sadece python python3 için böyle bir kod vardı bir uygulama ported ve ben sadece bu doğru çalışmak için alamadım. 'Line' değişkeni bir bayt türüdür, ben unicode içine kod çözme ve sonra değiştirmek ve sonra tekrar bayt kodlama çalıştı ama sadece doğru işe yaramaz. Kafamın üstünden hatırlayamadığım bir istisna yarattı. Python3'te fileinput inplace = 1 kullanan kişiler başarılı oluyor mu?
robru

1
@Robru: İşte Python 3 kodu
jfs

13
Ama sorun değil, çünkü önce önemsiz bir dosyada test ettiniz değil mi?
Paula Livingstone

33

Bir dosyayı yerinde yeniden yazmak genellikle eski kopyanın değiştirilmiş bir adla kaydedilmesiyle yapılır. Unix kullanıcıları ~eskisini işaretlemek için a ekler . Windows kullanıcıları her türlü şeyi yapar - .bak veya .old - ekleyin veya dosyayı tamamen yeniden adlandırın veya ~ adının önüne koyun.

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

Bunun yerine shutilaşağıdakileri kullanabilirsiniz.

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

1
İyi görünüyor. .Readlines () kaynağının yinelemekten daha iyi olup olmadığını mı merak ediyorsunuz?
bozdoz

2
@bozdoz: readlines tüm dosyayı okuduğundan yineleme daha iyidir. Büyük dosyalar için iyi değil. Elbette, bu, değişikliklerinizi böyle yerel bir şekilde yapabileceğinizi varsayar. Bazen yapamazsınız veya kodunuz çok daha karmaşık hale gelir.
Jürgen A.Erhard

@ S.Lott: os.rename(aFile, aFile + "~")kaynak dosyanın adını değiştirir, kopya oluşturmaz.
Patapoom

14

Python'un mmap modülü bir dosyaya eklemenizi sağlar. Aşağıdaki örnek Unix'te nasıl yapılabileceğini göstermektedir (Windows mmap farklı olabilir). Bunun tüm hata koşullarını karşılamadığını ve orijinal dosyayı bozabileceğinizi veya kaybedebileceğinizi unutmayın. Ayrıca, bu unicode dizeleri işlemez.

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

Bunu, 'r +' modunda açılan dosyalar ile mmap olmadan da yapmak mümkündür, ancak dosyanın içeriğini ekleme konumundan EOF'a geçici olarak okumak ve depolamak zorunda olduğunuz için daha az kullanışlı ve daha az verimlidir. büyük ol.


14

Adam'ın belirttiği gibi, hepsini belleğe okumak için yeterli belleğe sahip olup olmadığına ve yeniden yazıp yazmayacağınıza karar vermeden önce sistem sınırlamalarınızı dikkate almanız gerekir.

Küçük bir dosyayla uğraşıyorsanız veya bellek sorununuz yoksa bu yardımcı olabilir:

Seçenek 1) Dosyanın tamamını belleğe okuyun, satırın tamamı veya bir bölümünde normal ifade yerine koyma yapın ve bu satırla birlikte ekstra satırla değiştirin. 'Orta çizginin' dosyada benzersiz olduğundan emin olmanız gerekir veya her satırda zaman damgalarınız varsa bu oldukça güvenilir olmalıdır.

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

Seçenek 2) Orta çizgiyi bulun ve bu çizgiyle artı ekstra çizgiyle değiştirin.

# 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

Bunu temiz yapmak için küçük bir sınıf yazdı.

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

Sonra bu şekilde kullanabilirsiniz:

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

Bu benim için kişisel olarak çalışmıyor, dosyaya metin ekliyor, ancak önce her şeyi kaldırıyor!
Bret Hawker

Gerçekten, bu hiç işe yaramıyor. Utanç, çünkü iyi bir fikir gibi görünüyordu.
Mario Krušelj

0

Bazı unix biliyorsanız aşağıdakileri deneyebilirsiniz:

Notlar: $, komut istemi anlamına gelir

Diyelim ki böyle içerik içeren bir my_data.txt dosyanız var:

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

Sonra osmodülü kullanarak olağan sedkomutları kullanabilirsiniz.

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'in farkında değilseniz, göz atın, son derece yararlıdır.


3
Hiç Pythonic değil
DarkSuniuM
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.