Class 11Computer Science · Programming with PythonFull chapter

Strings

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

Strings, Indexing and Immutability

Quick answer A string is an ordered sequence of characters that you reach by position — forward with 0,1,2… and backward with -1,-2… — and which Python refuses to let you change in place.

A string is a sequence of characters written inside quotes. Python does not care which quotes you use, as long as the opening and closing quote match.

name = 'Aarav Sharma'
city = "Bengaluru"
pin = '''560001'''
print(name)
print(city)
print(pin)
print(type(name))

Output:

Aarav Sharma
Bengaluru
560001

Notice pin. It looks like a number, but it is inside quotes, so it is a string. This matters in real life: a PIN code '560001' and an account number '0050100234' must stay strings, because the leading zeros would vanish the moment you made them integers.

Every character has a position. Python gives each character two addresses — one counting from the left starting at 0, and one counting from the right starting at -1.

CharacterPRIODEMY
Forward index01234567
Backward index-8-7-6-5-4-3-2-1
s = "PRIODEMY"
print(s[0], s[3], s[7])
print(s[-1], s[-8])
print(len(s))

Output:

P O Y
Y P
8

The last valid forward index is always len(s) - 1, never len(s). Ask for one past the end and Python stops you:

s = "PRIODEMY"
print(s[8])

Output:

Traceback (most recent call last):
  File "s1c.py", line 2, in 
    print(s[8])
          ~^^^
IndexError: string index out of range

Why negative indexing exists. Without it you would write s[len(s)-1] every time you wanted the last character. s[-1] says the same thing in three characters and cannot go wrong when the length changes.

Strings are immutable. Once a string object is made, not one character in it can be changed. Try it and Python raises a TypeError:

s = "PRIODEMY"
s[0] = 'X'

Output:

Traceback (most recent call last):
  File "s1d.py", line 2, in 
    s[0] = 'X'
    ~^^^
TypeError: 'str' object does not support item assignment

The fix is not to edit the string but to build a new one and point the same variable name at it:

s = "PRIODEMY"
print(id(s))
s = "X" + s[1:]
print(s)
print(id(s))

Output (the id numbers are memory addresses and will be different every time you run it — only the fact that they differ from each other matters):

2655452667696
XRIODEMY
2655452706352

Two different ids prove the point: the old string was not edited, a brand new string was created and s was made to refer to it. Every string method you learn in this chapter works the same way — it hands back a new string and leaves the original untouched.

Escape sequences. Some characters cannot be typed directly, so they are written with a backslash. \n is a newline, \t is a tab, and \\ is one real backslash.

line = "Roll\tName\nA01\tMeera"
print(line)
print(len(line))
path = "C:\\school\\marks.txt"
print(path)
print(len(path))
print(len(""), len(" "), len("Namaste India"))

Output:

Roll	Name
A01	Meera
19
C:\school\marks.txt
19
0 1 13

Read the counts carefully. line is 19 characters even though it prints on two lines, because \n is one character, not two. Likewise \\ in the source is one backslash in the string. And len(" ") is 1 — a space is a character like any other.

Worked example — reading a roll number. A school roll number 11A0342 packs three facts into one string: class, section, serial number. Indexing and slicing pull them apart.

roll = "11A0342"
print("Class code :", roll[0:2])
print("Section    :", roll[2])
print("Serial     :", roll[-4:])
print("Last digit :", roll[-1])
print("Length     :", len(roll))

Output:

Class code : 11
Section    : A
Serial     : 0342
Last digit : 2
Length     : 7

Using roll[-4:] instead of roll[3:7] is deliberate — it keeps working if the class code becomes three characters long.

Creating a string s = 'text' | s = "text" | s = '''text''' All three produce the same str object. Triple quotes are the only ones that may span several lines.
Forward index s[i] where i runs from 0 to len(s)-1 s[0] is the first character. Anything from len(s) onwards raises IndexError: string index out of range.
Backward index s[-i] where i runs from 1 to len(s) s[-1] is the last character, s[-len(s)] is the first. Saves writing s[len(s)-1].
len() len(s) -> int A built-in FUNCTION, not a method — write len(s), never s.len(). len('') is 0, len(' ') is 1.
Immutability rule s[0] = 'X' -> TypeError Message: 'str' object does not support item assignment. Correct way: s = 'X' + s[1:]
Remember
  • A string is an ordered sequence of characters in matching single, double or triple quotes; the type is str.
  • Every character has a forward index (0 to len(s)-1) and a negative index (-1 to -len(s)); going past the end raises IndexError.
  • len(s) is a built-in function, not a method, and counts spaces and escape characters too — \n counts as one character.
  • Strings are immutable: s[0] = 'X' raises TypeError: 'str' object does not support item assignment. Build a new string with slicing and + instead.
  • Because strings are immutable, every string method returns a NEW string; the original variable is unchanged unless you reassign it.

String Operations: +, *, in and Slicing

Quick answer Four operations do most of the work on strings — join two with +, repeat one with *, test for a substring with in, and cut out any piece with s[start:stop:step].

Concatenation with +. The + operator glues two strings together end to end and returns a new string.

first = "Rohan"
last = "Verma"
print(first + " " + last)
print("-" * 30)
print("Ha" * 3)
marks = 87
print("Marks: " + str(marks))

Output:

Rohan Verma
------------------------------
HaHaHa
Marks: 87

Note the str(marks). Both sides of + must be strings. Drop the conversion and Python refuses, because + between a string and an int is genuinely ambiguous — should it add or join?

marks = 87
print("Marks: " + marks)

Output:

Traceback (most recent call last):
  File "s2b.py", line 2, in 
    print("Marks: " + marks)
          ~~~~~~~~~~^~~~~~~
TypeError: can only concatenate str (not "int") to str

Repetition with *. s * n repeats the string n times. One side must be a string and the other an integer. This is the standard way to draw a separator line in a report.

Membership with in and not in. These return True or False and work for whole substrings, not just single characters.

upi = "aarav@okaxis"
print('@' in upi)
print('okhdfc' in upi)
print('okhdfc' not in upi)
print("aarav" in upi)
print("AARAV" in upi)
print("" in upi)

Output:

True
False
True
True
False
True

