4.3 String Inspection and Classification Methods

Key Takeaways

  • `str.find()` and `str.rfind()` return `-1` when a target substring is missing, whereas `str.index()` and `str.rindex()` raise a `ValueError`.
  • `str.count(sub, start, end)` counts non-overlapping occurrences of a substring, and an empty substring argument `s.count('')` evaluates to `len(s) + 1`.
  • `startswith()` and `endswith()` verify boundary substrings and accept a tuple of candidate prefixes or suffixes for multi-pattern evaluation.
  • Character classification methods (`isalpha()`, `isdigit()`, `isdecimal()`, `isnumeric()`, `isalnum()`, `isspace()`, `islower()`, `isupper()`, `istitle()`) return boolean status flags based on Unicode properties.
  • All string classification predicate methods return `False` when invoked on an empty string (`""`), as they strictly require at least one qualifying character.
Last updated: August 2026

String Inspection and Classification Methods

Python provides a comprehensive suite of built-in string methods designed to search, inspect, count, and classify textual data. Mastering their specific return types, boundary parameters, and exception handling is essential for the PCAP certification.


1. Substring Searching: find() vs. index()

Python offers four primary methods to locate the position of a substring within a string:

+-------------+----------------------+-----------------------+
| Direction   | Returns -1 on Miss   | Raises ValueError     |
+-------------+----------------------+-----------------------+
| Left-to-Right | str.find(sub)      | str.index(sub)        |
| Right-to-Left | str.rfind(sub)     | str.rindex(sub)       |
+-------------+----------------------+-----------------------+

Method Signatures

All four methods share identical parameter structures: method(sub[,start[,end]])\text{method}(\text{sub}[, \text{start}[, \text{end}]])

  • start and end interpret search bounds identically to slice syntax [start:end].
  • The index returned is always relative to the start of the entire original string, never relative to start.
text = "banana"

# Left-to-right searching
print(text.find("an"))     # 1 (first 'an' begins at index 1)
print(text.index("an"))    # 1

# Right-to-left searching
print(text.rfind("an"))    # 3 (last 'an' begins at index 3)
print(text.rindex("an"))   # 3

# Searching with slice boundaries [2:5]
print(text.find("an", 2, 5)) # 3

The Critical Difference: Missing Substring Behavior

The single most tested distinction on the PCAP exam is how these methods handle missing substrings:

text = "developer"

# find() and rfind() return -1 when the substring is absent
print(text.find("xyz"))    # -1
print(text.rfind("xyz"))   # -1

# index() and rindex() raise ValueError when the substring is absent
try:
    text.index("xyz")
except ValueError as err:
    print(f"Caught: {err}")  # Caught: substring not found

2. Counting Occurrences: count()

The count() method returns the number of non-overlapping occurrences of a substring within an optional slice range [start:end]:

s.count(sub[,start[,end]])\text{s.count}(\text{sub}[, \text{start}[, \text{end}]])

The Non-Overlapping Rule

Matches are consumed from left to right. Characters inside an identified match cannot participate in subsequent matches:

# In 'aaaa', non-overlapping pairs are indices 0-1 and 2-3
print("aaaa".count("aa"))    # 2 (not 3!)

# In 'banana', matches for 'ana' occur at index 1; remaining 'na' does not match
print("banana".count("ana"))  # 1

Edge Cases with count()

  • Empty Substring (""): Returns len(s) + 1 (the number of zero-width positions before, between, and after characters):
    print("dog".count(""))     # 4 (len("dog") + 1)
    print("".count(""))        # 1
    
  • Substring Not Found: Returns 0 without raising any exceptions.

3. Prefix and Suffix Verification: startswith() and endswith()

The startswith() and endswith() methods test whether a string begins or ends with a specific sequence:

s.startswith(prefix[,start[,end]])\text{s.startswith}(\text{prefix}[, \text{start}[, \text{end}]]) s.endswith(suffix[,start[,end]])\text{s.endswith}(\text{suffix}[, \text{start}[, \text{end}]])

filename = "project_report_2026.pdf"

print(filename.startswith("project"))  # True
print(filename.endswith(".pdf"))        # True
print(filename.startswith("Report"))    # False (case-sensitive)

Passing a Tuple of Candidates

Both methods accept a tuple of strings to check against multiple possibilities simultaneously:

url = "https://openedg.org"

