128

Let's say I have a text file containing:

Dan
Warrior
500
1
0

Is there a way I can edit a specific line in that text file? Right now I have this:

#!/usr/bin/env python
import io

myfile = open('stats.txt', 'r')
dan = myfile.readline()
print dan
print "Your name: " + dan.split('\n')[0]

try:
    myfile = open('stats.txt', 'a')
    myfile.writelines('Mage')[1]
except IOError:
        myfile.close()
finally:
        myfile.close()

Yes, I know that myfile.writelines('Mage')[1] is incorrect. But you get my point, right? I'm trying to edit line 2 by replacing Warrior with Mage. But can I even do that?

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
test
  • 17,706
  • 64
  • 171
  • 244
  • 2
    I think this post covers what you're looking for: http://stackoverflow.com/questions/1998233/replace-string-in-a-specific-line-using-python – Kyle Wild Jan 18 '11 at 00:48
  • 2
    If you have to do this sort of thing a lot, you might want to look into converting this file from text to something like bdb or other bdb-alike. – Nick Bastin Jan 18 '11 at 01:23

11 Answers11

166

You want to do something like this:

# with is like your try .. finally block in this case
with open('stats.txt', 'r') as file:
    # read a list of lines into data
    data = file.readlines()

print data
print "Your name: " + data[0]

# now change the 2nd line, note that you have to add a newline
data[1] = 'Mage\n'

# and write everything back
with open('stats.txt', 'w') as file:
    file.writelines( data )

The reason for this is that you can't do something like "change line 2" directly in a file. You can only overwrite (not delete) parts of a file - that means that the new content just covers the old content. So, if you wrote 'Mage' over line 2, the resulting line would be 'Mageior'.

Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
  • 2
    Hi Jochen, the statement "with open(filename, mode)" also closes filename implicitly after program exits it, right? – Radu May 28 '14 at 13:31
  • @Gabriel Thx, that is important to note, although I still don't use the with ... as file statement. Pythonic or not, I don't like it :) – Radu Apr 16 '15 at 18:09
  • 1
    @Radu it's a matter of getting used to it. I also used to close opened files manually via `close.` but now I see that using a `with` block is much cleaner. – Gabriel Apr 16 '15 at 18:10
  • 5
    Is it correct to assume, this is a solution preferred for small files? Otherwise we might need a lot of memory just to store the data. Moreover, even for 1 edit, we need to write the whole thing again. – Arindam Roychowdhury Sep 23 '16 at 11:52
  • 24
    Bad.. what if you have 20 Gb file? – Brans Ds Jan 12 '17 at 15:57
37
def replace_line(file_name, line_num, text):
    lines = open(file_name, 'r').readlines()
    lines[line_num] = text
    out = open(file_name, 'w')
    out.writelines(lines)
    out.close()

And then:

replace_line('stats.txt', 0, 'Mage')
Peter C
  • 6,219
  • 1
  • 25
  • 37
  • 18
    this would load the entire file's content into memory which might not be a good thing if the file is huge. – Steve Ng Feb 24 '14 at 06:46
  • 5
    @SteveNg do you have a solution for the problem you noticed ? both this answer and the accepted one rely on loading the entire file in memory – Blupon Nov 04 '19 at 09:49
28

You can use fileinput to do in-place editing

import fileinput
for line in fileinput.FileInput("myfile", inplace=1):
    if line .....: # select which lines you care about
        print line
Dragon
  • 2,017
  • 1
  • 19
  • 35
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
16

You can do it in two ways, choose what suits your requirement:

Method I.) Replacing using line number. You can use built-in function enumerate() in this case:

First, in read mode get all data in a variable

with open("your_file.txt",'r') as f:
    get_all=f.readlines()

Second, write to the file (where enumerate comes to action)

with open("your_file.txt",'w') as f:
    for i,line in enumerate(get_all,1):         ## STARTS THE NUMBERING FROM 1 (by default it begins with 0)    
        if i == 2:                              ## OVERWRITES line:2
            f.writelines("Mage\n")
        else:
            f.writelines(line)

Method II.) Using the keyword you want to replace:

Open file in read mode and copy the contents to a list

with open("some_file.txt","r") as f:
    newline=[]
    for word in f.readlines():        
        newline.append(word.replace("Warrior","Mage"))  ## Replace the keyword while you copy.  

"Warrior" has been replaced by "Mage", so write the updated data to the file:

with open("some_file.txt","w") as f:
    for line in newline:
        f.writelines(line)

This is what the output will be in both cases:

Dan                   Dan           
Warrior   ------>     Mage       
500                   500           
1                     1   
0                     0           
Aseem Yadav
  • 728
  • 7
  • 15
  • I tried both the methods ; Method (I) didn't work quite well but Method (II) was working fine . Still , they're good to work on . – Vishal 10B Jan 11 '22 at 13:34
3

If your text contains only one individual:

import re

# creation
with open('pers.txt','wb') as g:
    g.write('Dan \n Warrior \n 500 \r\n 1 \r 0 ')

with open('pers.txt','rb') as h:
    print 'exact content of pers.txt before treatment:\n',repr(h.read())
with open('pers.txt','rU') as h:
    print '\nrU-display of pers.txt before treatment:\n',h.read()


# treatment
def roplo(file_name,what):
    patR = re.compile('^([^\r\n]+[\r\n]+)[^\r\n]+')
    with open(file_name,'rb+') as f:
        ch = f.read()
        f.seek(0)
        f.write(patR.sub('\\1'+what,ch))
roplo('pers.txt','Mage')


