Prox
Prox

Reputation: 759

How do I remove a particular line from a file but keep other lines intact?

I want to learn Python so I started writing my first program which is a phone book directory. It has the options to add a name and phone number, remove numbers, and search for them. Ive been stuck on the remove part for about 2 days now and just can't get it working correctly. I've been in the Python IRC and everything, but haven't been able to figure it out. Basically, my program stores the numbers to a list in a file. I cannot figure out how to remove a particular line in the file but keep the rest of the file intact. Can someone please help me with this?

Some people have advised that it will be easier to do if I create a temp file, remove the line, then copy the remaining lines from the original file over to the temp file. Then write over the original file over with the temp file. So I have been trying this...

if ui == 'remove':  
    coname = raw_input('What company do you want to remove? ') # company name
    f = open('codilist.txt', 'r') # original phone number listing        
    f1 = open('codilist.tmp', 'a') # open a tmp file

    for line in f:  
        if line.strip() != coname.strip():    
            for line in f: 
                f1.write(line)                             
            break         # WILL LATER OVERWRITE THE codilist.txt WITH THE TMP FILE
        else:
            f1.write(line)
    else:
        print 'Error: That company is not listed.'    
    f1.close()
    f.close()
    continue

Upvotes: 2

Views: 585

Answers (6)

Prox
Prox

Reputation: 759

I got this working...

if ui == 'remove':  
    coname = raw_input('What company do you want to remove? ') # company name
    f = open('codilist.txt')
    tmpfile = open('codilist.tmp', 'w')
    for line in f:
        if coname in line:
            print coname + ' has been removed.'
        else:
           tmpfile.write(line)
    f.close()
    tmpfile.close()
    os.rename('codilist.tmp', 'codilist.txt')
    continue

Upvotes: 0

Hugh Bothwell
Hugh Bothwell

Reputation: 56684

Here is a pretty extensively rewritten version:

  • all the phone data is wrapped into a Phonebook class; data is kept in memory (instead of being saved and reloaded for every call)

  • it uses the csv module to load and save data

  • individual actions are turned into short functions or methods (instead of One Big Block of Code)

  • commands are abstracted into a function-dispatch dictionary (instead of a cascade of if/then tests)

This should be much easier to understand and maintain.

import csv

def show_help():
    print('\n'.join([
        "Commands:",
        "    help                   shows this screen",
        "    load [file]            loads the phonebook (file name is optional)",
        "    save [file]            saves the phonebook (file name is optional)",
        "    add {name} {number}    adds an entry to the phonebook",
        "    remove {name}          removes an entry from the phonebook",
        "    search {name}          displays matching entries",
        "    list                   show all entries",
        "    quit                   exits the program"
    ]))

def getparam(val, prompt):
    if val is None:
        return raw_input(prompt).strip()
    else:
        return val

class Phonebook(object):
    def __init__(self, fname):
        self.fname = fname
        self.data = []
        self.load()

    def load(self, fname=None):
        if fname is None:
            fname = self.fname
        try:
            with open(fname, 'rb') as inf:
                self.data = list(csv.reader(inf))
                print("Phonebook loaded")
        except IOError:
            print("Couldn't open '{}'".format(fname))

    def save(self, fname=None):
        if fname is None:
            fname = self.fname
        with open(fname, 'wb') as outf:
            csv.writer(outf).writerows(self.data)
            print("Phonebook saved")

    def add(self, name=None, number=None):
        name   = getparam(name,   'Company name? ')
        number = getparam(number, 'Company number? ')
        self.data.append([name,number])
        print("Company added")

    def remove(self, name=None):
        name = getparam(name, 'Company name? ')
        before = len(self.data)
        self.data = [d for d in self.data if d[0] != name]
        after = len(self.data)
        print("Deleted {} entries".format(before-after))

    def search(self, name=None):
        name = getparam(name, 'Company name? ')
        found = 0
        for c,n in self.data:
            if c.startswith(name):
                found += 1
                print("{:<20} {:<15}".format(c,n))
        print("Found {} entries".format(found))

    def list(self):
        for c,n in self.data:
            print("{:<20} {:<15}".format(c,n))
        print("Listed {} entries".format(len(self.data)))

def main():
    pb = Phonebook('phonebook.csv')
    commands = {
        'help':   show_help,
        'load':   pb.load,
        'save':   pb.save,
        'add':    pb.add,
        'remove': pb.remove,
        'search': pb.search,
        'list':   pb.list
    }
    goodbyes = set(['quit','bye','exit'])

    while True:
        # get user input
        inp = raw_input("#> ").split()

        # if something was typed in
        if inp:
            # first word entered is the command; anything after that is a parameter
            cmd,args = inp[0],inp[1:]

            if cmd in goodbyes:
                # exit the program (can't be delegated to a function)
                print 'Goodbye.' 
                break
            elif cmd in commands:
                # "I know how to do this..."
                try:
                    # call the appropriate function, and pass any parameters
                    commands[cmd](*args)
                except TypeError:
                    print("Wrong number of arguments (type 'help' for commands)")
            else:
                print("I didn't understand that (type 'help' for commands)")

if __name__=="__main__":
    main()

Upvotes: 1

Ingrid
Ingrid

Reputation: 160

I assume your file contains something like <name><whitespace><number> on each line? If that's the case, you could use something like this for your if statement (error handling not included!):

name, num = line.strip().split()
if name != coname.strip():
    # write to file



Suggestion:

Unless there is some specific reason for you to use a custom format, the file format json is quite good for this kind of task. Also note the use of the 'with' statement in these examples, which saves you having to explicitly close the file.

To write the information:

import json

# Somehow build a dict of {coname: num,...}
info = {'companyA': '0123456789', 'companyB': '0987654321'}

with open('codilist.txt', 'w') as f:
    json.dump(info, f, indent=4) # Using indent for prettier files

To read/amend the file:

import json

with open('codilist.txt', 'r+') as f:

    info = json.load(f)

    # Remove coname
    if coname in info:
        info.pop(coname)
    else:
        print 'No record exists for ' + coname

    # Add 'companyC'
    info['companyC'] = '0112233445'

    # Write back to file
    json.dump(info, f, indent=4)

You'll need python2.6 or later for these examples. If you're on 2.5, you'll need these imports:

import simplejson as json
from __future__ import with_statement

Hope that helps!

Upvotes: 4

thebjorn
thebjorn

Reputation: 27351

You probably don't want to open the temp file in append ('a') mode:

f1 = open('codilist.tmp', 'a') # open a tmp file

also, be aware that

for line in f:
    ...
    f1.write(line)

will write everything to the file without newlines.

The basic structure you want is:

for line in myfile:
    if not <line-matches-company>:
        tmpfile.write(line + '\n')   # or print >>tmpfile, line

you'll have to implement <line-matches-company> (there isn't enough information in the question to know what it should be -- perhaps if you showed a couple of lines from your data file..?)

Upvotes: 0

trusko
trusko

Reputation: 5

Ned's answer looks like it should work. If you haven't tried this already, you can set python's interactive debugger above the line in question. Then you can print out the values of line.strip() and coname.strip() to verify you are comparing apples to apples.

for line in f:
    import pdb
    pdb.set_trace()
    if line.strip() != coname.strip():
        f1.write(line)

Here's a list of pdb commands.

Upvotes: 0

Ned Batchelder
Ned Batchelder

Reputation: 375854

Something simple like this will read all of f, and write out all the lines that don't match:

for line in f:
    if line.strip() != coname.strip():
        f1.write(line)

Upvotes: 0

Related Questions