Class 12Computer Science · Programming with PythonFull chapter

Text Files

The whole chapter in one place — read it, then test yourself. Clear notes, a reference sheet, a practice quiz, and worked NCERT solutions & PYQs.

Files, File Types and Paths

Quick answer A file is storage that outlives your program; text files hold encoded characters, binary files hold raw machine bytes, CSV is a text file with a delimiter, and every path handed to open() is read either from the drive root (absolute) or from the current working folder (relative).

Every variable you have used so far lives in RAM. Close the program and it is gone. A school fee counter that forgets every receipt at shutdown is not software, it is a calculator. A file is a named area of storage on disk that survives after your program ends.

Python never touches the disk directly. You call open() and get back a file object (a file handle). Attached to it is a file pointer that remembers how far into the file you have gone. Every read and write moves that pointer. Most confusing output in this chapter is not a wrong function, it is a pointer sitting somewhere you did not expect.

The three file types the syllabus names

PointText fileBinary fileCSV file
What is storedCharacters, turned into bytes by an encoding (UTF-8, cp1252)The raw internal form of the data, byte for byteCharacters, exactly like a text file
Readable in NotepadYesNo, you see garbageYes
Line conceptYes, lines end with \nNone, just a stream of bytesYes, one record per line
On readingEverything is str, numbers need int()Data returns in its original typeEverything is str
Extension.txt .py .html.dat .jpg .mp3 .exe.csv
Opened withopen(f, "r")open(f, "rb")open(f, "r") plus the csv module

Worked example: a text file stores characters, not numbers

Write 25000 into a text file, then peek at the bytes actually on disk by reopening in binary.

import os

f = open("fees.txt", "w")
f.write("25000")
f.close()

print("size on disk :", os.path.getsize("fees.txt"), "bytes")

f = open("fees.txt", "rb")
raw = f.read()
f.close()
print("raw bytes    :", raw)
print("byte values  :", list(raw))

f = open("fees.txt", "r")
data = f.read()
f.close()
print("read back    :", repr(data), type(data))
print("data + '500' :", data + "500")
print("int(data)+500:", int(data) + 500)

Real output:

size on disk : 5 bytes
raw bytes    : b'25000'
byte values  : [50, 53, 48, 48, 48]
read back    : '25000' 
data + '500' : 25000500
int(data)+500: 25500

The byte values 50, 53, 48, 48, 48 are the ASCII codes of '2', '5', '0', '0', '0'. The number 25000 was never stored, only its printed form. That is why it returns as str and why data + "500" glues text instead of adding. Anything numeric from a text file must go through int() or float() first. Avoiding that constant encode-decode round trip is the whole reason binary files exist.

Worked example: CSV is a text file wearing a uniform

f = open("students.csv", "w")
f.write("rollno,name,marks\n")
f.write("1,Aarav,87\n")
f.write("2,Diya,91\n")
f.close()

f = open("students.csv", "r")
print(f.read())
f.close()

Real output:

rollno,name,marks
1,Aarav,87
2,Diya,91

Nothing special happened. read() returned ordinary text. The commas become columns only because some other program agrees to treat them that way, whether Excel or Python's csv module.

Absolute and relative paths

An absolute path starts at the root of the drive and is complete on its own, like C:\school\marks.txt or /home/aarav/marks.txt. A relative path is measured from the current working directory, the folder your program is running from, which is often not the folder your .py file sits in. That mismatch is why FileNotFoundError surprises people. Inside a relative path . means this folder and .. means one folder up. This ran from a folder called class12:

import os

print("Working folder :", os.getcwd())
print("marks.txt   -> abs:", os.path.abspath("marks.txt"))
print("marks.txt   -> isabs:", os.path.isabs("marks.txt"))
print("data/fees.txt -> abs:", os.path.abspath("data/fees.txt"))
print("../notes.txt  -> abs:", os.path.abspath("../notes.txt"))

p = "X:\\data\\fees.txt"
print("isabs    :", os.path.isabs(p))
print("dirname  :", os.path.dirname(p))
print("basename :", os.path.basename(p))
print("join     :", os.path.join("data", "fees.txt"))

Real output:

Working folder : X:\class12
marks.txt   -> abs: X:\class12\marks.txt
marks.txt   -> isabs: False
data/fees.txt -> abs: X:\class12\data\fees.txt
../notes.txt  -> abs: X:\notes.txt
isabs    : True
dirname  : X:\data
basename : fees.txt
join     : data\fees.txt

Read the third line carefully. "marks.txt" is relative, so isabs is False, and Python silently expanded it to X:\class12\marks.txt. Run the same script from another folder and those same nine characters point at a different file. Note also that abspath never checks whether the file exists; it is pure string arithmetic on the working folder.

The backslash trap, which costs marks every year

In a Python string \n is a newline and \t is a tab, and a Windows path is full of backslashes.

p1 = "C:\school\notes.txt"
print("careless repr :", repr(p1))
print(p1)

print("doubled  repr :", repr("C:\\school\\notes.txt"))
print("raw      repr :", repr(r"C:\school\notes.txt"))
print("forward  repr :", repr("C:/school/notes.txt"))

Real output, warning and all:

s1_backslash.py:1: SyntaxWarning: invalid escape sequence '\s'
  p1 = "C:\school\notes.txt"
careless repr : 'C:\\school\notes.txt'
C:\school
otes.txt
doubled  repr : 'C:\\school\\notes.txt'
raw      repr : 'C:\\school\\notes.txt'
forward  repr : 'C:/school/notes.txt'

The \n in \notes.txt became a real newline, which is why the printed path broke across two lines. The three safe fixes are doubling the backslashes, prefixing the string with r, or using forward slashes, which Windows accepts perfectly well.

open() f = open(path, mode) Returns a file object. Defaults to open(path, 'r') in text mode. Raises FileNotFoundError for 'r' and 'r+' when the path does not exist.
Current working folder os.getcwd() The folder every relative path is measured against. Not necessarily where the .py file is saved.
Relative to absolute os.path.abspath('data/fees.txt') Joins the relative path onto the working folder and returns a full path string. Does not require the file to exist.
Is the path absolute? os.path.isabs(path) True only when the path starts at a root. 'marks.txt' and '../marks.txt' both give False.
Build a path safely os.path.join('data', 'fees.txt') Inserts the separator the OS uses. On Windows this returned 'data\fees.txt'.
Size on disk os.path.getsize(path) Bytes, not characters. On Windows each newline is stored as \r\n and counts as 2.
Remember
  • A file object plus its file pointer is the whole model: open() gives the object, every read and write moves the pointer, close() releases it.
  • A text file stores encoded characters, so 25000 occupies 5 bytes as '2','5','0','0','0' and returns as a str that must go through int() before arithmetic.
  • A binary file stores the raw internal representation, has no line structure and is not human readable; a CSV file is only a text file with an agreed delimiter.
  • An absolute path starts at the drive root and works from anywhere; a relative path is resolved against os.getcwd(), which need not be where your .py file sits.
  • In Windows paths always double the backslashes, use a raw string, or use forward slashes, because \n and \t inside a normal string are escape sequences.