Two things to remember. in is case sensitive — "AARAV" is not found even though "aarav" is. And the empty string is considered present in every string, so "" in upi is True.

Comparison. ==, < and > compare strings character by character using their Unicode codes, which is why all capitals sort before all small letters.

print("apple" == "Apple")
print("apple" < "banana")
print("Apple" < "apple")
print("Zebra" < "apple")
print("ab" < "abc")

Output:

False
True
True
True
True

"Zebra" < "apple" is True because 'Z' has code 90 and 'a' has code 97. This is not alphabetical order in the dictionary sense — it is code order.

Slicing. s[start:stop] returns the characters from start up to but not including stop. The stop is excluded so that s[0:4] and s[4:8] fit together with no overlap and no gap.

s = "COMPUTER"
print(s[0:4])
print(s[4:8])
print(s[:3])
print(s[3:])
print(s[-3:])
print(s[:-3])
print(s[2:100])
print(repr(s[5:2]))

Output:

COMP
UTER
COM
PUTER
TER
COMPU
MPUTER
''

Two behaviours that catch students out. Slicing never raises IndexError — s[2:100] simply stops at the end of the string. And if the start is after the stop, as in s[5:2], you get the empty string rather than an error or a reversed piece.

Slicing with a step. The third number says how far to jump each time. A negative step walks right to left.

s = "COMPUTER"
print(s[0:8:2])
print(s[1:8:2])
print(s[::3])
print(s[::-1])
print(s[::-2])
print(s[6:1:-1])

Output:

CMUE
OPTR
CPE
RETUPMOC
RTPO
ETUPM

s[::-1] is the shortest way to reverse a string in Python and appears in board papers constantly. When the step is negative the defaults flip: start defaults to the last character and stop defaults to just before the first.

Bulk concatenation with join(). Joining many pieces with + gets clumsy. sep.join(list) places the separator between every pair of items.

parts = ["Aarav", "Kumar", "Sharma"]
print(" ".join(parts))
print("-".join(parts))
print("".join(parts))
print("/".join(["11", "08", "2026"]))
print("+".join("UPI"))

Output:

Aarav Kumar Sharma
Aarav-Kumar-Sharma
AaravKumarSharma
11/08/2026
U+P+I

The last line shows a trick worth knowing: a string is itself a sequence of characters, so "+".join("UPI") puts the separator between the individual letters. Also note the separator goes between items only — three items give two separators, never a trailing one.

Worked example — masking sensitive numbers. Banks and payment apps show only the last four digits. That is repetition plus a negative slice.

mobile = "9876543210"
print("Sending OTP to", "X" * 6 + mobile[-4:])
acct = "50100234567890"
print("A/c ending", acct[-4:])
pnr = "2641853970"
print("PNR reversed:", pnr[::-1])
print("Alternate digits:", pnr[::2])
print("Is '18' part of PNR?", "18" in pnr)

Output:

Sending OTP to XXXXXX3210
A/c ending 7890
PNR reversed: 0793581462
Alternate digits: 24837
Is '18' part of PNR? True
Concatenation s1 + s2 -> str Both operands must be str. 'Marks: ' + 87 raises TypeError: can only concatenate str (not "int") to str.
Repetition s * n or n * s -> str n must be an int. If n is zero or negative the result is the empty string ''.
Membership sub in s -> bool sub not in s -> bool Case sensitive, and works for multi-character substrings. '' in s is always True.
Slicing s[start:stop] -> str start included, stop excluded. Missing start means 0, missing stop means len(s). Out-of-range values are clipped, never an error.
Slicing with a step s[start:stop:step] -> str step must not be 0. With a negative step the defaults flip to last-character-first, so s[::-1] reverses the string.
str.join() sep.join(sequence_of_str) -> str '-'.join(['11','08','2026']) gives '11-08-2026'. A non-string item raises TypeError: sequence item 1: expected str instance, int found.
Remember
  • + joins strings and both operands must be str — mixing in an int raises TypeError; convert with str() first.
  • s * n repeats a string; in and not in test for a substring and are case sensitive.
  • In s[start:stop] the stop index is excluded; slicing never raises IndexError, and a backward range like s[5:2] quietly returns the empty string.
  • s[start:stop:step] with a negative step reads right to left; s[::-1] reverses the whole string.
  • sep.join(list) is bulk concatenation — the separator appears between items only, and every item must already be a string.

Traversing a String Using Loops

Quick answer Walk a string character by character with a for loop, by position with range(len(s)), or with a while loop — then check your loop against the built-in count() and replace().

Traversing means visiting every character once. There are three standard ways, and which one you pick depends on whether you need the character, the position, or both.

1. Character loop — when the position does not matter.

name = "MEERA"
for ch in name:
    print(ch, end=" ")
print()
for i in range(len(name)):
    print(i, name[i])

Output:

M E E R A 
0 M
1 E
2 E
3 R
4 A

The first loop gives you the character directly. The second gives you the index, and you fetch the character with name[i]. Use range(len(s)) whenever the answer depends on where a character sits — for example when you want every alternate character, or when you must report a position.

2. while loop — when you control the step yourself.

s = "Priodemy"
i = 0
while i < len(s):
    print(i, s[i])
    i += 1
j = -1
while j >= -len(s):
    print(s[j], end="")
    j -= 1
print()

Output:

0 P
1 r
2 i
3 o
4 d
5 e
6 m
7 y
ymedoirP

The second while loop walks backwards using negative indices, from -1 down to -len(s). Forget the i += 1 or j -= 1 and the loop runs forever — that is the one real danger of while over for.

3. Counting while you traverse. The classic exercise: count vowels, consonants and spaces in one pass.

sentence = "Bharat is my country"
vowels = consonants = spaces = 0
for ch in sentence:
    if ch == " ":
        spaces += 1
    elif ch.lower() in "aeiou":
        vowels += 1
    else:
        consonants += 1
print("Vowels     :", vowels)
print("Consonants :", consonants)
print("Spaces     :", spaces)
print("Total      :", len(sentence))

Output:

Vowels     : 5
Consonants : 12
Spaces     : 3
Total      : 20

