10

I'm trying to delete a specific line that contains a specific string.

I've a file called numbers.txt with the following content:

peter
tom
tom1
yan

What I want to delete is that tom from the file, so I made this function:

def deleteLine():
fn = 'numbers.txt'
f = open(fn)
output = []
for line in f:
    if not "tom" in line:
        output.append(line)
f.close()
f = open(fn, 'w')
f.writelines(output)
f.close()

The output is:

peter
yan

As you can see, the problem is that the function delete tom and tom1, but I don't want to delete tom1. I want to delete just tom. This is the output that I want to have:

peter
tom1
yan

Any ideas to change the function to make this correctly?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Borja
  • 103
  • 1
  • 1
  • 4
  • @Jakob Bowyer: Because `line` comes with the the newline so you'd need `line == 'tom\n'` or `line.rstrip() == 'tom'`, at least. – nosklo May 10 '11 at 10:19
  • 1
    Please please please please search. This question has been asked a dozen times. http://stackoverflow.com/search?q=%5Bpython%5D+delete+line+from+file. Or. If you question is about matching a line in a file, please change the title to "string matching problem". Or "why doesn't `not "tom" in line` work?" Or something more focused and exact. – S.Lott May 10 '11 at 10:41
  • 1
    possible duplicate of [Fastest Way to Delete a Line from Large File in Python](http://stackoverflow.com/questions/2329417/fastest-way-to-delete-a-line-from-large-file-in-python) – S.Lott May 10 '11 at 10:41

5 Answers5

12

change the line:

    if not "tom" in line:

to:

    if "tom" != line.strip():
Matic
  • 484
  • 2
  • 7
  • You shouldn't need to strip a line iterated from a fileobj. – Jakob Bowyer May 10 '11 at 09:40
  • 2
    Python 2.5.4 on Mac doesn't strip new lines chars when iterating a fileobj, so if you want to compare to a string, you need to strip it. Also I've seen many bugs after a whitespace somehow managed to sneak at the end of line and the code didn't handle it. – Matic May 10 '11 at 10:02
  • How very strange, both win and linux seem to strip without complaint, thanks for teaching me something :) – Jakob Bowyer May 10 '11 at 10:03
  • 2
    @Jakob Bowyer: For completeness, that's not true. Lines are not stripped in linux or windows either. Any lines read from a file object in python come **with the newline**, regardless of platform – nosklo May 10 '11 at 10:18
4

That's because

if not "tom" in line

checks, whether tom is not a substring of the current line. But in tom1, tom is a substring. Thus, it is deleted.

You probably could want one of the following:

if not "tom\n"==line # checks for complete (un)identity
if "tom\n" != line # checks for complete (un)identity, classical way
if not "tom"==line.strip() # first removes surrounding whitespace from `line`
nosklo
  • 217,122
  • 57
  • 293
  • 297
phimuemue
  • 34,669
  • 9
  • 84
  • 115
  • You shouldn't need to strip a line when iterating directly over a fileobj, it should hand you stripped strings already. – Jakob Bowyer May 10 '11 at 09:20
  • @Jakob Bowyer: No, that's wrong. You **do** need to strip the line when iterating over a fileobj. It will yield lines **with the newline** so if you don't strip the strings won't compare equal. – nosklo May 10 '11 at 10:20
3

Just for fun, here's a two-liner to do it.

lines = filter(lambda x:x[0:-1]!="tom", open("names.txt", "r"))
open("names.txt", "w").write("".join(lines))

Challenge: someone post a one-liner for this.

You could also use the fileinput module to get arguably the most readable result:

import fileinput
for l in fileinput.input("names.txt", inplace=1):
    if l != "tom\n": print l[:-1]
Bemmu
  • 17,849
  • 16
  • 76
  • 93
  • 4
    ``open("names_out.txt", "w").write("\n".join(line for line in open("names.txt", "r") if line[:-1] != "tom"))``. I guess it won't work when the two filenames are identical, though. – Tamás May 10 '11 at 09:27
  • Whoops. Should be ``"".join`` instead of ``"\n".join`` above. And I guess the in-place transformation can also be achieved using the ``fileinput`` module. – Tamás May 10 '11 at 09:33
  • 1
    Something like `subprocess.Popen("sed -i '' -e'/^tom$/d' numbers.txt")` maybe? – Merlyn Morgan-Graham May 10 '11 at 09:34
1

You can use regex.

import re
if not re.match("^tom$", line):
    output.append(line)

The $ means the end of the string.

riza
  • 16,274
  • 7
  • 29
  • 29
  • If you go down that route you also need to match the start of the string i.e., ``re.match("^tom$", line)``. – Blair May 10 '11 at 10:23
  • @tzot, you were no less guilty of not fixing it than riza. – ArtOfWarfare Jul 03 '14 at 13:30
  • 1
    @ArtOfWarfare, you're guilty of mistaken presumptions. *I* never believed he should edit it, given he used `.match` and not `.search`; my statement was a simple “if-then” nudge in response to riza's commenting and not editing. – tzot Jul 04 '14 at 17:32
0

I'm new in programing and python (a few months)... this is my solution:

import fileinput

c = 0 # counter
for line in fileinput.input("korrer.csv", inplace=True, mode="rb"):
    # the line I want to delete
    if c == 3: 
        c += 1
        pass
    else:
        line = line.replace("\n", "")
        print line
        c +=1

I'm sure there is a simpler way, just it's an idea. (my English it's not very good looking!!)

koxmoz
  • 21
  • 3