Open Modes, close() and the with Clause

Quick answer The mode string decides three things at once, whether a missing file is created or an error, where the pointer starts, and whether old data survives; 'w' wipes the file the instant it opens, and the with clause is the only close() you can trust.

The full form is f = open(filename, mode). Leave the mode out and you get "r", text mode, read only. The mode is not a suggestion, it settles three separate things before a single byte moves:

  1. What happens if the file does not exist: created quietly, or FileNotFoundError.
  2. Where the pointer sits at the moment of opening: byte 0, or the end of the file.
  3. Whether the data already in the file survives.

The mode table, verified by experiment

ModeIf file missingPointer at openReadWriteExisting contents
rFileNotFoundError0YesNoKept
r+FileNotFoundError0YesYesKept, overwritten from the pointer
wCreated0NoYesErased at open
w+Created0YesYesErased at open
aCreatedEnd of fileNoYesKept, writes always land at the end
a+CreatedEnd of fileYesYesKept, writes always land at the end

Worked example 1: 'w' destroys the file before you write anything

Projects lose data because students believe the file is emptied by the first write(). It is emptied by open() itself. Here the size is checked while the handle is open and nothing has been written.

import os

f = open("notes.txt", "w")
f.write("Chapter 4 - Text Files\nStudy today\n")
f.close()
print("after step 1, size =", os.path.getsize("notes.txt"), "bytes")

f = open("notes.txt", "w")
print("file opened in 'w', nothing written yet")
print("size right now =", os.path.getsize("notes.txt"), "bytes")
f.close()

f = open("notes.txt", "r")
print("contents now =", repr(f.read()))
f.close()
print("after step 3, size =", os.path.getsize("notes.txt"), "bytes")

Real output:

after step 1, size = 37 bytes
file opened in 'w', nothing written yet
size right now = 0 bytes
contents now = ''
after step 3, size = 0 bytes

37 bytes became 0 with no write() anywhere between them, and there is no undo. If your intention is to add records the mode is "a", never "w".

Worked example 2: every mode on the same 5-character file

Each test rebuilt m.txt containing ABCDE first, using these two helpers, so the results compare directly.

def fresh():
    f = open("m.txt", "w")
    f.write("ABCDE")
    f.close()

def show(tag):
    f = open("m.txt", "r")
    print(tag, "->", repr(f.read()))
    f.close()

The eight tests and their real output:

try:
    f = open("ghost.txt", "r")
except FileNotFoundError as e:
    print("1. open('ghost.txt','r') :", type(e).__name__, "-", e.strerror)

fresh(); f = open("m.txt", "r")
try:
    f.write("XY")
except Exception as e:
    print("2. write in 'r' mode     :", type(e).__name__, "-", e)
f.close()

fresh(); f = open("m.txt", "r+")
print("3. r+ tell at open       :", f.tell())
f.write("xy"); f.close(); show("3. r+ after write('xy')  ")

fresh(); f = open("m.txt", "w")
print("4. w tell at open        :", f.tell(), " size =", os.path.getsize("m.txt"))
f.write("xy"); f.close(); show("4. w after write('xy')   ")

fresh(); f = open("m.txt", "w+")
print("5. w+ size at open       :", os.path.getsize("m.txt"))
f.write("xy"); f.seek(0)
print("5. w+ read back          :", repr(f.read())); f.close()

fresh(); f = open("m.txt", "a")
print("6. a tell at open        :", f.tell())
try:
    f.read()
except Exception as e:
    print("6. read in 'a' mode      :", type(e).__name__, "-", e)
f.write("xy"); f.close(); show("6. a after write('xy')   ")

fresh(); f = open("m.txt", "a+")
print("7. a+ tell at open       :", f.tell())
print("7. a+ read straightaway  :", repr(f.read()))
f.seek(0)
print("7. a+ read after seek(0) :", repr(f.read()))
f.write("xy"); f.close(); show("7. a+ after write('xy')  ")

fresh(); f = open("m.txt", "a")
f.seek(0)
print("8. a tell after seek(0)  :", f.tell())
f.write("Z"); f.close(); show("8. a wrote at END anyway ")

Real output:

1. open('ghost.txt','r') : FileNotFoundError - No such file or directory
2. write in 'r' mode     : UnsupportedOperation - not writable
3. r+ tell at open       : 0
3. r+ after write('xy')   -> 'xyCDE'
4. w tell at open        : 0  size = 0
4. w after write('xy')    -> 'xy'
5. w+ size at open       : 0
5. w+ read back          : 'xy'
6. a tell at open        : 5
6. read in 'a' mode      : UnsupportedOperation - not readable
6. a after write('xy')    -> 'ABCDExy'
7. a+ tell at open       : 5
7. a+ read straightaway  : ''
7. a+ read after seek(0) : 'ABCDE'
7. a+ after write('xy')   -> 'ABCDExy'
8. a tell after seek(0)  : 0
8. a wrote at END anyway  -> 'ABCDEZ'

Four results deserve a highlight. Line 3 shows r+ overwrites character for character, giving 'xyCDE' and not 'xyABCDE'; a text file has no insert operation. Line 7 shows why a+ traps beginners: the file is full of data, yet reading straight after opening returns '', because the pointer is parked at the end, so you must seek(0) first. Line 8 is the big one. In append mode seek(0) genuinely moves the pointer and tell() honestly reports 0, but the write still lands at the end. Append mode forces every write to the end regardless of the pointer.

Worked example 3: close(), buffering, and why with wins

close() is not politeness. Your data sits in a memory buffer and only close() or flush() pushes it to disk.

import os

f = open("buf.txt", "w")
f.write("Fees paid: 25000")
print("written, not closed. size on disk =", os.path.getsize("buf.txt"))
g = open("buf.txt", "r")
print("another handle reads          :", repr(g.read()))
g.close()
f.close()
print("after close(), size on disk   =", os.path.getsize("buf.txt"))

with open("buf.txt", "r") as f:
    print("inside with, f.closed =", f.closed)
    print("inside with, read     =", repr(f.read()))
print("after with,  f.closed =", f.closed)

try:
    with open("buf.txt", "r") as f:
        data = f.read()
        print(10 / 0)
except ZeroDivisionError:
    print("crashed inside with; f.closed =", f.closed)

Real output:

written, not closed. size on disk = 0
another handle reads          : ''
after close(), size on disk   = 16
inside with, f.closed = False
inside with, read     = 'Fees paid: 25000'
after with,  f.closed = True
crashed inside with; f.closed = True