Two ideas make this short. ch.lower() means you do not have to test both 'A' and 'a'. And ch in "aeiou" uses the membership operator instead of five separate or conditions.

Building a new string inside a loop. Because strings are immutable you cannot edit characters in place. The standard pattern is to start with an empty string and keep adding to it. This is how you reverse a string without slicing:

word = "BHARAT"
rev = ""
for ch in word:
    rev = ch + rev
print("Original :", word)
print("Reversed :", rev)
print("Check    :", rev == word[::-1])

Output:

Original : BHARAT
Reversed : TARAHB
Check    : True

Read rev = ch + rev carefully — the new character goes in front of what you already have, which is what reverses it. Write rev = rev + ch by mistake and you simply rebuild the original string. The last line checks the loop against word[::-1], and the two agree. One warning about testing this: do not try it first on a word like NAYAN or MALAYALAM. Those read the same in both directions, so a broken loop would still look correct. Always test a reversal on a word that is not a palindrome.

count() — the built-in version of a counting loop.

s = "banana"
print(s.count("a"))
print(s.count("na"))
print(s.count("an"))
print(s.count("z"))
print(s.count("a", 2))
print(s.count("a", 2, 5))
print("aaaa".count("aa"))

Output:

3
2
2
0
2
1
2

The last line is the exam trap. "aaaa".count("aa") is 2, not 3, because count() counts non-overlapping occurrences — after matching positions 0-1 it restarts the search at position 2. Also note count() never raises an error; a substring that is absent simply gives 0.

Worked example — loop versus built-in method. Here the same two jobs are done twice: once with a loop, once with the built-in method. Writing both is the fastest way to convince yourself the method is doing what you think.

title = "Indian Railway Catering Corporation"

# count 'a' the long way
n = 0
for ch in title:
    if ch == "a":
        n += 1
print("Loop count of 'a' :", n)
print("count() says      :", title.count("a"))

# turn spaces into hyphens the long way
slug = ""
for ch in title:
    if ch == " ":
        slug = slug + "-"
    else:
        slug = slug + ch.lower()
print("Loop-built slug   :", slug)
print("replace() version :", title.lower().replace(" ", "-"))
print("Same result?      :", slug == title.lower().replace(" ", "-"))

Output:

Loop count of 'a' : 5
count() says      : 5
Loop-built slug   : indian-railway-catering-corporation
replace() version : indian-railway-catering-corporation
Same result?      : True

replace(old, new) swaps every occurrence and returns a new string. Like every other string method it leaves the original alone — title still has its spaces and capitals after that line runs.

Character traversal for ch in s: ch is the character itself. Shortest form; use when the position is irrelevant.
Index traversal for i in range(len(s)): i runs 0 to len(s)-1; fetch the character with s[i]. Use when you must report or use the position.
Reverse traversal for i in range(len(s)-1, -1, -1): Visits index len(s)-1 down to 0. The middle -1 is the stop and is excluded, which is why index 0 is still visited.
str.count() s.count(sub[, start[, end]]) -> int Counts NON-overlapping occurrences. 'aaaa'.count('aa') is 2. Returns 0 if absent — never an error.
str.replace() s.replace(old, new[, count]) -> str Returns a NEW string; s is unchanged. Case sensitive. The optional count limits how many are replaced, left to right.
Remember
  • for ch in s gives you characters; for i in range(len(s)) gives you positions — pick the one that matches what the question asks for.
  • A while loop needs you to increment the counter yourself; miss it and the loop never ends.
  • Because strings are immutable, you build a modified string by starting from "" and concatenating; rev = ch + rev reverses a string this way.
  • s.count(sub) counts NON-overlapping occurrences, so 'aaaa'.count('aa') is 2, and returns 0 rather than an error when absent.
  • s.replace(old, new) returns a new string with every occurrence swapped; the original string is unchanged.

Case Conversion and Character-Test Methods

Quick answer Four methods change the case of a string and six is-methods answer yes/no questions about its characters — the standard toolkit for cleaning and validating user input.

Changing case. Four methods, all returning a new string and all leaving the original untouched.

s = "pRIYA sHARMA"
print(s.upper())
print(s.lower())
print(s.capitalize())
print(s.title())
print(s)
print("cbse board 2026".title())
print("RAM'S SHOP".title())
print("123abc def".title())

Output:

PRIYA SHARMA
priya sharma
Priya sharma
Priya Sharma
pRIYA sHARMA
Cbse Board 2026
Ram'S Shop
123Abc Def

Read those results line by line, because the differences are exactly what board questions test.

  • upper() and lower() change every letter and leave digits and punctuation alone.
  • capitalize() makes the first character uppercase and forces everything else to lowercase. That is why "pRIYA sHARMA" becomes "Priya sharma" and not "PRIYA sHARMA".
  • title() capitalises the first letter of every word and lowercases the rest of each word.
  • Line 5 proves immutability again: printing s afterwards still gives the original.

The title() gotcha. title() decides a new word has begun after any non-letter, not just after a space. So the apostrophe in RAM'S starts a new word and you get Ram'S, and the digits in 123abc start a new word so a becomes A. This is a genuine limitation of the method, not a bug in your code.

The six test methods. Each returns True or False for the whole string. Run all six on ten carefully chosen values and the whole pattern falls out.

tests = ["Delhi", "Delhi11", "110001", "delhi", "DELHI", "   ", "", "Del hi", "11.5", "Rs500"]
for t in tests:
    print(repr(t), t.isalnum(), t.isalpha(), t.isdigit(), t.islower(), t.isupper(), t.isspace())

Output:

'Delhi' True True False False False False
'Delhi11' True False False False False False
'110001' True False True False False False
'delhi' True True False True False False
'DELHI' True True False False True False
'   ' False False False False False True
'' False False False False False False
'Del hi' False False False False False False
'11.5' False False False False False False
'Rs500' True False False False False False

The same results, lined up so the columns are easy to read:

valueisalnum()isalpha()isdigit()islower()isupper()isspace()
'Delhi'TrueTrueFalseFalseFalseFalse
'Delhi11'TrueFalseFalseFalseFalseFalse
'110001'TrueFalseTrueFalseFalseFalse
'delhi'TrueTrueFalseTrueFalseFalse
'DELHI'TrueTrueFalseFalseTrueFalse
' 'FalseFalseFalseFalseFalseTrue
''FalseFalseFalseFalseFalseFalse
'Del hi'FalseFalseFalseFalseFalseFalse
'11.5'FalseFalseFalseFalseFalseFalse
'Rs500'TrueFalseFalseFalseFalseFalse

Four rules explain that entire table:

  1. A space is not alphanumeric. 'Del hi' is False for isalnum, isalpha and isdigit — one space is enough to fail all three.
  2. A dot is not a digit. '11.5'.isdigit() is False, which is why you cannot use isdigit() to check whether input is a valid decimal number.
  3. The empty string is False for all six. Nothing can be "all letters" if there is nothing there.
  4. islower() and isupper() look only at letters. '110001' has no letters at all, so both are False. 'Delhi11'.islower() is also False because of the capital D, but 'delhi' would be True even with digits mixed in.

Worked example — checking a PAN number. An Indian PAN is exactly ten characters: five capital letters, four digits, then one more capital letter. Slicing splits the string into those three zones and the test methods check each zone.

# PAN format: 5 letters, then 4 digits, then 1 letter -- all capitals
for pan in ["ABCDE1234F", "abcde1234f", "ABCD12345F", "ABCDE1234", "ABCDE12E4F"]:
    ok = (len(pan) == 10
          and pan[0:5].isalpha() and pan[0:5].isupper()
          and pan[5:9].isdigit()
          and pan[9].isalpha() and pan[9].isupper())
    if ok:
        print(pan, "-> VALID")
    else:
        print(pan, "-> INVALID")

Output:

ABCDE1234F -> VALID
abcde1234f -> INVALID
ABCD12345F -> INVALID
ABCDE1234 -> INVALID
ABCDE12E4F -> INVALID

Each rejection has a different cause, and that is the point of the example: abcde1234f fails on isupper(), ABCD12345F fails because pan[0:5] is 'ABCD1' which is not all letters, ABCDE1234 fails the length check, and ABCDE12E4F fails because pan[5:9] is '12E4' which is not all digits.

str.lower() / str.upper() s.lower() -> str s.upper() -> str Return new strings; digits and punctuation are untouched. Use lower() on both sides for a case-insensitive comparison.
str.capitalize() s.capitalize() -> str First character uppercase, EVERY other character lowercase. 'pRIYA sHARMA'.capitalize() is 'Priya sharma'.
str.title() s.title() -> str First letter of each word uppercase, rest lowercase. A word starts after any non-letter, so "RAM'S SHOP".title() is "Ram'S Shop".
str.isalpha() / str.isalnum() s.isalpha() -> bool s.isalnum() -> bool isalpha: letters only. isalnum: letters or digits. Both False for '' and for any string containing a space.
str.isdigit() / str.isspace() s.isdigit() -> bool s.isspace() -> bool '11.5'.isdigit() is False — the dot is not a digit. isspace() is True only for a non-empty string made entirely of whitespace.
str.islower() / str.isupper() s.islower() -> bool s.isupper() -> bool Judged on cased characters only. '110001'.isupper() is False because it has no letters. Both False for ''.
Remember
  • capitalize() uppercases the first character and forces every other character to lowercase; title() does it per word.
  • title() starts a new word after any non-letter, so "RAM'S SHOP".title() gives Ram'S Shop — a known limitation.
  • All six is-methods test the WHOLE string and return False for the empty string.
  • A space makes isalnum(), isalpha() and isdigit() all False; a decimal point makes isdigit() False.
  • islower() and isupper() judge only the cased characters, so a digits-only string gives False for both.

Searching, Cleaning and Splitting Strings

Quick answer find() and index() locate a substring and differ only in how they fail, startswith()/endswith() check the ends, strip() removes unwanted characters, and split()/partition() cut a string into pieces.

find() and index() — the classic exam pair. Both return the position of the first occurrence of a substring. They behave identically when the substring is present.

s = "computer science"
print(s.find("e"))
print(s.find("e", 8))
print(s.find("science"))
print(s.find("maths"))
print(s.index("science"))
print(s.find("e", 3, 7))

Output:

6
12
9
-1
9
6

s.find("e") gives 6, the first e. Adding a start position, s.find("e", 8), makes the search begin at index 8, so it finds the later e at 12. find("maths") returns -1 because the substring is absent.

Now the difference. Ask index() for something that is not there:

s = "computer science"
print(s.index("maths"))

Output:

Traceback (most recent call last):
  File "s5b.py", line 2, in 
    print(s.index("maths"))
          ~~~~~~~^^^^^^^^^
ValueError: substring not found

That is the whole difference: find() returns -1 when the substring is missing, index() raises ValueError: substring not found. Use find() when the substring might legitimately be absent and you want to test the result with an if. Use index() when its absence means the data is wrong and the program should stop.

startswith() and endswith(). These answer a yes/no question about the two ends of a string, which is how you check a file extension or a prefix code.

f = "marksheet_2026.csv"
print(f.startswith("marks"))
print(f.startswith("Marks"))
print(f.endswith(".csv"))
print(f.endswith(".txt"))
print(f.endswith((".csv", ".txt")))
print(f.startswith("sheet", 4))
print(f.startswith("sheet", 5))
print(f.endswith("2026", 0, 14))

Output:

True
False
True
False
True
True
False
True

Both are case sensitive, so "Marks" fails. Both accept a tuple of possibilities, which is how f.endswith((".csv", ".txt")) returns True — it means "does it end with any of these". And both take optional start and end positions: f.startswith("sheet", 4) is True because sheet begins at index 4, while starting the check at 5 gives False.

strip(), lstrip() and rstrip(). Real user input arrives with stray spaces around it. These three remove them — from both ends, the left end, and the right end respectively.

raw = "   Anjali Nair   "
print(repr(raw.strip()))
print(repr(raw.lstrip()))
print(repr(raw.rstrip()))
print(len(raw), len(raw.strip()))
code = "xxCS083xx"
print(code.strip("x"))
print(code.lstrip("x"))
print(code.rstrip("x"))
print("www.priodemy.com".strip("wmoc."))

Output:

'Anjali Nair'
'Anjali Nair   '
'   Anjali Nair'
17 11
CS083
CS083xx
xxCS083
priodemy

Two things to note. Only the ends are trimmed — the space inside Anjali Nair survives. And when you pass an argument, it is treated as a set of characters, not as a substring. That is why "www.priodemy.com".strip("wmoc.") gives priodemy: Python keeps chewing characters off each end as long as they appear anywhere in "wmoc.", and it stops at p and at y.

replace() with a limit.

s = "I love cricket. Cricket is life."
print(s.replace("Cricket", "Hockey"))
print(s.replace("cricket", "hockey"))
print(s)
print("a-b-c-d".replace("-", "+", 2))
print("Rs. 1,25,000".replace(",", ""))

Output:

I love cricket. Hockey is life.
I love hockey. Cricket is life.
I love cricket. Cricket is life.
a+b+c-d
Rs. 125000

replace() is case sensitive, so the capital and small versions of "cricket" are treated as different words. The third line proves the original is unchanged. The optional third argument limits the replacements, and replacing with "" is the normal way to delete characters — here, stripping the commas out of an Indian-format amount before converting it to a number.

split() — string to list.

line = "Aarav,Meera,Rohan,Sana"
print(line.split(","))
print(line.split(",", 2))
print("Computer Science is fun".split())
print("  spaced   out  ".split())
print("  spaced   out  ".split(" "))
print("a::b::c".split("::"))
print("nocomma".split(","))

Output:

['Aarav', 'Meera', 'Rohan', 'Sana']
['Aarav', 'Meera', 'Rohan,Sana']
['Computer', 'Science', 'is', 'fun']
['spaced', 'out']
['', '', 'spaced', '', '', 'out', '', '']
['a', 'b', 'c']
['nocomma']

Compare the fourth and fifth lines carefully — this is the single most misunderstood thing about split(). With no argument, it splits on any run of whitespace and throws away the empty pieces, giving a clean two-item list. With split(" ") it splits at every single space, so runs of spaces produce empty strings in the list. For cleaning up messy user input you almost always want the no-argument form.

Also note that split() always returns a list, and a substring that is never found still gives a one-item list rather than an error.

partition() — split once, keep the separator.

print("aarav@okaxis".partition("@"))
print("a-b-c-d".partition("-"))
print("nodash".partition("-"))
print("-".join(["11", "08", "2026"]))
print(", ".join(["Delhi", "Mumbai", "Chennai"]))
print("".join(["P", "y", "t", "h", "o", "n"]))
print("*".join("CBSE"))

Output:

('aarav', '@', 'okaxis')
('a', '-', 'b-c-d')
('nodash', '', '')
11-08-2026
Delhi, Mumbai, Chennai
Python
C*B*S*E

partition() differs from split() in three ways: it cuts only at the first occurrence, it keeps the separator, and it returns a tuple of exactly three items — always three, even when the separator is missing, in which case you get the whole string followed by two empty strings. That fixed shape is why it is convenient: you can safely unpack it without checking anything first.

join() is the inverse of split(): split takes one string apart into a list, join puts a list back together into one string.

Worked example — reading one line of a marksheet. This is exactly the shape of data you get from a spreadsheet export: comma separated, with sloppy spacing.

record = "  11A0342 , Meera  Iyer , 87 , 91 , 78  "
parts = record.split(",")
print(parts)
roll = parts[0].strip()
name = parts[1].strip()
m1 = int(parts[2].strip())
m2 = int(parts[3].strip())
m3 = int(parts[4].strip())
print("Roll  :", roll)
print("Name  :", name.title())
print("Total :", m1 + m2 + m3, "/ 300")
print("Marks :", "|".join([str(m1), str(m2), str(m3)]))
print("Class :", roll[:2], " Section:", roll[2])

Output:

['  11A0342 ', ' Meera  Iyer ', ' 87 ', ' 91 ', ' 78  ']
Roll  : 11A0342
Name  : Meera  Iyer
Total : 256 / 300
Marks : 87|91|78
Class : 11  Section: A

Look at the first printed line: split(",") keeps every stray space, which is why each piece then needs .strip(). Without the strip, int(" 87 ") would still work — int() tolerates surrounding spaces — but the roll number and the name would carry invisible spaces into the rest of the program. Notice too that the double space inside "Meera Iyer" survives, because strip() only touches the ends.

str.find() s.find(sub[, start[, end]]) -> int Index of the FIRST occurrence, or -1 if absent. Safe to test with an if. Searches s[start:end] when the extra arguments are given.
str.index() s.index(sub[, start[, end]]) -> int Identical to find() when the substring is present. When absent it raises ValueError: substring not found. That is the ONLY difference.
str.startswith() / str.endswith() s.startswith(prefix[, start[, end]]) -> bool s.endswith(suffix[, start[, end]]) -> bool Case sensitive. The prefix or suffix may be a TUPLE, so f.endswith(('.csv', '.txt')) means 'either extension'.
str.strip() / lstrip() / rstrip() s.strip([chars]) -> str s.lstrip([chars]) -> str s.rstrip([chars]) -> str No argument removes whitespace from both ends / left / right. With chars it removes ANY of those characters from the end, not that substring.
str.split() s.split([sep[, maxsplit]]) -> list No sep splits on runs of whitespace and drops empties. With sep, empties are kept: 'a,b,,c'.split(',') is ['a','b','','c']. sep='' raises ValueError: empty separator.
str.partition() s.partition(sep) -> (before, sep, after) Always a 3-item TUPLE and cuts only at the first occurrence. If sep is not present the result is (s, '', ''). Needs exactly one argument.
Remember
  • find() and index() both give the position of the first occurrence; find() returns -1 if absent while index() raises ValueError: substring not found.
  • startswith() and endswith() are case sensitive and accept a tuple of options, e.g. f.endswith(('.csv', '.txt')).
  • strip(), lstrip() and rstrip() trim only the ends; with an argument they remove any of those characters, not that substring.
  • split() with no argument collapses runs of whitespace and drops empties, but split(" ") keeps every empty piece; it always returns a list.
  • partition(sep) always returns a 3-item tuple (before, sep, after), cutting only at the first occurrence and keeping the separator; if sep is missing you get (s, '', '').