# Check multiple prefixes with a tuple
print(url.startswith(("http://", "https://")))  # True

# Check multiple file extensions
image_file = "header_logo.png"
valid_extensions = (".jpg", ".jpeg", ".png", ".webp")
print(image_file.endswith(valid_extensions))     # True

Exam Trap: Passing a list or set instead of a tuple raises a TypeError:

# Raises TypeError: tuple for startswith -- or str -- required, not list
filename.endswith([".pdf", ".txt"])

4. Character Classification and Predicate Methods

Python provides a robust set of boolean predicate methods to classify the characters within a string. All of these methods return bool (True or False).

MethodCondition for TrueExample Returning TrueExample Returning False
isalpha()All characters are alphabetic letters (A-Z, a-z, Unicode letters)"Python""Python3", "Hello World"
isalnum()All characters are alphanumeric (letters or digits)"Python3""Python 3", "item-1"
isdecimal()All characters are base-10 digits (0-9)"2026""20.26", "2²"
isdigit()All characters are digits (includes superscripts/subscripts)"2026", "²""20.26", "½"
isnumeric()All characters are numeric (includes fractions, Roman numerals)"2026", "½""20.26", "abc"
isspace()All characters are whitespace (' ', \t, \n, \r, \f, \v)" \t\n """, " a "
islower()At least one cased char, and all cased chars are lowercase"python 3.10""Python", "123"
isupper()At least one cased char, and all cased chars are uppercase"PCAP 2026""Pcap", "123"
istitle()String is titlecased (words start uppercase followed by lowercase)"Hello World 2""Hello world", "HELLO"
isidentifier()String is a valid Python variable/identifier name"var_1", "_test""2var", "my-var"
isprintable()All characters are printable (or string is empty)"Hello 123""Hello\nWorld"

5. The Digit Hierarchy: isdecimal() vs. isdigit() vs. isnumeric()

Understanding the exact Unicode subset relationship between digit methods is frequently tested:

isdecimal()isdigit()isnumeric()\text{isdecimal()} \subset \text{isdigit()} \subset \text{isnumeric()}

# 1. Standard ASCII Digits ('0'-'9')
s_ascii = "123"
print(s_ascii.isdecimal(), s_ascii.isdigit(), s_ascii.isnumeric())
# Output: True True True

# 2. Superscript / Subscript digits ('²')
s_super = "2\u00b2"  # '2²'
print(s_super.isdecimal(), s_super.isdigit(), s_super.isnumeric())
# Output: False True True

# 3. Vulgar Fractions ('½')
s_frac = "\u00bd"    # '½'
print(s_frac.isdecimal(), s_frac.isdigit(), s_frac.isnumeric())
# Output: False False True

6. Case Predicate Rules and Uncased Characters

The case testing methods (islower(), isupper(), and istitle()) ignore non-cased characters (digits, symbols, whitespace, punctuation), provided there is at least one cased character in the string:

# Non-cased characters are ignored if a cased letter exists
print("python_3.10!".islower())  # True ('p', 'y', 't', 'h', 'o', 'n' are lowercase)
print("HTTP_404_NOT_FOUND".isupper())  # True

# If NO cased characters exist, islower() and isupper() return False
print("12345!".islower())        # False
print("12345!".isupper())        # False
print("   ".islower())          # False

7. The Empty String Rule

A critical rule for PCAP candidates to memorize:

The Universal False Rule on Empty Strings: Every character classification method (isalpha(), isdigit(), isdecimal(), isnumeric(), isalnum(), isspace(), islower(), isupper(), istitle()) returns False when invoked on an empty string "".

Reason: All of these methods strictly require a string length of at least 1 character ($len \ge 1$) satisfying the condition.

(Exception: "".isprintable() evaluates to True).

Loading diagram...
String Inspection and Classification Decision Logic
Test Your Knowledge

What happens when executing the following Python code snippet?

text = "automation"
pos = text.find("xyz")
print(pos)

A
B
C
D
Test Your Knowledge

What is the output of the following occurrence count?

print("banana".count("ana"))

A
B
C
D
Test Your Knowledge

Consider the following expressions evaluated on empty and mixed strings:

print("".isalpha(), "123a".isdigit(), "Python 3".islower())
What is printed?

A
B
C
D
Test Your Knowledge

What exception, if any, is raised by the following code?

filename = "report.pdf"
print(filename.endswith([".pdf", ".docx"]))

A
B
C
D