The text was written and the file was still 0 bytes. Now picture a plain open() and close() pair with a crash in between: the close() line is skipped, the buffer is never flushed, the record is lost. The with clause closes the file on the way out whether the block finishes normally or blows up, which is exactly what the last line proves. Use with by default; the board accepts it everywhere.

One small extra: the mode string is validated. open("m.txt", "rw") does not mean read plus write, it raises ValueError: must have exactly one of create/read/write/append mode. Read and write is spelled with a +.

Read only f = open('a.txt', 'r') Pointer at 0. f.write() raises io.UnsupportedOperation: not writable.
Read and write, keep data f = open('a.txt', 'r+') Pointer at 0, writes overwrite existing characters. File must already exist or FileNotFoundError.
Write, wipe data f = open('a.txt', 'w') Truncates the file to 0 bytes the instant it opens, before any write(). Creates the file if missing.
Append f = open('a.txt', 'a') Pointer starts at end of file. Every write lands at the end even after f.seek(0). Reading raises UnsupportedOperation: not readable.
Auto-close with open('a.txt', 'r') as f: Runs f.close() on exit, including when the block raises. f.closed is True afterwards.
Handle attributes f.name, f.mode, f.closed str, str, bool. Reading after close raises ValueError: I/O operation on closed file. f.flush() forces the buffer to disk without closing.
Remember
  • 'w' and 'w+' truncate the file to zero bytes at the moment of open(), before any write() runs: 37 bytes became 0 with nothing written.
  • 'r' and 'r+' are the only modes that raise FileNotFoundError; w, w+, a and a+ all create a missing file.
  • 'a' and 'a+' start with the pointer at end of file, and every write lands at the end even after seek(0) reports the pointer at 0.
  • r+ overwrites existing characters in place ('ABCDE' became 'xyCDE'), it never inserts; a+ returns '' if you read before seeking, because the pointer starts at EOF.
  • Data sits in a buffer until close() or flush(); the with clause guarantees the close even when the block raises.

Writing and Appending Data

Quick answer write() takes one string, returns the count of characters written and adds no newline; writelines() takes a sequence of strings, returns None and adds no separator either, so every line break in a text file is one you typed yourself.

There are exactly two ways to put data into a text file, and both refuse to be helpful. Neither adds a newline. Neither accepts a number. Accept that and the rest is easy.

write() writes one string and returns a count

with open("w1.txt", "w") as f:
    n1 = f.write("Aarav")
    n2 = f.write("Diya")
    n3 = f.write("Kabir")
print("write() returned:", n1, n2, n3)

with open("w1.txt", "r") as f:
    print("file contains   :", repr(f.read()))

with open("w2.txt", "w") as f:
    f.write("Aarav\n")
    f.write("Diya\n")
    f.write("Kabir\n")

with open("w2.txt", "r") as f:
    print("with newline    :", repr(f.read()))

with open("w3.txt", "w") as f:
    try:
        f.write(87)
    except TypeError as e:
        print("f.write(87)     :", type(e).__name__, "-", e)
    f.write(str(87))

with open("w3.txt", "r") as f:
    print("after str(87)   :", repr(f.read()))

Real output:

write() returned: 5 4 5
file contains   : 'AaravDiyaKabir'
with newline    : 'Aarav\nDiya\nKabir\n'
f.write(87)     : TypeError - write() argument must be str, not int
after str(87)   : '87'

Three separate write() calls produced one unbroken line, 'AaravDiyaKabir'. Nothing separates them because nothing was asked to. The return values 5, 4 and 5 are the character counts, a favourite one-mark question. And f.write(87) is a TypeError, not a silent conversion, so marks and roll numbers must be wrapped in str() before they go near a text file.

writelines() is just write() in a loop

names = ["Aarav", "Diya", "Kabir"]

with open("wl1.txt", "w") as f:
    r = f.writelines(names)
print("writelines returned:", r)

with open("wl1.txt", "r") as f:
    print("wl1.txt            :", repr(f.read()))

with open("wl2.txt", "w") as f:
    f.writelines(["Aarav\n", "Diya\n", "Kabir\n"])

with open("wl2.txt", "r") as f:
    print("wl2.txt            :", repr(f.read()))

marks = [87, 91, 78]
with open("wl3.txt", "w") as f:
    try:
        f.writelines(marks)
    except TypeError as e:
        print("writelines([87,..]):", type(e).__name__, "-", e)
    f.writelines([str(m) + "\n" for m in marks])

with open("wl3.txt", "r") as f:
    print("wl3.txt            :", repr(f.read()))

Real output:

writelines returned: None
wl1.txt            : 'AaravDiyaKabir'
wl2.txt            : 'Aarav\nDiya\nKabir\n'
writelines([87,..]): TypeError - write() argument must be str, not int
wl3.txt            : '87\n91\n78\n'

The name is a trap. writelines does not write lines, it writes the items of a sequence one after another with nothing in between, which is why wl1.txt is identical to what three write() calls produced. It also returns None, unlike write() which returns a count. Compare those two return values in the outputs above, because examiners do.

Worked example: a marks register that does not eat itself

This is the shape of almost every board program on this topic. Note the mode: "a" creates the file the first time and preserves it every time after.

def add_students(records):
    with open("register.txt", "a") as f:
        for roll, name, marks in records:
            f.write(str(roll) + "," + name + "," + str(marks) + "\n")

def show():
    with open("register.txt", "r") as f:
        print(f.read(), end="")
    print("-" * 24)

add_students([(1, "Aarav", 87), (2, "Diya", 91)])
print("after first batch:")
show()

add_students([(3, "Kabir", 78)])
print("after second batch:")
show()

with open("register.txt", "w") as f:
    f.write("4,Ishita,95\n")
print("after opening in 'w':")
show()

Real output:

after first batch:
1,Aarav,87
2,Diya,91
------------------------
after second batch:
1,Aarav,87
2,Diya,91
3,Kabir,78
------------------------
after opening in 'w':
4,Ishita,95
------------------------

The first two batches stack up as intended. The last block is the same program with one character changed, "a" to "w", and three students are gone. In a real school ERP that single character is the difference between a working attendance module and a support call.

A neater alternative to the string concatenation above is ",".join(...). When you already have a list and want newlines only between the items, f.write("\n".join(names)) is the idiom; on ['Aarav', 'Diya', 'Kabir'] it produced 'Aarav\nDiya\nKabir' with no trailing newline, which changes what readlines() gives back later.