# after treatment
with open('pers.txt','rb') as h:
    print '\nexact content of pers.txt after treatment:\n',repr(h.read())
with open('pers.txt','rU') as h:
    print '\nrU-display of pers.txt after treatment:\n',h.read()

If your text contains several individuals:

import re

# creation
with open('pers.txt','wb') as g:
    g.write('Dan \n Warrior \n 500 \r\n 1 \r 0 \n Jim  \n  dragonfly\r300\r2\n10\r\nSomo\ncosmonaut\n490\r\n3\r65')

with open('pers.txt','rb') as h:
    print 'exact content of pers.txt before treatment:\n',repr(h.read())
with open('pers.txt','rU') as h:
    print '\nrU-display of pers.txt before treatment:\n',h.read()


# treatment
def ripli(file_name,who,what):
    with open(file_name,'rb+') as f:
        ch = f.read()
        x,y = re.search('^\s*'+who+'\s*[\r\n]+([^\r\n]+)',ch,re.MULTILINE).span(1)
        f.seek(x)
        f.write(what+ch[y:])
ripli('pers.txt','Jim','Wizard')


# after treatment
with open('pers.txt','rb') as h:
    print 'exact content of pers.txt after treatment:\n',repr(h.read())
with open('pers.txt','rU') as h:
    print '\nrU-display of pers.txt after treatment:\n',h.read()

If the “job“ of an individual was of a constant length in the texte, you could change only the portion of texte corresponding to the “job“ the desired individual: that’s the same idea as senderle’s one.

But according to me, better would be to put the characteristics of individuals in a dictionnary recorded in file with cPickle:

from cPickle import dump, load

with open('cards','wb') as f:
    dump({'Dan':['Warrior',500,1,0],'Jim':['dragonfly',300,2,10],'Somo':['cosmonaut',490,3,65]},f)

with open('cards','rb') as g:
    id_cards = load(g)
print 'id_cards before change==',id_cards

id_cards['Jim'][0] = 'Wizard'

with open('cards','w') as h:
    dump(id_cards,h)

with open('cards') as e:
    id_cards = load(e)
print '\nid_cards after change==',id_cards
eyquem
  • 26,771
  • 7
  • 38
  • 46
2

I have been practising working on files this evening and realised that I can build on Jochen's answer to provide greater functionality for repeated/multiple use. Unfortunately my answer does not address issue of dealing with large files but does make life easier in smaller files.

with open('filetochange.txt', 'r+') as foo:
    data = foo.readlines()                  #reads file as list
    pos = int(input("Which position in list to edit? "))-1  #list position to edit
    data.insert(pos, "more foo"+"\n")           #inserts before item to edit
    x = data[pos+1]
    data.remove(x)                      #removes item to edit
    foo.seek(0)                     #seeks beginning of file
    for i in data:
        i.strip()                   #strips "\n" from list items
        foo.write(str(i))
theYnot
  • 152
  • 9
0

Suppose I have a file named file_name as following:

this is python
it is file handling
this is editing of line

We have to replace line 2 with "modification is done":

f=open("file_name","r+")
a=f.readlines()
for line in f:
   if line.startswith("rai"):
      p=a.index(line)
#so now we have the position of the line which to be modified
a[p]="modification is done"
f.seek(0)
f.truncate() #ersing all data from the file
f.close()
#so now we have an empty file and we will write the modified content now in the file
o=open("file_name","w")
for i in a:
   o.write(i)
o.close()
#now the modification is done in the file
juanlumn
  • 6,155
  • 2
  • 30
  • 39
Shivam Rai
  • 31
  • 3
0

writing initial data, print an empty str for updating it to a new data here we insert an empty str in the last line of the code, this code can be used in interative updation, in other words appending data in text.txt file

with open("data.txt", 'w') as f:
    f.write('first line\n'
            'second line\n'
            'third line\n'
            'fourth line\n'
            ' \n')

updating data in the last line of the text file

my_file=open('data.txt')
string_list = my_file.readlines()
string_list[-1] = "Edit the list of strings as desired\n"
my_file = open("data.txt", "w")
new_file_contents = "". join(string_list)
my_file. write(new_file_contents)
0

I used to have same request, eventually ended up with Jinja templating. Change your text file to below, and a variable lastname, then you can render the template by passing lastname='Meg', that's the most efficient and quickest way I can think of.

Dan 
{{ lastname }} 
Warrior 
500 
1 
0
ScottC
  • 3,941
  • 1
  • 6
  • 20
Saul Han
  • 49
  • 3
-1
#read file lines and edit specific item

file=open("pythonmydemo.txt",'r')
a=file.readlines()
print(a[0][6:11])

a[0]=a[0][0:5]+' Ericsson\n'
print(a[0])

file=open("pythonmydemo.txt",'w')
file.writelines(a)
file.close()
print(a)
Mast
  • 1,788
  • 4
  • 29
  • 46
  • 1
    Welcome to Stack Overflow! Please note you are answering a very old and already answered question. Here is a guide on [How to Answer](http://stackoverflow.com/help/how-to-answer). – help-info.de Aug 11 '20 at 07:09
  • @ajay-jaiswal Please specify your question and provide a minimal reproducible example and any error messages you're getting. You posted the code but not the actual question. – dmitryro Nov 28 '20 at 19:41
-2

This is the easiest way to do this.

f = open("file.txt", "wt")
for line in f:
    f.write(line.replace('foo', 'bar'))
f.close()

I hope it will work for you.

Rohit Nishad
  • 2,570
  • 2
  • 22
  • 32