Overview
This unit on String Handling introduces students to the concept, manipulation, and application of strings in programming. It covers how strings are represented, how to access and modify characters, and common operations such as concatenation, comparison, searching, slicing, and formatting. Students will learn built-in functions and methods typically available in high-level languages, and how to implement simple routines for tasks like counting characters, reversing strings, and validating input. Emphasis is placed on understanding immutability vs mutability, efficiency considerations for repeated operations, and using strings in practical contexts such as file processing and simple data validation. Mastery of strings is essential because text is a major form of data in programmes, user interfaces, and file input/output. By learning systematic ways to handle strings, students build problem-solving skills that apply across algorithms, debugging, and real-world tasks like preparing data for storage or display. The unit balances conceptual clarity with hands-on examples and practice questions similar to those in board examinations, preparing students for both theory and programming tasks.
Learning Objectives
- Explain what a string is and how it is stored in memory.
- Demonstrate how to create, access, and traverse strings using indexing and loops.
- Perform common string operations such as concatenation, comparison, and slicing.
- Use built-in functions and methods to search, replace, and format strings.
- Write programs to solve practical problems using string manipulation techniques.
- Differentiate between mutable and immutable types and explain implications for strings.
- Apply validation techniques to check input strings for patterns like digits or email-like formats.
- Analyze the time complexity of basic string operations and choose efficient approaches.
Topics in this chapter
18 topics · tap a topic title to jump straight to it.
Introduction to Strings
What is a string?
A string is a sequence of characters that together form text. This sequence can include letters, digits, punctuation marks, spaces and other symbols. In programming, strings are one of the basic data types used to store and manipulate text. They are fundamental for displaying messages, storing names, handling user input, and for working with files that contain readable text.
Character sets and encoding
Characters in a string are stored internally using a character set and encoding. ASCII is a simple set that covers common English characters. Unicode (for example UTF-8) is more general and can represent characters from many languages, including Hindi, symbols and emojis. Understanding encoding is important when reading and writing files and when exchanging text between systems.
Storage and representation
In memory, strings are typically stored as a sequence of bytes representing character codes. Some languages model strings as arrays of characters, letting programmers access each character with an index. Other languages treat strings as objects with methods for common operations. Implementation details differ: some languages store strings as immutable values, while others can use mutable character arrays. Knowing whether strings are immutable affects how you write code, especially when modifying or combining many strings.
Properties of strings
Important properties include length (number of characters), whether it is empty, and the ability to compare two strings. The length determines loops and indexing bounds. Empty strings (length zero) serve as defaults and must be handled in programs to avoid errors. Strings also have operations like concatenation (joining), slicing (extracting a part), and searching (finding a substring).
Why strings matter for students
Text is everywhere: messages, names, addresses, file contents, and simple data formats. Handling strings correctly is essential for creating user-friendly programs and for processing text files. Many programming exercises, projects and real-world applications depend on string handling: validating input, parsing data, producing formatted reports, and creating interactive interfaces. Learning string concepts early builds a foundation for more advanced topics such as parsing, data cleaning and regular expressions.
Common pitfalls for beginners
Beginners often make off-by-one indexing mistakes, forget to strip newline characters read from files, or try to modify immutable strings directly. Another common issue is not handling different character encodings which can cause text to display incorrectly. Practice with typical examples and careful attention to indexing and boundary conditions helps avoid these mistakes.
- A name stored as "Raj" is a string of length 3.
- An empty string is written as "" and has length 0.
- A sentence like "Hello, world!" includes letters, punctuation and a space.
- Quotes inside strings: "He said \"Hi\"" shows how to include a quote character.
- length(s) = number of characters in string s
- indexing: s[i] returns the (i+1)th character if indices start at 0
String Creation and Literals
String literals and variables
A string literal is text written directly in source code inside quotation marks. Depending on the programming language you may use single quotes (' '), double quotes (" ") or triple quotes (for multi-line text). A variable that holds a string simply refers to a location in memory that contains the character sequence. For example, name = "Anita" assigns the literal "Anita" to the variable name. Variables make it possible to re-use and manipulate text throughout a program.
Escape sequences
Some characters cannot be written directly inside quotes because they have special meaning or are invisible. Escape sequences allow you to include special characters: \n for newline, \t for tab, \\\\ for a backslash, and \" or \' for quotes inside a quoted string. Correct use of escape sequences ensures that the text contains the intended characters and that the program parses the literal correctly. For file paths in Windows, backslashes must be escaped or raw string notation used to avoid accidental escapes.
Raw and multi-line strings
For text that contains many backslashes (like regular expressions) or spans multiple lines (like a paragraph), languages often provide raw string literals or triple-quoted literals. Raw strings treat backslashes as ordinary characters, which makes patterns simpler to read. Multi-line literals preserve line breaks and spacing inside the literal, useful for large blocks of text or formatted messages.
Concatenation at creation
Some languages automatically join adjacent string literals at compilation time, letting you split long literals across lines for readability. At runtime, strings can be concatenated using operators like + or specific concat functions. While simple concatenation is easy, building a large string by repeatedly concatenating inside a loop is inefficient in many languages; a better technique for many parts is to collect pieces in a list and join them at the end.
Interpolated strings and templates
Many languages support string interpolation or template literals that embed expressions directly within the string. This makes building messages with variable values concise and readable: for example, "Name: {name}" or backtick templates in some languages. Interpolation handles conversion of non-string values and avoids many manual conversions and separators.
Literal forms for different needs
Choose the literal form that fits your need: use single or double quotes for simple strings, triple quotes for multi-line text, raw strings when backslashes matter, and formatted templates for combining data into readable output. Understanding the strengths of each form makes programs clearer and less error-prone.
- Create: greeting = "Good morning"
- Escape: path = "C:\\Users\\Name"
- Multi-line: note = """Line1 Line2"""
- Concatenate literals: "Hello," " world" gives "Hello, world" in some languages
- concatenate: s3 = s1 + s2
- repeat: s2 = s1 * n (language dependent)
Indexing and Slicing
Indexing explained
Indexing lets you access a single character in a string by its position. Most modern programming languages use zero-based indexing: the first character is at index 0, the second at index 1, and so on. If s is "India", then s[0] refers to 'I', s[1] to 'n', etc. Some languages also allow negative indices: s[-1] gives the last character, s[-2] the second last. Indexing is fundamental for tasks like checking a character or modifying a character in a mutable sequence.
Slicing for substrings
Slicing extracts a continuous part of a string, called a substring. Typical slice notation uses start and end indices with the start inclusive and the end exclusive. For example, s[1:4] returns characters at indices 1, 2 and 3. Omitting start or end implies the beginning or end of the string: s[:3] returns the first three characters, s[2:] returns from index 2 to the end. Slicing is a clean way to get prefixes, suffixes and internal parts without writing loops.
Index bounds and errors
Direct indexing with an out-of-range index often raises an error. Slicing usually handles out-of-range indices gracefully by adjusting to the available range and returning a shorter substring. Know the exact behaviour of your language to avoid runtime errors. Always check length before indexing or use safe slicing when bounds are uncertain.
Common uses
Slicing is used to extract file extensions, parse fixed-width data, and obtain parts of formatted values like dates. For example, given a date string "2025-04-01", year = s[:4], month = s[5:7]. Indexing is useful when checking a leading character or implementing simple parsers that process characters one by one.
Multi-step slicing and copying
Slicing often produces a new string (a copy) rather than a view, depending on language. This matters for performance and memory use: copying long substrings can be costly. Some languages provide view types or slicing that refers to the same buffer; these allow efficient subviews but require careful lifetime management.
Off-by-one errors and defensive coding
Off-by-one mistakes are common with indexing: remember that the end index in typical slice notation is not included. When writing loops with indices, ensure the loop limit and slice boundaries match intended ranges. Use helper functions for common tasks like last_char = s[-1] only if your language supports negative indices; otherwise compute last index as len(s)-1.
- Index: s = "India"; s[0] = 'I', s[2] = 'd'
- Slice: s[0:2] gives "In"; s[2:] gives "dia"
- Negative index: s[-1] gives 'a' (last character)
- Out-of-range slice: s[0:10] returns entire string without error in some languages
- slice(s, i, j) returns substring from index i to j-1
- first char: s[0]; last char: s[length(s)-1] or s[-1]
Traversal and Iteration
Basic traversal
Traversal means visiting each character in a string, one after another. This operation is performed with loops. A for-loop that yields each character directly is simple and readable: for ch in s: process ch. An index-based for-loop gives access to both index and character: for i in range(len(s)): ch = s[i]. Which style you choose depends on whether you need the position as well as the value.
Common tasks using traversal
Traversing is used to count character types (vowels, digits), to validate text (all characters are letters), to transform text (change case), and to build new strings (filtering or mapping characters). For instance, counting vowels requires checking each character and incrementing a counter when the character matches a vowel set. Reversing can be done by traversing from the end to the start and appending characters to a result.
Two-pointer technique
Some algorithms use two pointers: one from the start and one from the end, moving inward. This is efficient for tasks such as palindrome checks without creating a reversed copy. Compare s[i] and s[n-1-i] for i from 0 up to n//2. If any mismatch occurs, the string is not a palindrome. This method is O(n) time and uses constant extra memory.
Building new strings
When a program constructs a new string by appending many parts during traversal, naive repeated concatenation can be inefficient because each append may create a new string. Better practice is to collect parts in a mutable list or buffer during traversal and join them once at the end. This improves performance from quadratic to linear time for many languages.
Nested traversal and substring search
Searching for substrings manually can require nested loops: for each start position, check subsequent characters for a match. This naive approach can be O(n*m) in worst case where m is pattern length. Built-in search functions are usually optimized and preferable. For educational purposes, writing a simple nested-loop search helps understand the idea.
Error handling during traversal
Always ensure traversal respects boundaries: avoid modifying a string in-place while iterating over it in languages where mutation is allowed, because this can lead to unpredictable behaviour. Prefer iterating over a copy or using indices to control modifications safely.
- Count vowels by iterating each character and checking membership in a set of vowels
- Reverse by iterating from last index down to 0 and appending characters
- Validate numeric string by checking each character is between '0' and '9'
- for i in 0..length(s)-1: access s[i]
- reverse(s) = join(s[length-1] ... s[0])
Concatenation and Repetition
Concatenation basics
Concatenation means joining two or more strings end to end to form a longer string. Common operators include + or functions named concat. Concatenation is used to create messages from parts, such as combining a greeting and a name to produce "Hello, Sita". When concatenating a small number of strings, the syntax is simple and efficient enough for learning and many practical tasks.
Repetition
Some languages provide a repetition operator that repeats a string a fixed number of times. For example, '-' * 10 produces a line of 10 dashes useful for simple visual separators. Repetition is handy for padding, creating simple patterns, or constructing test data quickly.
Performance considerations
While concatenation is straight-forward, repeated concatenation inside loops is often costly in languages where strings are immutable. Each concatenation can copy the entire result so far, causing O(n^2) time when building a final string from many small pieces. An efficient pattern is to gather all pieces in a list and use a single join operation at the end. This approach copies each character once and achieves linear time relative to total output length.
Formatting vs manual concatenation
For combining text with non-string values like numbers, use formatting or interpolation features offered by the language. These handle conversion and spacing with clearer syntax than manual concatenation, reducing bugs and improving readability. For example, templates like "Age: {}".format(age) or similar constructs produce readable code that is easier to maintain.
Edge cases and separators
When concatenating many parts, be careful about missing or extra separators (spaces, commas). Decide on a consistent approach: either include separators in each piece or use a join with a separator. join is preferable because it centralizes control of separators and works efficiently with many parts.
Practical examples
Use concatenation for small message building and repetition for simple patterns; use buffers and join for assembly of large texts. Remember to consider readability and performance when choosing which method to use.
- concat: "Hello" + " " + "Sita" gives "Hello Sita"
- repeat: "-" * 10 yields "----------"
- format: template "Name: {}" with value "Rohit" gives "Name: Rohit"
- s_total = s1 + s2 + ... + sn
- s_repeat = repeat(s, n)
Comparison and Ordering
Equality and ordering
String comparison answers two questions: are two strings equal, and if not, which comes before the other in ordering? Equality checks whether both strings contain the same sequence of characters. Ordering, called lexicographic or dictionary order, compares character codes from left to right: the first differing character decides the result. If all compared characters are equal but lengths differ, the shorter string is considered smaller if it ends earlier.
Case sensitivity and normalization
By default many string comparisons are case-sensitive: "Apple" is not equal to "apple". For user-facing comparisons it is common to first normalize case by converting both strings to lowercase or uppercase. Additional normalization for Unicode can be necessary to treat visually identical characters the same way. For consistent results, remove extra spaces and normalize accents when required.
Application: sorting and searching
Comparison is central to sorting lists of strings such as names, city names and filenames. Sorting algorithms use pairwise comparisons to order elements. When searching in sorted lists, binary search relies on consistent ordering to find elements quickly in O(log n) time.
Locale and language rules
Lexicographic order can depend on locale: some languages have different rules for accents and letter order. For example, in some contexts 'Å' may be treated near 'A' or in separate positions. Many programming environments provide locale-aware comparison functions for correct local sorting. For ICSE-level exercises, lexicographic comparison by character code is usually sufficient unless the question specifies otherwise.
Practical issues
Trailing spaces or invisible control characters can make two otherwise identical strings compare unequal. Always trim strings and remove unexpected control characters before equality checks when inputs come from users or files. For keys in dictionaries or sets, consistent normalization prevents duplicate entries caused by minor differences like case.
Comparing length and content
Sometimes ordering should consider length before lexicographic order, e.g., arranging words by length. Make sure to follow question instructions: many exam problems specify the comparison method to use. When implementing comparisons in code, document whether comparisons are case-sensitive and what normalization is applied.
- "cat" < "dog" because 'c' < 'd'
- "apple" == "Apple" is false in case-sensitive compare; true if both lowercased
- Sort list ["Zee","Adam","bob"] to ["Adam","bob","Zee"] depending on case rules
- compare(s1,s2) returns -1,0,1 for less,equal,greater respectively
- equal(s1,s2) = (compare(s1,s2) == 0)
Searching and Matching
Basics of searching
Searching means finding whether a smaller string (pattern) occurs inside a larger string and, if so, where. Common built-in functions return the index of first occurrence or a negative value such as -1 if not found. Searches may be case-sensitive or insensitive depending on requirements. For quick checks, converting both strings to the same case simplifies case-insensitive searches.
Startswith and endswith
Many languages provide specific functions to test whether a string begins with a given prefix or ends with a suffix. These are faster and clearer than manual substring checks when you only need to test the beginning or ending. For example, checking whether a filename ends with ".txt" quickly identifies text files.
Counting occurrences
Counting how many times a substring appears is a common task. Some functions count non-overlapping occurrences, while others can be used in loops to detect overlapping matches. For example, the pattern "ana" in "banana" overlaps and careful handling is required if overlapping matches should be counted. Clarify whether overlaps are allowed before choosing the counting method.
Naive search vs optimized algorithms
The simplest search checks each index in the main string and tests whether the pattern matches starting at that index; this is easy to implement but can be slow for large inputs. Efficient algorithms such as Knuth-Morris-Pratt (KMP) preprocess the pattern to avoid redundant comparisons and achieve linear time, which is important for large datasets. For Class 10, built-in search functions are sufficient, but understanding that better algorithms exist is useful.
Practical tips
For reliable searches, trim input, normalize case, and be aware of special characters. When searching for literal characters that have special meaning in other contexts (for example in regular expressions), either escape them or use functions that treat the search string as plain text. Remember that searching in very large text may require streaming and incremental processing rather than loading all data into memory.
Search return values and error handling
Some functions return a special value like -1 if not found, while others raise exceptions. Use conditional checks and handle absent matches gracefully to avoid crashes. When a position is returned, validate it before slicing to avoid index errors.
- find "an" in "banana" returns first index 1
- count occurrences of "ana" in "banana" can be 1 or 2 depending on overlap handling
- check if filename endswith ".txt" to identify text files
- find(s, p) returns smallest i such that s[i:i+len(p)] == p or -1 if none
- count(s, p) = number of (possibly non-overlapping) occurrences of p in s
Replacement and Editing
Replacing substrings
Replacement functions create a new string where occurrences of a given substring are replaced by another substring. Many languages allow specifying whether to replace only the first occurrence or all occurrences. Because strings are often immutable, replace returns a new string leaving the original unchanged. Replacement is useful for correcting common typos, sanitizing input, or applying simple transformations like replacing tabs with spaces.
Insert and delete operations
To insert text into a string, slice the original into left and right parts around the insertion point and join left + insert + right. To delete a substring, slice out the unwanted portion and join the remaining parts. These operations are straightforward but may be inefficient for many repeated edits; for many edits work on a mutable buffer or list and convert back to a string at the end.
Trimming whitespace
Removing leading and trailing whitespace (spaces,tabs,newlines) is commonly done with functions named strip, lstrip, and rstrip. Trimming normalises user input and avoids mismatch problems in comparisons and parsing. For instance, user-supplied names frequently include accidental spaces that should be trimmed before comparison or storage.
Case conversion
Converting to uppercase or lowercase is a common editing step for normalization. Title-case and capitalize functions are useful for display. When converting case, remember that Unicode case-mapping can be complex for some alphabets; for Class 10 tasks basic ASCII-based conversions are sufficient.
Replacing with patterns
For more flexible replacements based on patterns (for example, replacing all sequences of digits with a placeholder) regular expressions are used. Regular expressions allow complex matching and replacement rules, but simple replace functions are easier and faster for fixed substrings. Understand which tool matches the task: simple textual replacement or pattern-based transformation.
Performance and safety
When editing very large strings or performing many replacements, prefer methods that avoid repeated copying: operate on lists of pieces, use efficient libraries, or use streaming transforms. Also validate output to avoid accidental corruption when replacements change structure, for example altering CSV separators inside quoted fields. Test replacement on representative inputs before applying to real data.
- replace "cat" with "dog" in "catapult cat" -> "dogapult dog"
- delete middle: new = s[:i] + s[j:] removes substring s[i:j]
- strip: " hi \n" strip -> "hi"
- replace(s, old, new, count) returns s with up to count replacements
- insert: s[:i] + t + s[i:]
Splitting and Joining
Splitting to tokens
Splitting a string breaks it into a list of substrings, or tokens, using a delimiter such as a space or comma. This operation converts a flat text line into separate meaningful pieces useful for parsing data. For example, splitting a CSV line by commas gives individual fields. Many languages offer a default split by whitespace that also collapses multiple spaces into single separators, which is convenient for user input.
Handling empty tokens and separators
When delimiters are adjacent or a line begins/ends with a delimiter, split can produce empty tokens. For CSV parsing, empty tokens often represent missing fields and should be handled accordingly. Some split functions offer options to keep or discard empty tokens; choose the option that matches the expected data format.
Joining tokens
Joining is the inverse operation: given a list of strings, combine them into a single string using a chosen separator. The join operation is efficient and preferred when assembling a large string from many parts. Using join centralises the separator choice and produces consistent output formatting for things like CSV lines or human-readable lists.
Common workflow
A typical processing pipeline reads a line, strips trailing newline, splits into tokens, processes tokens (trim, convert types), and optionally joins processed tokens to write back. This flow appears often in file processing and simple parsers. Proper trimming of tokens avoids subtle bugs when tokens include stray spaces.
Practical parsing considerations
Simple split works for straightforward formats, but CSV files with quoted fields containing delimiters require more robust parsing libraries. Similarly, splitting on fixed-width fields requires slicing rather than split. When writing code for exams, explain assumptions such as no embedded delimiters or that fields are trimmed.
Efficiency
Splitting a string of length n into k tokens typically costs O(n) time to scan the whole string. Joining k tokens into a single string uses time proportional to the total output length. Use built-in split and join for clarity and performance; avoid manual token assembly with repeated concatenation when many tokens are involved.
- split: "one,two,three" by ',' -> ["one","two","three"]
- join: join(["a","b","c"], ":") -> "a:b:c"
- split default: " a b " split -> ["a","b"]
- tokens = split(s, delim)
- s = join(tokens, sep)
Immutable vs Mutable Strings
Immutability explained
An immutable string cannot be changed after it has been created. Any operation that alters a string, such as replace or concatenation, returns a new string and leaves the original untouched. Many high-level languages use immutable strings because they are safer: immutable objects can be freely shared without worrying that one part of a program will change them unexpectedly.
Mutable alternatives and why they exist
Mutable sequence types such as character arrays, buffers, or string builders allow in-place changes. These types are provided because modifying text frequently by inserting, deleting or repeatedly appending is inefficient with immutable strings: each modification may require copying the entire string to make a new one. Mutable buffers let programs perform many edits and then produce a final string once.
When to prefer immutable strings
Use immutable strings when changes are few or when safety and simplicity matter. Immutability avoids side effects and makes reasoning about code easier. For tasks like comparing strings, reading data, or formatting output where few modifications happen, immutable strings are convenient.
When to prefer mutable buffers
Use mutable buffers when building large strings from many small parts, such as assembling the contents of a large file, or when performing many local edits. The typical efficient pattern is: append parts to a buffer or list during processing, then join or convert the buffer to an immutable string once at the end. This reduces time complexity and memory churn significantly.
Performance trade-offs
Naive repeated concatenation of an immutable string in a loop can produce O(n^2) time behaviour where n is the number of appended characters. The buffer-and-join approach yields O(n) time. Be mindful of memory use: mutable buffers hold intermediate data but avoid creating many temporary strings; choose the option suitable for the input size and operation count.
Practical coding advice
When writing solutions for exams or small programs, use simple code for clarity. When performance is a concern (large inputs or frequent edits), explain use of a buffer or builder and show the improved approach. Document assumptions about mutability if your language has both string and mutable types.
- Immutable: s = s + "a" in a loop creates many temporary strings
- Mutable: use list append and join to build string efficiently
- String builder: append parts then convert to string once
- cost of n appends: O(n^2) for naive immutable appends; O(n) with buffer and join
Formatting and Templates
Purpose of formatting
Formatting transforms raw values into readable text. When a program outputs numbers, dates or combined fields, formatting ensures consistent width, precision and alignment. Templates let you place placeholders in a string that are replaced by values at runtime—this keeps output code clean and readable compared to manual concatenation and string conversions.
Placeholder formats
Simple templates use placeholders like {} or %s, which are substituted with values. Advanced formatting allows specifying field width, alignment (left, right, center) and numeric precision. For example, formatting a float to two decimal places produces consistent monetary or measurement displays. Using named placeholders can make templates self-documenting and reduce errors when parameters change order.
Aligning columns
When printing tables or lists, aligning columns improves readability. Format specifications allow you to allocate fixed widths and align content within those fields. For example, using a width of 10 for names and a width of 5 for scores aligns columns neatly in console output or simple reports.
Formatting numbers and dates
Formatting is not only for strings: numbers and dates require specific formats. For numbers, control decimal places, sign and padding. For dates, present day, month and year in a chosen order and optionally zero-pad values. Many languages provide libraries for date/time formatting that produce locale-aware representations, but for Class 10 tasks basic numeric and date formatting using templates is usually sufficient.
Templates with type conversion
Formatting mechanisms often handle type conversion automatically: inserting an integer or float into a template converts it to a string representation according to the specified format. This avoids manual conversion and concatenation, and reduces errors caused by incorrect spacing or type mismatches. For example, a template can specify that an inserted float is shown with two decimals while an integer appears as-is.
Using format for localization
Templates can be combined with locale settings to display numbers with appropriate decimal separators or digit grouping. While locale-aware formatting is advanced, be aware that different regions display numbers and dates differently. For exam programs focus on consistent formatting rather than full localization unless specifically asked.
Security and safe templating
Avoid constructing templates by inserting unchecked user input into format strings that might be interpreted. Use parameterised formatting functions that separate template and data to prevent unexpected evaluation. Most modern template methods safely substitute values without executing them as code.
Readable code and documentation
Format strings make code easier to read and maintain. When answering exam questions, show both the template and the output that results for given inputs. Explain format specifiers used (width, alignment, precision) so the examiner understands the intended output layout.
Practical examples
Use formatting to create clear messages such as "Name: {name}, Score: {score:.2f}" where score is shown with two decimals. For tabular output, show headers and use fixed-width fields. Practice several examples to become comfortable with specifiers and their effects on final output.
- Template: "Name: {} Age: {}".format("Asha", 14) -> "Name: Asha Age: 14"
- Alignment: format number in width 5 with leading spaces
- Precision: display 3.14159 as 3.14 with 2 decimal places
- template.format(v1, v2, ...)
- format(number, width, precision) controls display
Parsing and Validation
Parsing structured text
Parsing means breaking down a string into meaningful components and converting those components into appropriate types. For example, a date string "2025-04-01" is parsed into year=2025, month=4, day=1 by slicing or splitting and converting tokens to integers. Parsing requires clear rules about separators, expected token formats and how to handle missing values. Always validate tokens before conversion to avoid runtime errors.
Validation rules and techniques
Validation checks whether a string meets expected conditions: correct length, allowed characters, presence of required separators, and so on. Simple checks use character classification functions like isdigit or isalpha, length checks, and split-based verification. For instance, a mobile number can be validated by ensuring it has exactly 10 characters and all are digits. For more flexible patterns, regular expressions are used, but basic checks are sufficient for many school-level tasks.
Error handling and user feedback
When input fails validation, programs should handle the situation gracefully: prompt the user again, provide clear error messages, use default values, or abort with a helpful message. Avoid letting parsing errors cause program crashes. In file processing, record malformed lines for review rather than stopping the whole process.
Robust parsing strategies
Defensive parsing includes trimming whitespace, normalizing case, and handling optional fields. Use try-except (or equivalent) blocks around conversions like string-to-integer to catch and manage conversion errors. When parsing many records, count and report errors so that issues can be fixed systematically.
Parsing real formats
Simple CSV-like lines can be parsed by splitting on commas and trimming tokens. However, full CSV support with quoted fields and embedded commas needs a dedicated parser. For tasks that involve known simple formats, write parsing code that documents assumptions: for example, assume no embedded commas and stable field counts. These assumptions should be stated in answers for exams if relevant.
Practical exercises
Practice parsing phone numbers, roll numbers, dates and simple configuration lines. Validate before converting and show clear error messages when validation fails. This practice builds confidence in writing reliable input-processing code used both in exams and real projects.
- Parse date: parts = s.split('-'); year = int(parts[0])
- Validate digits: all(c.isdigit() for c in s) checks numeric string
- Check email-like string contains exactly one '@' and non-empty local and domain parts
- is_numeric(s) = True if every character in s is a digit
- parse(s, delim) = map parts using split(s, delim)
Regular Expressions: Basic Idea
What are regular expressions?
Regular expressions (often called regex) are compact patterns used to describe sets of strings. They let you match flexible criteria such as digit sequences, optional parts, or choice between alternatives. Regex is powerful for validations and searches where plain substring functions are not enough. For Class 10, learn the basic ideas and a few simple patterns rather than every advanced feature.
Core building blocks
Key elements include literal characters (match themselves), character classes like [0-9] for digits or [a-zA-Z] for letters, and shorthand classes such as \d for digits and \w for word characters in many engines. The dot . matches any single character except newline in many settings. Quantifiers specify repetition: + means one or more, * means zero or more, and ? means zero or one. Curly braces like {n} specify exact counts or ranges, for example {2,4} means between two and four times.
Anchors and groups
Anchors ^ and $ match the start and end of a string, respectively. Using ^ and $ ensures the whole string matches a pattern, which is useful for validation: ^[0-9]{10}$ requires exactly ten digits. Parentheses group parts of a pattern for repetition or extraction and may capture matched text for later use. Alternation uses | to indicate choice between patterns, for example "cat|dog" matches either "cat" or "dog".
Simple practical patterns
For many tasks a small set of patterns is enough: ^[0-9]+$ to check that a string contains only digits; ^[A-Za-z]+$ to check only letters; ^[^@]+@[^@]+\.[^@]+$ as a very simple email-like check (note: this is not fully RFC compliant but useful for basic validation). Use these patterns carefully and test them on several inputs.
When to use and when not to
Regular expressions are excellent for compact checks and complex matches. However, they are not a good fit for parsing deeply nested or highly structured languages such as full HTML or programming languages; proper parsers are needed there. Also complex regex can be hard to read — prefer simple, well-documented patterns for exams.
Safety and engine differences
Regex syntax and features differ slightly between languages and tools. Always test patterns in your environment. Overly general or careless patterns can accept invalid input or reject valid input; test with typical and edge-case examples. For school work, focus on basic, clear patterns and explain assumptions when presenting regex solutions.
- Pattern for digits: "^[0-9]+$" checks if a string has only digits
- Email-like simple check: "^[^@]+@[^@]+\.[^@]+$" (basic and not fully RFC compliant)
- Find repeated word: use pattern "\b(\w+)\b.*\b\1\b" in advanced engines
- regex ^ and $ to anchor start and end
- {n} repeats exactly n times; + means 1 or more; * means 0 or more
String Algorithms: Reverse and Palindrome
Reversing a string
Reversal produces a new string with characters in the opposite order. A simple algorithm iterates from the last character to the first, appending characters to a result. Reversal can also be done by slicing if the language supports it, for example s[::-1] in some languages returns the reversed string. Understanding reversal helps with small algorithms and is often asked in exams as a basic programming exercise.
Algorithmic approaches
Two common methods are: (1) build a new string by iterating from end to start and appending; (2) convert to a mutable sequence, reverse in-place and convert back. The in-place method is more memory efficient for large strings if mutable buffers are available. Time complexity for both approaches is O(n), where n is the string length, because each character is read once.
Palindrome checking
A palindrome reads the same forwards and backwards. A direct check compares the string to its reverse. For an efficient method, use the two-pointer technique: compare characters at positions i and n-1-i while moving i from 0 to n//2; if all pairs match the string is a palindrome. This method avoids creating a reversed copy and uses O(1) extra memory.
Normalization before checking
Often palindromes are defined ignoring case and non-letter characters. Pre-process the string by removing spaces and punctuation and converting to a single case before checking. For example, "A man, a plan, a canal: Panama" becomes "amanaplanacanalpanama" and is a palindrome after normalization.
Edge cases and tests
Test algorithms with empty strings, single-character strings and strings with even and odd lengths. Ensure your code handles Unicode characters if needed and documents any assumptions such as ignoring punctuation. For exam answers, explain the method and show sample runs with given inputs to demonstrate correctness.
Applications and extensions
Reversal is used in text processing, simple encodings and puzzle solutions. Palindrome checks appear in algorithmic problems and can extend to words and phrases. Understanding these simple algorithms builds confidence for larger string manipulation tasks.
- Reverse: "ABCD" -> "DCBA" by iterating from index 3 to 0
- Palindrome: "level" equals its reverse, so it is a palindrome
- Two-pointer check comparing s[i] and s[n-1-i] for i from 0 to n/2
- reverse(s) = s[n-1] s[n-2] ... s[0]
- palindrome(s) if s == reverse(s) after normalization
String and File I/O
Files store text as strings
Text files contain sequences of characters organized into lines. Reading a text file retrieves strings representing lines or the whole content. Writing requires converting values into strings and storing them into a file. Typical operations are: open a file, read or write, and close the file. Many languages provide context managers (for example with) to ensure files are closed even if an error occurs.
Reading modes and line endings
Files may be read in text mode or binary mode. In text mode the system translates line-ending conventions between platforms; in binary mode raw bytes are returned. Lines end with newline characters which may be included when reading; use strip to remove trailing newline characters before further processing. When reading, choose whether to read all content at once (read()) or to iterate line by line which is more memory-friendly for large files.
Parsing file lines
Each line read from a file is a string that often needs parsing: splitting into fields, trimming whitespace, and converting to numbers. When processing structured data like CSV, use proper parsing methods to handle quoted fields and embedded delimiters, or assume simplified formats for classroom exercises and document these assumptions.
Writing and formatting output
When writing, format strings properly for readability. Use join to assemble multiple lines efficiently and write them with newline separators. Include headers or column alignment for reports. Be careful with file modes: 'w' overwrites, 'a' appends. Choose the correct mode based on desired behaviour.
Encoding and compatibility
Always know the encoding used when reading or writing text files. UTF-8 is common and supports many languages. Reading a file with the wrong encoding can produce errors or incorrect characters. When exchanging files between systems, be explicit about encoding to avoid mismatches.
Processing large files
For very large files, read one line at a time and process it immediately to keep memory usage low. Use streaming patterns: open file, iterate lines, process and possibly write results incrementally. Handle exceptions such as missing files, permission errors and corrupted lines gracefully so that programs do not crash unexpectedly.
- Open 'data.txt', read lines, for each line strip and split into tokens
- Write results: join output lines with newline and write to output file
- Process large file by iterating over file object rather than reading whole content
- readlines(file) -> list of line strings
- write(file, s) writes string s to file
Common Built-in Functions and Methods
Useful built-ins
Programming languages offer many built-in functions and methods for strings. Common ones include length (len or length), find or index (to locate substrings), replace, split, join, strip, lower and upper for case conversion, isdigit/isalpha for character checks, and format for templating. Learning these functions speeds up programming and reduces the chance of bugs compared to writing low-level loops for every task.
Return types and behaviour
Each method has a specific return type and behaviour: some return new strings, others return integers or lists. For example, split returns a list of strings while find returns an index number or -1. Some methods raise exceptions on error, while others return sentinel values; check documentation. Understanding exact return behaviour helps handle edge cases correctly in programs.
Chaining methods
You can combine methods for concise code: s.strip().lower() both trims whitespace and normalizes case. Chaining is powerful but be mindful of readability: overly long chains can confuse readers. Use intermediate variables or comments if a chain performs several distinct logical steps.
Examples of method usage
Use strip to clean user input, split to tokenize sentences, join to assemble outputs, and format to build readable messages. Use isdigit to check validity before converting strings to integers. Use replace for simple substitutions. Combining these functions covers many typical programming tasks involving text.
Testing and exceptions
Test functions on typical and boundary inputs: empty strings, strings with only whitespace, very long strings and strings containing unusual characters. Handle exceptions such as index errors or value errors raised by conversions. For production code always validate inputs before converting types.
Practical advice for exams
In answers, mention the method names and expected results clearly. When asked to write small programs, prefer using built-ins for clarity and brevity. Demonstrate understanding by giving sample inputs and the output produced by method calls.
- s.strip().lower() to normalize input before comparison
- pos = s.find('abc'); if pos != -1 then found
- tokens = s.split(','); result = ':'.join(tokens)
- s.method(args) denotes calling a string method on s
- len(s) returns integer length
Performance and Complexity
Time complexity basics
Many basic string operations run in linear time O(n) relative to the length of the string n: computing length, scanning characters, finding a substring with simple search, and creating a copy through slicing typically require inspecting each character. Knowing these costs helps you write code that behaves well on larger inputs.
Costly patterns to avoid
A common performance pitfall is repeated concatenation within a loop: repeatedly doing s = s + part inside a loop may copy the growing string each time, leading to quadratic time O(n^2) in the number of characters processed. Similarly, creating many small temporary strings by repeated slicing and concatenation can increase both time and memory use. Instead, accumulate parts in a list and use a single join operation at the end to achieve linear time O(n).
Slicing and copying
Slicing often produces a copy of the requested substring. For a one-time slice this cost is acceptable, but repeated slicing inside nested operations can become expensive. Some languages provide views or substring references that avoid copying, but these are language-specific. When performance matters, learn whether slicing creates copies in your language and choose data structures accordingly.
Search algorithms
Naive substring search is O(n*m) in worst case where n is length of text and m is pattern length. Optimized algorithms like KMP or library implementations achieve O(n + m) time and are helpful for large-scale searches. For ICSE-level tasks built-ins are usually fine, but be aware that algorithm choice matters for big inputs.
Memory considerations
Copying large strings can increase memory usage significantly. For processing large files, prefer streaming line-by-line processing rather than loading entire content. Use generators or iterators where available to process sequences without creating large intermediate lists.
Practical heuristics
Use built-in, optimized string functions when available. For building large strings from many parts, buffer-and-join is the standard efficient technique. When performance is a concern, measure with representative inputs or explain algorithmic complexity in answers to show awareness of scaling behaviour.
- Inefficient: s = ""; for part in parts: s += part # can be O(n^2)
- Efficient: s = ''.join(parts) # O(n)
- Process huge file line by line to limit memory usage
- Appending n pieces by repeated concat: O(n^2) in worst case
- Joining list of total length N: O(N)
Practical Examples and Programs
Bringing ideas together
This topic shows how earlier concepts form complete programs. Typical small tasks include counting vowels and consonants, finding the longest word in a sentence, checking whether a given string is a palindrome, extracting file extensions, and parsing simple CSV lines. Each program follows a clear sequence: read input, clean it, parse or split as needed, apply the main logic using traversal or built-ins, and produce formatted output.
Step-by-step problem solving
For every problem, begin by writing the steps in plain language: what the input is, what the desired output should be, and any special cases to handle. Translate steps into code using functions like split, strip, find, and join. For example, to find the longest word: strip the sentence, split by spaces, then traverse tokens to track maximum length. Show sample inputs and expected outputs to verify correctness.
Testing and edge cases
For reliable programs test boundary cases: empty input, strings of length one, strings with only spaces or punctuation, and maximum expected lengths. For parsing tasks that rely on delimiters, test lines with missing fields, extra separators, and leading/trailing separators. Handle invalid input gracefully by returning clear messages or default behaviour.
Example program patterns
Common patterns include: normalize input with strip() and lower() before comparison, use split() to obtain tokens, use list accumulators and join() for efficient construction of output, and validate tokens before converting to numbers. For file processing, read line by line and apply the same single-line logic for each record.
Documenting and commenting
In exam answers and project code include brief comments explaining each major step: input handling, parsing, main loop and output. This clarity helps examiners follow reasoning and makes debugging easier. When showing sample runs, include both input and output and explain why the program produces the output.
Sample problems to practice
Practice problems include: count vowels and consonants, reverse a string, check for palindrome, extract usernames from email-like strings, find most frequent word, and validate phone numbers. These exercises reinforce indexing, splitting, traversal, and use of built-ins and prepare students for the kinds of questions that appear in ICSE exams.
- Count vowels: iterate and increment counter if character in set {a,e,i,o,u,A,E,I,O,U}
- Longest word: split sentence into words and track maximum length word
- File extension: if '.' in filename -> ext = filename.split('.')[-1]
- vowel_count(s) = sum(1 for c in s if c.lower() in 'aeiou')
- longest_word(s) = argmax_word length(word) for word in split(s)
Key Concepts
- String
- A sequence of characters used to represent text.
- Literal
- Text written directly in source code inside quotes representing a string value.
- Indexing
- Accessing a character at a specific position within a string.
- Slicing
- Extracting a substring by specifying start and end positions.
- Concatenation
- Joining two or more strings end-to-end to form a new string.
- Immutability
- Property of strings that prevents modification of the original object; operations create new strings.
- Split
- Dividing a string into a list of substrings using a delimiter.
- Join
- Combining a list of strings into one string with a chosen separator.
- Trim/Strip
- Removing leading and trailing whitespace from a string.
- Substring
- A contiguous sequence of characters within a larger string.
- Lexicographic order
- Dictionary-like ordering of strings based on character codes.
- Regular expression
- A compact pattern language to describe sets of strings for searching and validation.
- Palindrome
- A string that reads the same forwards and backwards after optional normalization.
- Buffer/Builder
- A mutable structure used to build strings efficiently by avoiding repeated copying.
- Encoding
- The scheme that maps characters to bytes, for example UTF-8 or ASCII.
Practice Questions
-
What is a string? Give two examples. / स्ट्रिंग क्या है? दो उदाहरण दीजिए।
Show answer
A string is a sequence of characters used to represent text, for example "Hello" and "12345". / एक स्ट्रिंग वर्णों का अनुक्रम होती है जिसका उपयोग पाठ को दर्शाने के लिए किया जाता है; उदाहरण के लिए "Hello" और "12345"।
-
How do you get the length of a string and the last character using index? / किसी स्ट्रिंग की लंबाई और अंतिम अक्षर को इंडेक्स से कैसे प्राप्त करते हैं?
Show answer
Use a length function len(s) to get length n, then last character is s[n-1] or s[-1] in languages that support negative indices. / len(s) से लंबाई n मापें, फिर अंतिम अक्षर s[n-1] होगा या जहाँ भाषा नेगेटिव इंडेक्स समर्थन करे वहां s[-1]।
-
Write steps to check whether a given string is a palindrome (case-insensitive). / दिया गया स्ट्रिंग पेलिन्ड्रोम है या नहीं (केस-इन्सेंसिटिव) जांचने के चरण लिखिए।
Show answer
Convert string to one case and remove non-alphanumeric characters, then compare the string to its reverse; if equal it is a palindrome. / स्ट्रिंग को एक ही केस में बदलें और गैर-आल्फान्यूमेरिक अक्षरों को हटाएं, फिर उसे उसके रिवर्स से मिलाकर देखें; यदि बराबर हो तो वह पेलिन्ड्रोम है।
-
Explain why repeated concatenation in a loop can be inefficient and give a better approach. / लूप में बार-बार संयोजन (concatenation) करना असमर्थक क्यों हो सकता है और एक बेहतर तरीका बताइए।
Show answer
Repeated concatenation may create many intermediate strings leading to O(n^2) time. A better approach is to collect parts in a list or buffer and join once, giving O(n) time. / बार-बार संयोजन कई मध्यवर्ती स्ट्रिंग बनाता है जिससे O(n^2) समय लग सकता है. बेहतर तरीका यह है कि भागों को सूची या बफर में इकट्ठा करें और अंत में एक बार join करें, जिससे O(n) समय मिलता है।
-
Given s = "banana", what is s[1:4] and how many times does substring "an" appear? / यदि s = "banana" है, तो s[1:4] क्या है और उपस्ट्रिंग "an" कितनी बार आती है?
Show answer
s[1:4] is "ana" (characters at indices 1,2,3). The substring "an" appears twice: at positions 1 and 3 (overlapping). / s[1:4] "ana" है (इंडेक्स 1,2,3 के अक्षर). उपस्ट्रिंग "an" दो बार आती है: स्थान 1 और 3 पर (ओवरलैपिंग)।
-
How do you extract file extension from "report.pdf"? / "report.pdf" से फ़ाइल एक्सटेंशन कैसे निकालेंगे?
Show answer
Split the filename by '.' and take the last part: extension = filename.split('.')[-1], giving "pdf". / फ़ाइलनाम को '.' से विभाजित करें और अंतिम भाग लें: extension = filename.split('.')[-1], जो "pdf" होगा।
-
Describe a method to count vowels in a sentence. / किसी वाक्य में स्वर (vowels) गिनने की विधि बताइए।
Show answer
Iterate over each character, convert to lowercase, and increment a counter if the character is in the set {a,e,i,o,u}. Return the counter. / प्रत्येक अक्षर पर जाएँ, उसे लोअरकेस में बदलें और अगर वह {a,e,i,o,u} में है तो काउंटर बढ़ाएँ। काउंटर लौटाएँ।
-
What does strip() do and why is it useful before comparison? / strip() क्या करता है और तुलना से पहले यह उपयोगी क्यों है?
Show answer
strip() removes leading and trailing whitespace (spaces, tabs, newlines). It is useful to avoid false mismatches caused by accidental spaces around input. / strip() अग्रसर और पश्च भाग के whitespace (स्पेस, टैब, न्यूलाइन) हटाता है। यह इनपुट के चारों ओर अनजाने स्पेस के कारण गलत असमानताओं से बचाता है।
-
Give a simple regular expression to check a 10-digit phone number. / 10-अंकीय फ़ोन नंबर जांचने के लिए साधारण नियमित अभिव्यक्ति (regex) दीजिए।
Show answer
A basic pattern is ^[0-9]{10}$ which requires exactly 10 digits and nothing else. / एक साधारण पैटर्न ^[0-9]{10}$ है जो केवल 10 अंक और कुछ नहीं चाहिए।
-
Explain how to safely read a large text file and count lines containing the word "error" (case-insensitive). / बड़े टेक्स्ट फ़ाइल को सुरक्षित रूप से पढ़कर "error" शब्द (केस-इन्सेंसिटिव) वाली पंक्तियों की संख्या कैसे गिनेंगे समझाइए।
Show answer
Open the file in read mode and iterate line by line to avoid loading whole file. For each line, convert to lowercase and check if "error" is a substring; increment a counter when it is. Close the file or use a with/try block to ensure closure. / फ़ाइल को पढ़ने के लिए खोलें और लाइन दर लाइन पढ़ें ताकि पूरी फ़ाइल मेमोरी में न आएँ। हर पंक्ति को लोअरकेस में बदलें और जांचें कि "error" उपस्ट्रिंग है या नहीं; यदि है तो काउंटर बढ़ाएँ। फ़ाइल बंद करने के लिए with या try ब्लॉक का उपयोग करें।
-
Write steps to split a CSV line "apple, banana, cherry" into clean tokens. / "apple, banana, cherry" CSV लाइन को साफ टोकन में विभाजित करने के चरण लिखिए।
Show answer
Split by comma into tokens, then strip whitespace from each token: tokens = [t.strip() for t in line.split(',')] resulting in ["apple","banana","cherry"]. / कॉमा से विभाजित करें और फिर प्रत्येक टोकन से whitespace हटाएँ: tokens = [t.strip() for t in line.split(',')] जिससे ["apple","banana","cherry"] प्राप्त होंगे।
Related Laws & Principles
Explore allFoundational laws & principles connected to this chapter — tap to open in the Laws Explorer.