write() n = f.write('Aarav\n') Returns the count of characters written, 6 for this string. Adds nothing of its own.
write() needs a str f.write(str(87)) f.write(87) raises TypeError: write() argument must be str, not int.
writelines() f.writelines(['Aarav\n', 'Diya\n']) Returns None. Writes the items back to back with no separator inserted.
List of numbers f.writelines([str(m) + '\n' for m in marks]) Passing the raw numbers raises TypeError on the first item, since writelines just calls write().
Append safely with open('reg.txt', 'a') as f: Creates the file when missing, never erases existing records, and writes always land at the end.
One string from a list f.write('\n'.join(names)) Puts \n between items but not after the last one. Produced 'Aarav\nDiya\nKabir'.
Remember
  • write(s) returns the number of characters written; writelines(seq) returns None. That difference alone is a standard one-mark question.
  • Neither write() nor writelines() adds a newline. Every \n in a text file is one you typed.
  • Both refuse non-strings: f.write(87) raises TypeError: write() argument must be str, not int, so wrap numbers in str().
  • Use 'a' to add records: it creates the file if missing and never erases what is there, while 'w' destroys the file at open().
  • f.write('\n'.join(names)) puts a newline between items but not after the last one, which changes the result of a later readlines().

Reading: read, readline and readlines

Quick answer read() returns the whole remaining file as one string, read(n) at most n characters, readline() one line with its \n attached and readlines() a list of all lines each keeping its \n, and all of them return an empty result once the pointer reaches end of file.

Every reading function shares one rule: it reads from the pointer onward and it moves the pointer. Read the file once and a second read gives nothing unless you rewind. That single fact explains most "my program prints blank" questions. All examples below use this file:

with open("poem.txt", "w") as f:
    f.write("Sare jahan se achha\n")
    f.write("Hindustan hamara\n")
    f.write("Hum bulbulen hain iski\n")

Worked example: all four functions on the same file

with open("poem.txt", "r") as f:
    data = f.read()
print("type      :", type(data))
print("repr      :", repr(data))
print("len       :", len(data))

with open("poem.txt", "r") as f:
    print("read(5)   :", repr(f.read(5)))
    print("read(9)   :", repr(f.read(9)))
    print("read()    :", repr(f.read()))
    print("read() eof:", repr(f.read()))

with open("poem.txt", "r") as f:
    print("line1     :", repr(f.readline()))
    print("line2     :", repr(f.readline()))
    print("line3     :", repr(f.readline()))
    print("line4 eof :", repr(f.readline()))

with open("poem.txt", "r") as f:
    print("readline(4)  :", repr(f.readline(4)))
    print("readline(100):", repr(f.readline(100)))

with open("poem.txt", "r") as f:
    lines = f.readlines()
print("readlines :", lines)
print("count     :", len(lines))
print("last item :", repr(lines[-1]))

Real output:

type      : 
repr      : 'Sare jahan se achha\nHindustan hamara\nHum bulbulen hain iski\n'
len       : 60
read(5)   : 'Sare '
read(9)   : 'jahan se '
read()    : 'achha\nHindustan hamara\nHum bulbulen hain iski\n'
read() eof: ''
line1     : 'Sare jahan se achha\n'
line2     : 'Hindustan hamara\n'
line3     : 'Hum bulbulen hain iski\n'
line4 eof : ''
readline(4)  : 'Sare'
readline(100): ' jahan se achha\n'
readlines : ['Sare jahan se achha\n', 'Hindustan hamara\n', 'Hum bulbulen hain iski\n']
count     : 3
last item : 'Hum bulbulen hain iski\n'

Read that slowly, because five separate exam points sit in it.

  • read() returned a single str of 60 characters with the \n characters embedded. It is not a list.
  • The three read() calls in the second block each continued where the previous one stopped: characters 0 to 4, then 5 to 13, then everything left. The fourth returned '', which is how end of file announces itself. Reading at EOF does not raise an error.
  • readline() keeps the \n, so 'Sare jahan se achha\n' is 20 characters, not 19.
  • readline(4) stopped after four characters but the next readline(100) stopped at the newline instead of at 100. The argument is an upper limit, not a promise.
  • readlines() returned a list of exactly three strings, each still carrying its \n. Nobody strips it for you.

Worked example: looping, and the phantom blank line

with open("poem.txt", "r") as f:
    print("--- print(line) : blank lines appear ---")
    for line in f:
        print(line)

with open("poem.txt", "r") as f:
    print("--- print(line, end='') : correct ---")
    for line in f:
        print(line, end="")

with open("poem.txt", "r") as f:
    print("--- while readline() ---")
    line = f.readline()
    while line != "":
        print(len(line), repr(line))
        line = f.readline()

Real output:

--- print(line) : blank lines appear ---
Sare jahan se achha

Hindustan hamara

Hum bulbulen hain iski

--- print(line, end='') : correct ---
Sare jahan se achha
Hindustan hamara
Hum bulbulen hain iski
--- while readline() ---
20 'Sare jahan se achha\n'
17 'Hindustan hamara\n'
23 'Hum bulbulen hain iski\n'

The double spacing in the first block is not a bug in your file. The line already ends with \n and print adds a second one. Fix it with end="", or use line.strip(), which on these three lines returned 'Sare jahan se achha', 'Hindustan hamara' and 'Hum bulbulen hain iski' with the newline gone. In the last block note the stop condition: while line != "" is correct because readline() returns "\n" for a genuinely blank line inside the file and only ever returns "" at EOF.

Prefer for line in f: when you can. It reads one line at a time and never builds a list, so it survives a file bigger than your RAM, whereas readlines() loads everything at once.

Worked example: counting, the standard board program

with open("poem.txt", "r") as f:
    data = f.read()

words = data.split()

print("characters (with newline):", len(data))
print("characters (no space)   :", len(data.replace(" ", "").replace("\n", "")))
print("words                   :", len(words))
print("words list              :", words)

with open("poem.txt", "r") as f:
    print("lines via readlines()   :", len(f.readlines()))

with open("poem.txt", "r") as f:
    c = 0
    for line in f:
        if line[0] == "H":
            c += 1
print("lines starting with H   :", c)

h = 0
for w in words:
    if w[0] in "hH":
        h += 1
print("words starting with h   :", h)

Real output:

characters (with newline): 60
characters (no space)   : 50
words                   : 10
words list              : ['Sare', 'jahan', 'se', 'achha', 'Hindustan', 'hamara', 'Hum', 'bulbulen', 'hain', 'iski']
lines via readlines()   : 3
lines starting with H   : 2
words starting with h   : 4

Two habits to lock in. Use data.split() with no argument to count words: it splits on any run of whitespace including newlines and discards empty pieces. But do not count lines with len(data.split("\n")); because the file ends with \n that returns 4 here, not 3, since it produces a trailing empty string. len(f.readlines()) gives the honest 3.

Finally, reading is a one-way trip. Calling f.read() twice on one handle gave 'Aarav\nDiya\n' and then ''; only after f.seek(0) did the full text come back. If a program needs two passes, either rewind with seek(0) or store the data in a list on the first pass.