The formula sheet

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

s = 'text' | s = "text" | s = '''text'''
Creating a string
s[i] where i runs from 0 to len(s)-1
Forward index
s[-i] where i runs from 1 to len(s)
Backward index
len(s) -> int
len()
s[0] = 'X' -> TypeError
Immutability rule
s1 + s2 -> str
Concatenation
s * n or n * s -> str
Repetition
sub in s -> bool sub not in s -> bool
Membership
s[start:stop] -> str
Slicing
s[start:stop:step] -> str
Slicing with a step
sep.join(sequence_of_str) -> str
str.join()
for ch in s:
Character traversal
for i in range(len(s)):
Index traversal
for i in range(len(s)-1, -1, -1):
Reverse traversal
s.count(sub[, start[, end]]) -> int
str.count()
s.replace(old, new[, count]) -> str
str.replace()
s.lower() -> str s.upper() -> str
str.lower() / str.upper()
s.capitalize() -> str
str.capitalize()
s.title() -> str
str.title()
s.isalpha() -> bool s.isalnum() -> bool
str.isalpha() / str.isalnum()
s.isdigit() -> bool s.isspace() -> bool
str.isdigit() / str.isspace()
s.islower() -> bool s.isupper() -> bool
str.islower() / str.isupper()
s.find(sub[, start[, end]]) -> int
str.find()
s.index(sub[, start[, end]]) -> int
str.index()
s.startswith(prefix[, start[, end]]) -> bool s.endswith(suffix[, start[, end]]) -> bool
str.startswith() / str.endswith()
s.strip([chars]) -> str s.lstrip([chars]) -> str s.rstrip([chars]) -> str
str.strip() / lstrip() / rstrip()
s.split([sep[, maxsplit]]) -> list
str.split()
s.partition(sep) -> (before, sep, after)
str.partition()

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

What is the output of this code?s = "PYTHON"print(s[1:4], s[-2:], s[::-2])

Q2

What is the output of this code?s = "Programming"print(s.count("m"), s.find("m"), s.index("g"))

Q3

What is the output of this code?s = " CBSE 2026 "print(len(s.strip()), s.strip().split())

Q4

What is the output of print("a,b,,c".split(","))?

Q5

What is the output of this code?s = "Hello World"print(s.replace("l", "L", 2))

Q6

What is the output of print("ab".join("XYZ"))?

Q7

What is the output of print("banana".partition("na"))?

Q8

Which statement about find() and index() is correct?

Q9

What is the output of this code?t = "Amit Kumar"print(t[0:4:2] + t[-1])

Q10

What is the output of print("Del hi".isalnum(), "110001".isdigit(), " ".isspace())?

Q11

What do these two lines print?print("mAHATMA gANDHI".title())print("mAHATMA gANDHI".capitalize())

Q12

For w = "ABCDEF", what does print(w[5:2], len(w[5:2]), w[5:2:-1]) display?

NCERT solutions & previous-year questions

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

NCERT questions 6

1 Consider the string mySubject = "Computer Science". What will be the output of the following string operations?(i) print(mySubject[0:len(mySubject)])(ii) print(mySubject[-7:-1])(iii) print(mySubject[::2])(iv) print(mySubject[len(mySubject)-1])(v) print(mySubject[::-2])(vi) print(mySubject[:3] + mySubject[3:])(vii) print(mySubject.startswith("Comp"))(viii) print(mySubject.isalpha())Indexing and slicing

First write out the index map, because every part depends on it. len(mySubject) is 16.

CharComputer(sp)Science
Index0123456789101112131415
Neg-16-15-14-13-12-11-10-9-8-7-6-5-4-3-2-1

Working:

  • (i) [0:16] is the whole string.
  • (ii) [-7:-1] is indices -7 to -2, i.e. 9 to 14 = S c i e n c. The -1 stop excludes the final e.
  • (iii) [::2] takes indices 0, 2, 4, 6, 8, 10, 12, 14 = C, m, u, e, space, c, e, c.
  • (iv) [15] is the last character.
  • (v) [::-2] starts at index 15 and steps back two: 15, 13, 11, 9, 7, 5, 3, 1 = e, n, i, S, r, t, p, o.
  • (vi) Splitting at 3 and rejoining gives back the original — this shows s[:n] + s[n:] is always s.
  • (vii) The string does begin with Comp, and the case matches, so True.
  • (viii) isalpha() is False because of the space at index 8.

Program and real output:

mySubject = "Computer Science"
print(len(mySubject))
print(mySubject[0:len(mySubject)])
print(mySubject[-7:-1])
print(mySubject[::2])
print(mySubject[len(mySubject)-1])
print(mySubject[::-2])
print(mySubject[:3] + mySubject[3:])
print(mySubject.startswith("Comp"))
print(mySubject.isalpha())
print(mySubject.isalnum())
16
Computer Science
Scienc
Cmue cec
e
eniSrtpo
Computer Science
True
False
False

isalnum() is also False, and for the same reason — the space is neither a letter nor a digit.

2 Consider the string myAddress = "WZ-1,New Ganga Nagar,New Delhi". What will be the output of the following operations?(i) print(myAddress.lower())(ii) print(myAddress.upper())(iii) print(myAddress.count('New'))(iv) print(myAddress.find('New'))(v) print(myAddress.split(','))(vi) print(myAddress.split(' '))(vii) print(myAddress.replace('New','Old'))(viii) print(myAddress.partition(','))(ix) print(myAddress.index('Agra'))Built-in string methods

Working:

  • (iii) New appears twice — once before Ganga Nagar and once before Delhi.
  • (iv) The string starts W(0) Z(1) -(2) 1(3) ,(4) N(5), so the first New begins at index 5. find() always reports the first occurrence.
  • (v) Splitting on the comma gives 3 items, because there are 2 commas.
  • (vi) Splitting on a space gives 4 items — and notice the commas stay attached, so you get 'WZ-1,New' and 'Nagar,New'. This is why choosing the right separator matters.
  • (viii) partition() cuts only at the first comma and returns a 3-item tuple, keeping the comma as the middle item. Compare this with (v), which cut at both commas and threw the commas away.
  • (ix) Agra is not in the string, so index() raises an error rather than returning a value.

