3

I would like to find a better solution to achieve the following three steps:

  1. read strings at a given row
  2. update strings
  3. write the updated strings back

Below are my code which works but I am wondering is there any better (simple) solutions?

new='99999'

f=open('C:/Users/th/Dropbox/com/MS1Ctt-P-temp.INP','r+')
lines=f.readlines()
#the row number we want to update is given, so just load the content
x = lines[95]
print(x)
f.close()


#replace
f1=open('C:/Users/th/Dropbox/com/MS1Ctt-P-temp.INP')
con = f1.read()
print con
con1 = con.replace(x[2:8],new) #only certain columns in this row needs to be updated
print con1
f1.close()


#write
f2 = open('C:/Users/th/Dropbox/com/MS1Ctt-P-temp.INP', 'w')
f2.write(con1)
f2.close()

Thanks! UPDATE: get an idea from jtmoulia this time it becomes easier

def replace_line(file_name, line_num, col_s, col_e, text):
    lines = open(file_name, 'r').readlines()
    temp=lines[line_num]
    temp = temp.replace(temp[col_s:col_e],text)
    lines[line_num]=temp
    out = open(file_name, 'w')
    out.writelines(lines)
    out.close()
TTT
  • 4,354
  • 13
  • 73
  • 123

4 Answers4

3

The problem with textual data, even when tabulated, is that the byte offsets are not predictable. For example, when representing numbers with strings you have one byte per digit, whereas when using binary (e.g. two's complement) you always need four or eight bytes either for small and large integers.

Nevertheless, if your text format is strict enough you can get along by replacing bytes without changing the size of the file, you can try using the standard mmap module. With it, you'll be able to treat a file as a mutable byte string and modify parts of it inplace and letting the kernel do the file saving for you.

Otherwise, whatever of the other answers are much better suited for the problem.

C2H5OH
  • 5,452
  • 2
  • 27
  • 39
  • nice point. But some of my replacements will change the lengths of the string like change 0111 to 01110. Does that mean mmap does not work for me, right? – TTT May 23 '12 at 04:49
  • Correct, it won't work. Resizing a file using `mmap` is quite more complicated than by reading/writing: you need to know the new file size in advance, truncate the file to that size, before mapping the file, and shift the content manually, depending on where you perform modifications. – C2H5OH May 23 '12 at 08:42
1

Well, to begin with you don't need to keep reopening and reading from the file every time. The r+ mode allows you to read and write to the given file.

Perhaps something like

with open('C:/Users/th/Dropbox/com/MS1Ctt-P-temp.INP', 'r+') as f:
    lines = f.readlines()
    #... Perform whatever replacement you'd like on lines
    f.seek(0)
    f.writelines(lines)

Also, Editing specific line in text file in python

Community
  • 1
  • 1
jtmoulia
  • 660
  • 8
  • 19
0

When I had to do something similar (for a Webmin customization), I did it entirely in PERL because that's what the Webmin framework used, and I found it quite easy. I assume (but don't know for sure) there are equivalent things in Python. First read the entire file into memory all at once (the PERL way to do this is probably called "slurp"). (This idea of holding the entire file in memory rather than just one line used to make little sense {or even be impossible}. But these days RAM is so large it's the only way to go.) Then use the split operator to divide the file into lines and put each line in a different element of a giant array. You can then use the desired line number as an index into the array (remember array indices usually start with 0). Finally, use "regular expression" processing to change the text of the line. Then change another line, and another, and another (or make another change to the same line). When you're all done, use join to put all the lines in the array back together into one giant string. Then write the whole modified file out.

While I don't have the complete code handy, here's an approximate fragment of some of the PERL code so you can see what I mean:

our @filelines = ();
our $lineno = 43;
our $oldstring = 'foobar';
our $newstring = 'fee fie fo fum';
$filelines[$lineno-1] =~ s/$oldstring/$newstring/ig; 
# "ig" modifiers for case-insensitivity and possible multiple occurences in the line
# use different modifiers at the end of the s/// construct as needed
Chuck Kollars
  • 2,135
  • 20
  • 18
  • Reading the entire file in is the only way to go, still doesn't make sense, size of the files we can deal with increased because f the amount of memory avalable or vice versa. For files say in the gigabyte range slurp will become choke. – Tony Hopkinson May 23 '12 at 00:00
  • You may be right. In my experience any text file is something that can be printed out and taken to the pizza parlor to look over. Such files typically range from a few K to a few hundred K. I've never had any experience with anything remotely like a gigabyte text file. (Even so, I suspect that on a system with 4GB RAM, using an array would make processing a 1GB text file so much easier it might be worth it. Old habits die hard. We've been reading files one line at a time for well over 40 years now, and the good reasons for doing so are getting pretty thin:-) – Chuck Kollars May 23 '12 at 01:46
  • Thanks for the comment. I think `linecache.getline` can read text by line. But I just not sure how to save the replaced to the proper position. – TTT May 23 '12 at 05:03
  • Sorry, I know the language constructs for Perl but not for Python. What worked well for me may not work so well for you; it might be better to just ignore my response. I was talking about having the entire file in a giant array so you could touch any line you wished without re-reading it - the idea of "reading text by line" indicates you're still thinking about something quite different. In Perl, the s/// construct modifies the original in place; the idea of "replacing the modified text in the proper position" never even comes up. – Chuck Kollars May 23 '12 at 16:57
-1
FILENAME = 'C:/Users/th/Dropbox/com/MS1Ctt-P-temp.INP'
lines = list(open(FILENAME))
lines[95][2:8] = '99999'
open(FILENAME, 'w').write(''.join(lines))
guidoism
  • 7,820
  • 8
  • 41
  • 59