read() s = f.read() The whole remaining file as one string, \n included. Returns '' if the pointer is already at EOF.
read(n) s = f.read(5) At most n characters. Returns fewer near EOF and '' at EOF.
readline() s = f.readline() One line including its trailing \n. Returns '' only at EOF; a blank line inside the file comes back as '\n'.
readline(n) s = f.readline(4) Stops at n characters or at the \n, whichever comes first. Still moves the pointer.
readlines() L = f.readlines() List of every remaining line, each keeping its \n. Returns [] when the pointer is at EOF.
Line by line loop for line in f: Reads one line at a time without building a list, so it works on files larger than RAM. Pair with line.strip().
Remember
  • read() returns one str of the whole remaining file, readlines() returns a list, readline() returns one line. All three keep the \n characters.
  • At end of file read() and readline() return '' and readlines() returns []. None of them raises, so '' is the loop's stop condition.
  • read(n) and readline(n) are upper limits: readline(100) stopped at the newline after 16 characters.
  • print(line) inside a for loop double-spaces the output because the line already ends in \n. Use print(line, end='') or line.strip().
  • Count words with data.split(), but count lines with len(f.readlines()); split('\n') over-counts by one when the file ends with a newline.

seek(), tell() and Manipulating Data

Quick answer tell() reports the pointer position in bytes and seek() moves it there, which breaks the moment a rupee sign or a Devanagari letter appears; because a text file cannot insert or delete in place, every real update is read the lines, edit the list, rewrite the file.

tell() asks where the pointer currently is. seek(offset) moves it. Together they let you re-read part of a file or jump to a known position without reading everything before it.

Worked example: watching the pointer move

with open("st.txt", "w") as f:
    f.write("Aarav 87\nDiya 91\nKabir 78\n")

with open("st.txt", "r") as f:
    print("tell at open       :", f.tell())
    print("read(5)            :", repr(f.read(5)))
    print("tell after read(5) :", f.tell())
    print("readline()         :", repr(f.readline()))
    print("tell after readline:", f.tell())
    f.seek(0)
    print("tell after seek(0) :", f.tell())
    print("read(5) again      :", repr(f.read(5)))
    f.seek(9)
    print("after seek(9) read :", repr(f.read(4)))
    f.seek(0, 2)
    print("seek(0,2) tell     :", f.tell())
    print("read at end        :", repr(f.read()))

with open("st.txt", "r") as f:
    for args in [(3, 1), (-5, 2)]:
        try:
            f.seek(*args)
        except Exception as e:
            print("seek" + str(args), ":", type(e).__name__, "-", e)

Real output, on Windows:

tell at open       : 0
read(5)            : 'Aarav'
tell after read(5) : 5
readline()         : ' 87\n'
tell after readline: 10
tell after seek(0) : 0
read(5) again      : 'Aarav'
after seek(9) read : '\nDiy'
seek(0,2) tell     : 29
read at end        : ''
seek(3, 1) : UnsupportedOperation - can't do nonzero cur-relative seeks
seek(-5, 2) : UnsupportedOperation - can't do nonzero end-relative seeks

Two numbers there should look wrong. The string written was 26 characters, yet seek(0, 2) reported 29. And readline() returned the 4 characters ' 87\n' starting at position 5, yet tell() jumped to 10, not 9. Both have the same cause, and it is the most important idea in this section.

tell() counts bytes, never characters

Cause one is Windows line endings. In text mode on Windows every \n you write is stored as the two bytes \r\n and translated back on read. Your program sees one character, the disk holds two bytes.

import os

text = "Aarav 87\nDiya 91\nKabir 78\n"
print("characters in the string :", len(text))

with open("nl.txt", "w") as f:
    f.write(text)
print("bytes on disk (Windows)  :", os.path.getsize("nl.txt"))
with open("nl.txt", "rb") as f:
    print("raw bytes                :", f.read())

with open("nl2.txt", "w", newline="") as f:
    f.write(text)
print("bytes with newline=''    :", os.path.getsize("nl2.txt"))

Real output:

characters in the string : 26
bytes on disk (Windows)  : 29
raw bytes                : b'Aarav 87\r\nDiya 91\r\nKabir 78\r\n'
bytes with newline=''    : 26

Three newlines, three extra bytes, 29 instead of 26. On Linux the same program prints 26 both times. So never work out a tell() value by counting characters alone.

Cause two matters more for Indian data. UTF-8 uses one byte for plain English letters but three for the rupee sign and for Devanagari letters.

import os

line = "Fee ₹5000\n"
print("characters :", len(line))

with open("fee.txt", "w", encoding="utf-8", newline="") as f:
    f.write(line)
print("bytes      :", os.path.getsize("fee.txt"))
with open("fee.txt", "rb") as f:
    print("raw        :", f.read())

with open("fee.txt", "r", encoding="utf-8", newline="") as f:
    print("read(4)    :", repr(f.read(4)))
    print("tell()     :", f.tell())
    print("read(1)    :", repr(f.read(1)))
    print("tell()     :", f.tell())
    print("read()     :", repr(f.read()))
    print("tell()     :", f.tell())

name = "आरव\n"
print("chars in name :", len(name))
with open("hi.txt", "w", encoding="utf-8", newline="") as f:
    f.write(name)
print("bytes on disk :", os.path.getsize("hi.txt"))

Real output:

characters : 10
bytes      : 12
raw        : b'Fee \xe2\x82\xb95000\n'
read(4)    : 'Fee '
tell()     : 4
read(1)    : '₹'
tell()     : 7
read()     : '5000\n'
tell()     : 12
chars in name : 4
bytes on disk : 10

Look at the middle. f.read(1) read exactly one character and tell() moved from 4 to 7. One character, three bytes. The three Devanagari letters of आरव took 9 bytes plus 1 for the newline. So read(n) counts characters while tell() and seek() count bytes, and the two agree only while the file is plain ASCII.

The encoding="utf-8" in those calls is not decoration. On the Windows machine this ran on, open() defaults to cp1252, and writing the same rupee line without it produced UnicodeEncodeError: 'charmap' codec can't encode character '₹' in position 4: character maps to . Two details in that message are worth noticing: Python names the offending character by its Unicode escape rather than printing ₹, and position 4 is a zero-based index, so it is the fifth character of the string. Any file holding rupee amounts or Indian-language names should be opened with encoding="utf-8" explicitly.

One more rule from the first output. In text mode, seek() takes an optional second argument whence, where 0 means from the start, 1 from the current position and 2 from the end, but with whence 1 or 2 the offset must be 0, so only seek(0, 1) and seek(0, 2) are legal. seek(3, 1) and seek(-5, 2) both raised UnsupportedOperation. Free movement with whence 1 and 2 belongs to binary mode.

Why you cannot edit a text file in place

A text file is a flat run of bytes. No operation pushes the rest of the file aside to make room, so writing over a record with something of a different length damages whatever came next.

with open("ip.txt", "w", newline="") as f:
    f.write("1,Aarav,87\n2,Diya,91\n")