Program and real output:

myAddress = "WZ-1,New Ganga Nagar,New Delhi"
print(len(myAddress))
print(myAddress.lower())
print(myAddress.upper())
print(myAddress.count('New'))
print(myAddress.find('New'))
print(myAddress.split(','))
print(myAddress.split(' '))
print(myAddress.replace('New', 'Old'))
print(myAddress.partition(','))
30
wz-1,new ganga nagar,new delhi
WZ-1,NEW GANGA NAGAR,NEW DELHI
2
5
['WZ-1', 'New Ganga Nagar', 'New Delhi']
['WZ-1,New', 'Ganga', 'Nagar,New', 'Delhi']
WZ-1,Old Ganga Nagar,Old Delhi
('WZ-1', ',', 'New Ganga Nagar,New Delhi')

Part (ix) run on its own:

myAddress = "WZ-1,New Ganga Nagar,New Delhi"
print(myAddress.index('Agra'))
Traceback (most recent call last):
  File "ncert2b.py", line 2, in 
    print(myAddress.index('Agra'))
          ~~~~~~~~~~~~~~~^^^^^^^^
ValueError: substring not found

Had the question used myAddress.find('Agra') instead, the answer would simply have been -1 with no error.

3 Write a program that takes a line of text as input and counts the number of uppercase letters, lowercase letters, digits, spaces and other characters in it.Traversal with character-test methods

Approach. Walk the string once with a for loop. For each character, ask the test methods in a chain of if / elif / else. The order matters: put isupper() and islower() before the catch-all else, and make the else collect everything that failed all four tests (punctuation, symbols).

text = input("Enter a line of text: ")
upper = lower = digits = spaces = others = 0
for ch in text:
    if ch.isupper():
        upper += 1
    elif ch.islower():
        lower += 1
    elif ch.isdigit():
        digits += 1
    elif ch.isspace():
        spaces += 1
    else:
        others += 1
print("Uppercase letters :", upper)
print("Lowercase letters :", lower)
print("Digits            :", digits)
print("Spaces            :", spaces)
print("Other characters  :", others)
print("Total (len)       :", len(text))

Sample run with the input CBSE 083 Exam 2026, All the Best!

Enter a line of text: CBSE 083 Exam 2026, All the Best!
Uppercase letters : 7
Lowercase letters : 11
Digits            : 7
Spaces            : 6
Other characters  : 2
Total (len)       : 33

Check your answer. The five counts must add up to len(text): 7 + 11 + 7 + 6 + 2 = 33, which matches. Making the program print len(text) as well is a free self-check and worth doing in the exam too. The two "other" characters here are the comma and the exclamation mark.

4 Write a program that takes a string containing several words and produces a new string in which the first letter of each word is capitalised.split(), case methods and join()

Approach. Break the sentence into words with split(), then for each word take w[0].upper() and stick the rest of the word on in lowercase with w[1:].lower(). Because strings are immutable you cannot edit a word in place — you build a fresh one.

line = input("Enter a sentence: ")
words = line.split()
newline = ""
for w in words:
    newline = newline + w[0].upper() + w[1:].lower() + " "
newline = newline.rstrip()
print("Built manually :", newline)
print("Using title()  :", line.title())
print("Using join()   :", " ".join(words).title())

Sample run with the input the taj mahal is in AGRA

Enter a sentence: the taj mahal is in AGRA
Built manually : The Taj Mahal Is In Agra
Using title()  : The Taj Mahal Is In Agra
Using join()   : The Taj Mahal Is In Agra

Points worth marks.

  • split() with no argument is used deliberately, so that extra spaces between words do not create empty items.
  • The loop adds a trailing space after every word, so rstrip() removes the one left dangling at the end. Without it the answer has an invisible extra space.
  • w[1:].lower() is what turns AGRA into Agra. If you write just w[1:] you get AGRA unchanged apart from its first letter.
  • The built-in title() does the whole job in one call, and all three methods agree here. Remember its limitation though: it also capitalises after apostrophes and digits.
5 Write a program to input a string and check whether it is a palindrome. Solve it in two ways — using slicing, and using a loop.Slicing and two-pointer traversal

What a palindrome is. It reads the same forwards and backwards. To make the test fair we first remove spaces and put everything into one case, so that Nurses Run and NURSESRUN are treated alike.

s = input("Enter a string: ")
clean = s.replace(" ", "").lower()
if clean == clean[::-1]:
    print(s, "-> PALINDROME")
else:
    print(s, "-> NOT a palindrome")

# same check with an explicit loop
i = 0
j = len(clean) - 1
flag = True
while i < j:
    if clean[i] != clean[j]:
        flag = False
        break
    i += 1
    j -= 1
print("Loop method says palindrome?", flag)

Run 1 with the input Malayalam

Enter a string: Malayalam -> PALINDROME
Loop method says palindrome? True

Run 2 with the input Nitin Kumar

Enter a string: Nitin Kumar -> NOT a palindrome
Loop method says palindrome? False

How the loop version works. Two markers start at opposite ends. Compare clean[i] with clean[j]; if they ever differ the string cannot be a palindrome, so set the flag to False and break out immediately. Otherwise move i right and j left. The condition while i < j stops the loop as soon as the markers meet or cross, so each pair is checked exactly once and a single middle character is correctly ignored.

Why .lower() matters. Without it, Malayalam would fail: the capital M at the start would be compared with the small m at the end, and 'M' != 'm'.

6 Write a program to count how many times a given word occurs in a line of text, and to print all the positions at which it occurs.split(), count() and find() with a start position

Two different questions, two different tools. "How many whole words match" is answered by splitting into words and comparing. "Where does the substring occur" is answered by find() called repeatedly with a moving start position.

text = "the quick brown fox jumps over the lazy dog the end"
word = "the"

words = text.split()
count = 0
for w in words:
    if w == word:
        count += 1
print("Whole-word count :", count)
print("count() says     :", text.count(word))
print("First position   :", text.find(word))

pos = text.find(word)
positions = []
while pos != -1:
    positions.append(pos)
    pos = text.find(word, pos + 1)
print("All positions    :", positions)

Output:

Whole-word count : 3
count() says     : 3
First position   : 0
All positions    : [0, 31, 44]

The important idea in the last loop. find() only ever reports the first match. To get the next one you call it again with pos + 1 as the start, so the search resumes just past the match you already recorded. The loop ends when find() returns -1, meaning there is nothing left. This pattern only works with find(); using index() here would crash with ValueError on the final call instead of ending the loop neatly.

A warning about count(). Here the whole-word count and text.count("the") happen to agree at 3, but they are not the same question. If the text contained the word theatre, count("the") would count the the inside it, while the split-and-compare loop would not. When a question says "how many times does the WORD occur", split into words first.

Previous-year board questions 4

Q1 Predict the output of the following code. (2 marks)s = "Priodemy@2026"n = len(s)m = ""for i in range(0, n): if s[i].isupper(): m = m + s[i].lower() elif s[i].isalpha(): m = m + s[i].upper() elif s[i].isdigit(): m = m + "#" else: m = m + "*"print(m) Board pattern — 2 marks

How to attack this in the exam: make a small table, one row per character, and decide which branch fires. The branches are tested in order, so the first one that is True wins.

CharacterBranch that firesAdded to m
Pisupper()p
r i o d e m yisalpha() (not upper)R I O D E M Y
@else*
2 0 2 6isdigit()# # # #

So every capital becomes small, every small letter becomes capital, digits become # and anything else becomes *.

Answer:

pRIODEMY*####

The two traps. First, the order of the branches: isalpha() is True for capitals as well, so if elif s[i].isalpha() came first, every letter would become uppercase and nothing would ever go lowercase. Second, m starts as "" and is rebuilt by concatenation on every pass — you cannot modify s itself, because strings are immutable.

Q2 Differentiate between the find() and index() methods of a string. Support your answer with a suitable example. (2 marks) Board pattern — 2 marks

The difference in one line: both return the index of the first occurrence of a substring, but when the substring is not present find() returns -1 while index() raises ValueError: substring not found.

Pointfind()index()
Substring presentReturns its indexReturns the same index
Substring absentReturns -1Raises ValueError
Program continues?YesNo — it stops
When to useWhen absence is normal and you want to test the resultWhen absence means the data is wrong

Example — both behave the same when the substring is there:

s = "INDIA IS GREAT"
print(s.find("IS"))
print(s.index("IS"))
print(s.find("SMALL"))
print(s.find("I", 3))
print(s.index("I", 3))
6
6
-1
3
3

And the difference when it is not:

s = "INDIA IS GREAT"
print(s.index("SMALL"))
Traceback (most recent call last):
  File "pyq2b.py", line 2, in 
    print(s.index("SMALL"))
          ~~~~~~~^^^^^^^^^
ValueError: substring not found

Both also accept optional start and end positions, which is why s.find("I", 3) skips the I at index 0 and reports the one at index 3.

Q3 Rewrite the following code after removing all syntax and logical errors. Underline each correction. (2 marks)Text = 'CBSE Exam 2026"print(Text.Upper())Text[0] = "c"print(len Text)print(Text.find['Exam']) Board pattern — 2 marks

The five errors, one line at a time:

  1. Text = 'CBSE Exam 2026" — the string opens with a single quote and closes with a double quote. Python reports SyntaxError: unterminated string literal. The quotes must match.
  2. Text.Upper() — Python is case sensitive and the method is upper(), all lowercase. As written it would raise AttributeError.
  3. Text[0] = "c" — strings are immutable, so this raises TypeError: 'str' object does not support item assignment. Build a new string with slicing instead.
  4. len Text — len is a function and needs brackets: len(Text).
  5. Text.find['Exam'] — a method is called with round brackets, not square ones: Text.find('Exam').

Corrected program (the corrected part of each line is shown in bold, which is what you would underline in the answer sheet):

Text = "CBSE Exam 2026"
print(Text.upper())
Text = "c" + Text[1:]
print(len(Text))
print(Text.find('Exam'))
print(Text)

Real output of the corrected code:

CBSE EXAM 2026
14
5
cBSE Exam 2026

Reading the output. The upper() line runs before the reassignment, so it still shows the original capital C. After Text = "c" + Text[1:] the string is cBSE Exam 2026, which is 14 characters long, and Exam begins at index 5 (c0 B1 S2 E3 space4 E5).

Q4 Write a Python program that accepts a sentence from the user and displays: (a) the number of words in it, (b) the words that begin with a vowel and how many there are, and (c) the sentence with its words in reverse order. (3 marks) Board pattern — 3 marks

Plan. split() with no argument turns the sentence into a list of words and handles messy spacing for you. Once you have the list, part (a) is just len(), part (b) is a loop testing w[0].lower() in "aeiou", and part (c) is a loop that walks the list backwards.

sentence = input("Enter a sentence: ")
words = sentence.split()

print("Number of words :", len(words))

vowel_words = []
for w in words:
    if w[0].lower() in "aeiou":
        vowel_words.append(w)
print("Start with vowel:", vowel_words)
print("How many        :", len(vowel_words))

rev = ""
for i in range(len(words) - 1, -1, -1):
    rev = rev + words[i] + " "
print("Reverse order   :", rev.rstrip())
print("Same with join  :", " ".join(words[::-1]))

Sample run with the input India is an amazing and old country

Enter a sentence: India is an amazing and old country
Number of words : 7
Start with vowel: ['India', 'is', 'an', 'amazing', 'and', 'old']
How many        : 6
Reverse order   : country old and amazing an is India
Same with join  : country old and amazing an is India

Points that earn the marks.

  • w[0].lower() — without lower() the capital I in India would be missed, and the answer would be 5 instead of 6.
  • w[0].lower() in "aeiou" uses the membership operator in place of five or conditions.
  • range(len(words)-1, -1, -1) counts down from the last index to 0. The middle -1 is the stop value and is excluded, which is exactly why index 0 is still visited.
  • rstrip() removes the trailing space the loop leaves behind. The last line shows the neater one-line alternative: reverse the list with [::-1] and glue it with join().

Careful: the word order is reversed here, not the letters. If the question had asked for the whole sentence reversed character by character, the answer would be sentence[::-1] instead.

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