Alejandro Rodriguez
Alejandro Rodriguez

Reputation: 127

how to create a loop to replace variable number of lines

So I'm trying to replace x no. of lines in a text file with a new version of that line. The new line is the same as the old but without the first character '#'. I can't just delete the first character as, of course, strings are immutable. I've created the script below but the changes do not take place. When I print the section in question it is unchanged. I've tried using string.replace() as well but that hasn't worked either. No errors appear so I don't know where I'm going wrong.

import os
import subprocess 

x = input('How many unique conformers were identified? ') 
with open('file.txt', 'r') as f: 
  f_contents = f.readlines() 
  for line in (f_contents[18:(18 + x)]):
    newline = str(line[1:(len(line))])
    if line[0] == "#":
      line = newline
print f_contents[18:(18 + x)] 

  with open('x.txt', 'w') as f: 
    f.writelines(f_contents)  

Upvotes: 1

Views: 413

Answers (2)

gboffi
gboffi

Reputation: 25023

Let me introduce the standard library module fileinput

import fileinput

x = input('How many unique conformers were identified? ')
# Python3 input() returns an unevaluated string
x = int(x) 

# fileinput objects can be used as context managers
# the inplace=True argument directs to edit the file in place...

with fileinput.input('file.txt', inplace=True) as f:

  for line in f:
      # decrement the line number to match the list indexing of OP
      ln = f.lineno() - 1
      # use "<=" and "<" to match slice addressing
      if 18 <= ln < (18+x):
          # if first character is "#" remove it
          if line[0]=='#': line = line[1:]
      print(line, end='')

Let me quote from the site I linked above:

Although the script uses print(), no output is produced because fileinput redirects standard output to the file being overwritten.

PS forgot to mention — a backup file is produced before any further processing...

Upvotes: 1

mhawke
mhawke

Reputation: 87084

line = newline

simply rebinds the name line to the value of newline. It does not modify the value of f_contents because the value is immutable.

You could try this instead to update the f_contents list:

offset = 18
x = input('How many unique conformers were identified? ')

with open('file.txt', 'r') as f: 
  f_contents = f.readlines()
  for i in range(offset, offset + x):
    if f_contents[i].startswith('#'):
        f_contents[i] = f_contents[i][1:]

print f_contents[offset:offset + x]

This slice assignment using a list comprehension will also work:

with open('file.txt', 'r') as f:
  f_contents = f.readlines()
  f_contents[offset:offset+x] = [line[1:] if line.startswith('#') else line
                                    for line in f_contents[offset:offset+x]]

If you are aiming to write the updated content back out to file then a better way is to iterate over the lines in the file, updating those within the required range of lines, and writing out to a new file.

offset = 18
x = input('How many unique conformers were identified? ')

with open('file.txt', 'r') as infile, open('outfile.txt', 'w') as outfile:
    for i, line in enumerate(infile):
        if offset <= i < offset+x and line.startswith('#'):
            line = line[1:]
        outfile.write(line)

Upvotes: 1

Related Questions