with open("ip.txt", "r+", newline="") as f:
    f.seek(8)
    f.write("100")
with open("ip.txt", "r", newline="") as f:
    print("after in-place write :", repr(f.read()))

with open("ip.txt", "w", newline="") as f:
    f.write("1,Aarav,87\n2,Diya,91\n")
with open("ip.txt", "r+", newline="") as f:
    f.seek(8)
    f.write("5")
with open("ip.txt", "r", newline="") as f:
    print("shorter value        :", repr(f.read()))

Real output:

after in-place write : '1,Aarav,1002,Diya,91\n'
shorter value        : '1,Aarav,57\n2,Diya,91\n'

Changing 87 to 100 needed one extra character, so it ate the newline and welded two records together. Changing 87 to 5 was shorter, so the leftover 7 stayed and Aarav now scores 57. In-place editing with r+ is safe only when the replacement has exactly the same length, which almost never happens with real data.

Worked example: the read-modify-write pattern

The correct approach has three steps every time. Read all lines into a list, change the list in memory, reopen in "w" and write the whole list back.

def show(tag):
    with open("marks.txt", "r") as f:
        print(tag)
        print(f.read(), end="")
        print("-" * 22)

with open("marks.txt", "w") as f:
    f.writelines(["1,Aarav,87\n", "2,Diya,91\n", "3,Kabir,78\n", "4,Ishita,95\n"])
show("original:")

with open("marks.txt", "r") as f:
    lines = f.readlines()
for i in range(len(lines)):
    parts = lines[i].strip().split(",")
    if parts[1] == "Diya":
        parts[2] = "96"
        lines[i] = ",".join(parts) + "\n"
with open("marks.txt", "w") as f:
    f.writelines(lines)
show("after update:")

with open("marks.txt", "r") as f:
    lines = f.readlines()
kept = [ln for ln in lines if ln.strip().split(",")[1] != "Kabir"]
with open("marks.txt", "w") as f:
    f.writelines(kept)
show("after delete:")

total = 0
count = 0
with open("marks.txt", "r") as f:
    for line in f:
        roll, name, marks = line.strip().split(",")
        total += int(marks)
        count += 1
        if int(marks) > 90:
            print("above 90 :", name, marks)
print("average  :", round(total / count, 2))

Real output:

original:
1,Aarav,87
2,Diya,91
3,Kabir,78
4,Ishita,95
----------------------
after update:
1,Aarav,87
2,Diya,96
3,Kabir,78
4,Ishita,95
----------------------
after delete:
1,Aarav,87
2,Diya,96
4,Ishita,95
----------------------
above 90 : Diya 96
above 90 : Ishita 95
average  : 92.67

Three details make this pattern reliable. strip() removes the trailing \n before splitting, otherwise the marks field would be '87\n' and int() would still work but comparisons on the last field would not. The "\n" is added back when the line is rebuilt, or the records would merge. And the whole list is rewritten with writelines() in "w" mode in one go, which is safe here precisely because the list already holds the complete new contents. Deleting a record is the same pattern with a filter instead of an edit; there is no delete-a-line function in Python.

For real projects, safer still is to write the new version to a temporary file, then use os.remove() and os.rename() to swap it in, so a crash halfway through cannot leave you with a half-written register.

tell() pos = f.tell() Current pointer position in BYTES from the start of the file, not in characters.
seek() f.seek(offset) Moves the pointer offset bytes from the start and returns the new position. f.seek(4) returned 4.
Jump to end f.seek(0, 2) whence 0 = start, 1 = current, 2 = end. On a 26-character file this reported 29 on Windows because of \r\n.
Illegal relative seek f.seek(3, 1) Raises io.UnsupportedOperation: can't do nonzero cur-relative seeks. Text mode allows only offset 0 with whence 1 or 2.
Force UTF-8 open('fee.txt', 'w', encoding='utf-8') Required for ₹ or Devanagari. Without it the Windows cp1252 default raises UnicodeEncodeError.
Read-modify-write with open(fn) as f: L = f.readlines() Edit the list L in memory, then reopen in 'w' and writelines(L). The only safe way to change a record whose length changes.
Remember
  • tell() and seek() work in BYTES from the start of the file, while read(n) counts characters. Reading one rupee sign moved tell() from 4 to 7.
  • On Windows every \n is stored as \r\n, so a 26-character string occupied 29 bytes and seek(0,2) reported 29.
  • In text mode only seek(0,1) and seek(0,2) are allowed; seek(3,1) raises UnsupportedOperation: can't do nonzero cur-relative seeks.
  • Open with encoding='utf-8' for rupee signs or Indian-language names, because the Windows cp1252 default raises UnicodeEncodeError.
  • A text file cannot insert or delete in place: overwriting 87 with 100 via r+ produced '1,Aarav,1002,Diya,91'. Always read the lines, edit the list, then rewrite the file with 'w'.

The formula sheet

Every formula in this chapter, in one place — screenshot it before your exam.

f = open(path, mode)
open()
os.getcwd()
Current working folder
os.path.abspath('data/fees.txt')
Relative to absolute
os.path.isabs(path)
Is the path absolute?
os.path.join('data', 'fees.txt')
Build a path safely
os.path.getsize(path)
Size on disk
f = open('a.txt', 'r')
Read only
f = open('a.txt', 'r+')
Read and write, keep data
f = open('a.txt', 'w')
Write, wipe data
f = open('a.txt', 'a')
Append
with open('a.txt', 'r') as f:
Auto-close
f.name, f.mode, f.closed
Handle attributes
n = f.write('Aarav\n')
write()
f.write(str(87))
write() needs a str
f.writelines(['Aarav\n', 'Diya\n'])
writelines()
f.writelines([str(m) + '\n' for m in marks])
List of numbers
with open('reg.txt', 'a') as f:
Append safely
f.write('\n'.join(names))
One string from a list
s = f.read()
read()
s = f.read(5)
read(n)
s = f.readline()
readline()
s = f.readline(4)
readline(n)
L = f.readlines()
readlines()
for line in f:
Line by line loop
pos = f.tell()
tell()
f.seek(offset)
seek()
f.seek(0, 2)
Jump to end
f.seek(3, 1)
Illegal relative seek
open('fee.txt', 'w', encoding='utf-8')
Force UTF-8
with open(fn) as f: L = f.readlines()
Read-modify-write

Test yourself

Tap an answer to check it instantly — you'll see why it's right, and what to revise if it isn't.

0 correct · 0/12 answered
Q1

Predict the output.f = open("a.txt", "w")f.writelines(["10", "20", "30"])f.close()f = open("a.txt", "r")print(len(f.readlines()))

Q2

A file b.txt holds the single line Aarav Diya Kabir with no newline anywhere in it. What does the last line print?f = open("b.txt", "r")f.read(3)f.readline(5)f.readline(2)print(f.tell())

Q3

Predict the final contents of the file.f = open("c.txt", "w")f.write("HELLO")f.close()f = open("c.txt", "a")f.seek(0)f.write("Hi")f.close()

Q4

A file d.txt contains ABCDEFGHIJ. What is printed?f = open("d.txt", "r+")f.seek(3)f.write("xyz")f.seek(0)print(f.read())

Q5

A file e.txt contains India is great\nPython is fun\n. What is printed?s = open("e.txt").read()print(len(s.split()), len(s.split("\n")))

Q6

A file f.txt was created with f.write("Delhi\nMumbai\nChennai") (note: no newline at the end). What is printed?L = open("f.txt").readlines()print(len(L))print(repr(L[2]))

Q7

A UTF-8 file g.txt contains Fee ₹500. What does the second tell() print?f = open("g.txt", "r", encoding="utf-8")print(f.read(4), f.tell())print(f.read(1), f.tell())

Q8

Which mode raises FileNotFoundError when the file does not exist?

Q9

What does f.write("Priodemy") return?

Q10

Which statement about writelines() is TRUE?

Q11

Which of these is an absolute path?

Q12

A with block raises a ZeroDivisionError after opening a file. What is f.closed afterwards?

NCERT solutions & previous-year questions

Step-by-step model answers — tap a question to reveal the full solution.

NCERT questions 6

1 Differentiate between a text file and a binary file.Types of files
BasisText fileBinary file
What is storedCharacters, converted to bytes by an encoding such as UTF-8The raw internal representation of the data, byte for byte
ReadableYes, in Notepad or any editorNo, appears as garbage
StructureOrganised into lines terminated by \nNo lines, just a continuous stream of bytes
TranslationEncoding on write, decoding on read; on Windows \n is also translated to \r\nNo translation of any kind
Type on readingAlways str, numbers need int() or float()Data comes back in its original type
Default modeopen(f, "r")open(f, "rb")
Examples.txt, .py, .csv, .html.dat, .jpg, .mp3, .exe

Demonstration. Writing the number 25000 into a text file:

import os
f = open("fees.txt", "w")
f.write("25000")
f.close()
print(os.path.getsize("fees.txt"))
f = open("fees.txt", "rb")
raw = f.read()
f.close()
print(raw, list(raw))
f = open("fees.txt", "r")
d = f.read()
f.close()
print(repr(d), d + "500", int(d) + 500)

Real output:

5
b'25000' [50, 53, 48, 48, 48]
'25000' 25000500 25500

The file holds five characters, not one number: the byte values 50, 53, 48, 48 and 48 are the ASCII codes of '2', '5', '0', '0' and '0'. That is why d + "500" concatenates to 25000500 while int(d) + 500 gives 25500. A binary file would have stored the value itself, so no conversion would be needed on reading.

2 What is the difference between read(), readline() and readlines()?Reading methods

All three read from the current position of the file pointer and move it forward. They differ in how much they take and what type they return.

MethodReturnsHow much it readsValue at EOF
read()a single strEverything from the pointer to the end''
read(n)a single strAt most n characters''
readline()a single strOne line, including its \n''
readlines()a list of strAll remaining lines, each keeping its \n[]
with open("para.txt", "w") as f:
    f.write("Python is easy\nFiles are useful\nPractice daily\n")

with open("para.txt", "r") as f:
    print("read()      :", repr(f.read()))
with open("para.txt", "r") as f:
    print("readline()  :", repr(f.readline()))
with open("para.txt", "r") as f:
    print("readlines() :", f.readlines())

Real output:

read()      : 'Python is easy\nFiles are useful\nPractice daily\n'
readline()  : 'Python is easy\n'
readlines() : ['Python is easy\n', 'Files are useful\n', 'Practice daily\n']

Note that a fresh handle was opened each time. Had one handle been reused, the second and third calls would have started from wherever the previous call left the pointer.

3 Write a program to read a text file and count the number of vowels, consonants, uppercase and lowercase characters in it.Counting characters
with open("N3.txt", "w") as f:
    f.write("India Is Great\nPython Rocks\n")

v = c = u = l = 0
with open("N3.txt", "r") as f:
    data = f.read()

for ch in data:
    if ch.isalpha():
        if ch in "aeiouAEIOU":
            v += 1
        else:
            c += 1
        if ch.isupper():
            u += 1
        else:
            l += 1

print("vowels     :", v)
print("consonants :", c)
print("uppercase  :", u)
print("lowercase  :", l)

Real output:

vowels     : 8
consonants : 15
uppercase  : 5
lowercase  : 18

Check. The file has 23 letters in total (India 5, Is 2, Great 5, Python 6, Rocks 5). Vowels 8 plus consonants 15 is 23, and uppercase 5 plus lowercase 18 is also 23, so the counts are consistent.

Why ch.isalpha() is essential. Without it the spaces and the \n characters would be counted as consonants and as lowercase, because they are neither vowels nor uppercase. This is the single most common mistake in this question.

4 Write a program that copies a text file source.txt onto target.txt barring all the lines that start with a '@' sign.Copying with a filter
with open("source.txt", "w") as f:
    f.write("Namaste India\n@ignore this line\nPython is fun\n@skip me too\nFile handling\n")

with open("source.txt", "r") as src, open("target.txt", "w") as tgt:
    for line in src:
        if not line.startswith("@"):
            tgt.write(line)

with open("target.txt", "r") as f:
    print(f.read(), end="")

Real output:

Namaste India
Python is fun
File handling

Points that earn the marks. The source is opened in "r" and the target in "w", and a single with statement can manage both handles at once. The loop writes line unchanged, not line.strip(), because the line still carries its own \n; stripping here would merge every record into one line. line.startswith("@") is equivalent to line[0] == "@", but startswith is safer because it does not raise IndexError on a completely empty line.

5 Write a program to count the total number of words in a text file and display the words having fewer than 4 characters.Word processing
with open("story.txt", "w") as f:
    f.write("Ravi went to the mela with his sister\n")
    f.write("He ate hot jalebi and won a toy\n")

with open("story.txt", "r") as f:
    words = f.read().split()

print("total words :", len(words))
short = [w for w in words if len(w) < 4]
print("short words :", short)
print("how many    :", len(short))

Real output:

total words : 16
short words : ['to', 'the', 'his', 'He', 'ate', 'hot', 'and', 'won', 'a', 'toy']
how many    : 10

Why split() with no argument. It splits on any run of whitespace, which includes the \n at the end of each line, and it discards empty pieces. Using split(" ") instead would leave the last word of line 1 as 'sister\n', whose length is 7 rather than 6, and any double space would produce an empty string that then crashes a w[0] test.

The same job with an explicit loop, which is what many board schemes expect:

count = 0
for w in words:
    if len(w) < 4:
        print(w, end=" ")
        count += 1
6 Write a program that reads a text file and creates another file that is identical except that every sequence of consecutive blank spaces is replaced by a single space.Manipulation of data
with open("messy.txt", "w") as f:
    f.write("Delhi    is    the    capital\nof     India\n")

with open("messy.txt", "r") as src, open("clean.txt", "w") as tgt:
    for line in src:
        tgt.write(" ".join(line.split()) + "\n")

with open("clean.txt", "r") as f:
    print(repr(f.read()))

Real output:

'Delhi is the capital\nof India\n'

How the one-line trick works. line.split() breaks the line at every run of whitespace and throws away the empty pieces, so "Delhi is" becomes ['Delhi', 'is']. " ".join(...) then rebuilds it with exactly one space between words. Because split() also consumed the trailing \n, it has to be added back explicitly, which is why + "\n" appears in the write.

If the question insists on a manual method without split(), the equivalent loop is:

out = ""
prev = ""
for ch in line:
    if ch == " " and prev == " ":
        continue
    out += ch
    prev = ch
tgt.write(out)

Previous-year board questions 4

Q1 Write a function COUNT_LINES() in Python, which should read each line of a text file NOTES.TXT and count those lines which start with either 'a' or 'A', and display the total count of such lines. (2 marks) CBSE board pattern, 2 marks

Sample file NOTES.TXT

Aarav topped the class
the result was good
All students passed
a small note here
Priodemy notes

Solution

def COUNT_LINES():
    f = open("NOTES.TXT", "r")
    count = 0
    for line in f.readlines():
        if line[0] == 'a' or line[0] == 'A':
            count += 1
    f.close()
    print("Number of lines starting with a or A:", count)

COUNT_LINES()

Real output:

Number of lines starting with a or A: 3

Examiner notes. The three qualifying lines are Aarav topped the class, All students passed and a small note here. Marks are usually split as 1 for correctly opening and looping over the file and 1 for the correct condition and count. Two common errors: testing line[0] == 'a' only, which misses the capital and loses half the marks, and using line.startswith('a') without also checking 'A'. A compact accepted alternative is if line[0] in 'aA':. Do not forget f.close() if you are not using a with block.

Q2 Write a function COUNT_TO() in Python to read the text file STORY.TXT and count the number of times the word 'to' occurs in the file. (2 marks) CBSE board pattern, 2 marks

Sample file STORY.TXT

I want to go to Agra tomorrow
She said to me that today is a holiday

Solution

def COUNT_TO():
    f = open("STORY.TXT", "r")
    words = f.read().split()
    count = 0
    for w in words:
        if w == "to":
            count += 1
    f.close()
    print("Count of 'to' as a word :", count)

COUNT_TO()

Real output:

Count of 'to' as a word : 3

The trap this question is built around. If you test if "to" in w instead of if w == "to", the words tomorrow and today also match. That version was run on the same file and printed 5 instead of 3. The question asks for the word 'to', so equality is required.

If the paper asks for a case-insensitive count, compare w.lower() == "to". If punctuation may be attached, as in to, or to., strip it first with w.strip(",.;:!?").lower().

Q3 Write a function count_Dwords() in Python to count and return the number of words ending with a digit in a text file named DETAILS.TXT. (3 marks) CBSE board pattern, 3 marks

Sample file DETAILS.TXT

Roll1 Aarav marks87 pass
Roll2 Diya marks91 pass
Section A room 12

Solution

def count_Dwords():
    f = open("DETAILS.TXT", "r")
    c = 0
    for w in f.read().split():
        if w[-1].isdigit():
            c += 1
    f.close()
    return c

print("Words ending with a digit :", count_Dwords())

Real output:

Words ending with a digit : 5

Working. The qualifying words are Roll1, marks87, Roll2, marks91 and 12. Note that 12 counts: the question says ending with a digit, not ending with a digit after a letter.

Examiner notes. Marks are typically 1 for opening and reading the file, 1 for splitting into words and iterating, 1 for the correct test and returning the count. Two points that decide the third mark. First, w[-1] is the last character; using w[0] answers a different question. Second, the question says return, not display, so the function must end with return c and the printing happens at the call. Using f.read().split() rather than a per-line split is important, because split() with no argument removes the trailing \n that would otherwise make the last word of each line end in a newline instead of a digit.

Q4 A text file PARA.TXT stores several lines of text. Write Python code to: (a) display the size of the file in bytes; (b) count and display how many times the words 'My' or 'me' appear; (c) write all words beginning with a vowel into another file VOWEL.TXT and display how many were written; (d) display the longest line in the file. (5 marks) CBSE board pattern, 5 marks

Sample file PARA.TXT

My school is in Delhi
I study Computer Science
My teacher gave me a project
an easy one on files

Solution

import os

# (a) size in bytes
print("(a) size in bytes         :", os.path.getsize("PARA.TXT"))

# (b) count of 'My' or 'me'
f = open("PARA.TXT", "r")
words = f.read().split()
f.close()
print("(b) 'My'/'me' count       :", sum(1 for w in words if w in ("My", "me")))

# (c) words starting with a vowel written to VOWEL.TXT
f = open("PARA.TXT", "r")
g = open("VOWEL.TXT", "w")
n = 0
for w in f.read().split():
    if w[0] in "AEIOUaeiou":
        g.write(w + "\n")
        n += 1
f.close()
g.close()
print("(c) vowel words written   :", n)

# (d) longest line
f = open("PARA.TXT", "r")
longest = ""
for line in f:
    if len(line.strip()) > len(longest):
        longest = line.strip()
f.close()
print("(d) longest line          :", repr(longest))

Real output:

(a) size in bytes         : 101
(b) 'My'/'me' count       : 3
(c) vowel words written   : 8
(d) longest line          : 'My teacher gave me a project'

The contents of VOWEL.TXT after part (c) were 'is\nin\nI\na\nan\neasy\none\non\n'.

The subtlety in part (a). The four lines contain 97 characters, but the answer is 101. This program ran on Windows, where each \n is stored on disk as the two bytes \r\n, adding 4 bytes for 4 lines. On Linux the same file is exactly 97 bytes. os.path.getsize() reports what is on the disk, so state the platform if the paper asks you to justify the number. An accepted alternative that avoids os is f.seek(0, 2) followed by f.tell(), which returned the same 101.

Part (b). The three matches are My school, My teacher and gave me. Comparison is case sensitive here because the question names 'My' and 'me' with those exact cases; if it had said 'my' in any case, use w.lower() == "my".

Part (d). line.strip() is applied before measuring, otherwise the trailing \n adds one to every length and the comparison between the last line and the others would be unfair.

Part of Priodemy for School

Interactive CBSE lessons, Class 8–12 — free with every school on Priodemy EduSuite. Explore more chapters and labs on the Priodemy for School hub.

Ask AI