Regular expressions are an essential tool for validating and extracting text, from checking email formats to parsing structured data out of a larger string.
This collection of 30 C# regex exercises covers the System.Text.RegularExpressions namespace, using Regex.IsMatch(), Regex.Match(), and Regex.Replace() to solve real, practical pattern-matching problems.
Each exercise includes a Practice Problem, Purpose, Hint, and a full Solution with Explanation, so you build confidence reading and writing regex patterns.
Also, See: C# Exercises with 22 topic-wise sets and 620+ practice questions.
What You’ll Practice
- Fundamentals:
Regex.IsMatch(),Regex.Match(), andRegex.Matches(). - Validation: Email, phone number, and password pattern checks.
- Extraction: Pulling structured data out of unstructured text.
- Replacement:
Regex.Replace()for text cleanup and formatting.
+ Table Of Contents (30 Exercises)
Table of contents
- Exercise 1: Exactly 5 Digits
- Exercise 2: Alphabetic Words
- Exercise 3: Starts and Ends with Specified Letters
- Exercise 4: Valid Whole Numbers
- Exercise 5: Whitespace Cleaner
- Exercise 6: Hexadecimal Color Codes
- Exercise 7: Extracting Words Starting with a Vowel
- Exercise 8: Finding Isolated Symbols
- Exercise 9: Simple Variable Names
- Exercise 10: Stripping Punctuation
- Exercise 11: Basic Email Validation
- Exercise 12: Date Format Converter (MM/DD/YYYY)
- Exercise 13: Extraction of href Attributes
- Exercise 14: North American Phone Numbers
- Exercise 15: Extracting Domain Names
- Exercise 16: Duplicate Word Finder
- Exercise 17: Numeric Values with Optional Decimals
- Exercise 18: Extracting Log Severity Levels
- Exercise 19: Password Strength Requirement
- Exercise 20: Matching Content Inside Quotes
- Exercise 21: Extracting Prices Preceded by Currency Text
- Exercise 22: Stripping HTML Tags Safely
- Exercise 23: Parsing Key-Value Configurations
- Exercise 24: Validating IPv4 Addresses
- Exercise 25: Negative Lookahead Filtering
- Exercise 26: Extracting MAC Addresses
- Exercise 27: Non-Backtracking Verification
- Exercise 28: Splitting CamelCase Words
- Exercise 29: Parsing C# Comments
- Exercise 30: Credit Card Number Masking
Exercise 1: Exactly 5 Digits
Practice Problem: Write a program that uses a regular expression to match a string that consists entirely of exactly 5 numeric digits.
Purpose: This exercise helps you practice anchoring a pattern to the start and end of a string with ^ and $, combined with a quantifier that specifies an exact repeat count.
Given Input: input = "12345"
Expected Output: True
▼ Hint
- Anchor the pattern with
^at the start and$at the end, so the match must cover the entire string. - Use
\dto match a single digit character. - Follow
\dwith{5}to require exactly five repetitions. - Pass the pattern and the input string to
Regex.IsMatchto get a true or false result.
▼ Solution & Explanation
Explanation:
^and$: Anchor the pattern to the very start and very end of the string, so partial matches inside a longer string don’t count.\d: A shorthand character class matching any single digit, 0 through 9.{5}: A quantifier requiring the preceding element,\d, to repeat exactly five times.Regex.IsMatch(input, pattern): Returns true only if the entire input satisfies the pattern from start to end, thanks to the anchors.
Exercise 2: Alphabetic Words
Practice Problem: Find all words in a sentence that contain only uppercase or lowercase letters, with no numbers or symbols mixed in.
Purpose: This exercise helps you practice using Regex.Matches to retrieve every occurrence of a pattern within a larger string, rather than just checking the whole string once.
Given Input: sentence = "Hello, user123!"
Expected Output: Hello
▼ Hint
- Use
\bto mark a word boundary at the start and end of each match. - Use a character class like
[A-Za-z]+to match one or more letters in a row. - Call
Regex.Matches(input, pattern)to get every matching substring in the input, not just the first one. - Loop through the resulting collection and print each match’s
Value.
▼ Solution & Explanation
Explanation:
\b[A-Za-z]+\b: Matches a sequence of one or more letters bounded by word boundaries on both sides, so it won’t match a letter run that’s part of a mixed word like “user123”.Regex.Matches(sentence, pattern): Scans the entire input string and returns every non-overlapping match it finds, packaged as aMatchCollection.foreach (Match match in matches): Iterates over each match found, since there could be zero, one, or several in a given string.match.Value: Extracts the actual matched text from eachMatchobject.
Exercise 3: Starts and Ends with Specified Letters
Practice Problem: Match a 5-letter word that starts with a and ends with e.
Purpose: This exercise helps you practice combining anchors, literal characters, and a fixed-count wildcard to constrain both the length and the specific starting and ending letters of a match.
Given Input: words = ["apple", "awake", "table"]
Expected Output:
apple: True awake: True table: False
▼ Hint
- Anchor the pattern with
^and$so the entire word must match, not just part of it. - Match the literal letter
aright after the starting anchor. - Use
.repeated a fixed number of times to allow any three letters in the middle. - Match the literal letter
eright before the ending anchor.
▼ Solution & Explanation
Explanation:
^a: Anchors the match to the beginning of the string and requires the very first character to be a literala..{3}: Matches any three characters in a row, allowing the middle of the word to be anything.e$: Requires the string to end immediately with a literale.- Overall length: Since the pattern accounts for exactly 1 + 3 + 1 = 5 characters between the anchors, only exactly five-letter words starting with
aand ending withecan match.
Exercise 4: Valid Whole Numbers
Practice Problem: Determine if a string is a valid positive whole number (digits only, no signs or decimals).
Purpose: This exercise helps you practice a straightforward digits-only pattern anchored to the full string, distinguishing it from patterns that would also need to reject signs or decimal points.
Given Input: input = "4820"
Expected Output: True
▼ Hint
- Anchor the pattern to the start and end of the string.
- Use
\d+to require one or more digit characters with no other characters allowed. - Test the pattern against a string that includes a decimal point or a minus sign to confirm it correctly returns false.
- Remember that
\d+alone, without anchors, would also match a number embedded inside a longer string.
▼ Solution & Explanation
Explanation:
^\d+$: Requires the entire string, from start to end, to consist of one or more digit characters and nothing else.\d+: The+quantifier allows the pattern to match whole numbers of any length, not just a fixed number of digits.- Rejecting other formats: A string like “48.20” or “-4820” fails this pattern, since the decimal point and minus sign aren’t digit characters.
- Anchors matter: Without
^and$, the pattern would also match a numeric substring found anywhere inside a larger, non-numeric string.
Exercise 5: Whitespace Cleaner
Practice Problem: Identify any instance of multiple consecutive spaces so they can be replaced with a single space.
Purpose: This exercise helps you practice using Regex.Replace to normalize repeated whitespace in a string down to a single space in one call.
Given Input: text = "Hello World"
Expected Output: Hello World
▼ Hint
- Use a pattern like
" {2,}"to match two or more consecutive space characters. - Pass that pattern to
Regex.Replacealong with the input string and a replacement of a single space. Regex.Replacereturns a brand new string rather than modifying the original in place.- Test with a string containing single spaces only to confirm those are left untouched.
▼ Solution & Explanation
Explanation:
" {2,}": Matches a run of two or more consecutive space characters, using the{2,}quantifier for “two or more” repetitions.Regex.Replace(text, pattern, " "): Finds every matching run of spaces in the input and replaces each one with a single space character.- Returned as a new string: The original
textvariable is unaffected, since strings in C# are immutable; the result is captured in a separateresultvariable. - Runs of any length: Whether there are two spaces or ten spaces in a row, the
{2,}quantifier matches the whole run, and it gets collapsed down to one space.
Exercise 6: Hexadecimal Color Codes
Practice Problem: Match standard 6-character hex color strings starting with a #.
Purpose: This exercise helps you practice using a character class combined with a fixed repeat count to validate a specific, structured text format.
Given Input: colors = ["#FF5733", "#00A3FF", "#GG1234"]
Expected Output:
#FF5733: True #00A3FF: True #GG1234: False
▼ Hint
- Start the pattern with a literal
#character. - Use a character class like
[0-9A-Fa-f]to match a single hexadecimal digit, covering both uppercase and lowercase letters. - Follow that character class with
{6}to require exactly six hexadecimal digits. - Anchor the pattern with
^and$so the whole string must match this exact format.
▼ Solution & Explanation
Explanation:
^#: Anchors the match to the start of the string and requires a literal#as the very first character.[0-9A-Fa-f]: A character class matching any single hexadecimal digit, including both uppercase and lowercase letters A through F.{6}: Requires exactly six hexadecimal digits to follow the#character.$: Anchors the end of the match, so a string with extra characters after the six digits, or fewer than six digits, would not match.
Exercise 7: Extracting Words Starting with a Vowel
Practice Problem: Find all individual words in a text block that begin with a, e, i, o, or u, matching regardless of case.
Purpose: This exercise helps you practice combining a character class for the starting letter with a case-insensitive matching option, so both uppercase and lowercase vowels are handled by the same pattern.
Given Input: text = "An elephant ate an Apple"
Expected Output:
An elephant ate an Apple
▼ Hint
- Use
\bto anchor the start of each match to a word boundary. - Match a single vowel using a character class like
[aeiou]. - Follow the vowel with
\w*to allow any number of additional word characters. - Pass
RegexOptions.IgnoreCasetoRegex.Matchesso both uppercase and lowercase vowels are matched by the same pattern.
▼ Solution & Explanation
Explanation:
\b[aeiou]\w*\b: Matches a word that starts with a single vowel, followed by zero or more additional word characters, bounded by word boundaries on both sides.RegexOptions.IgnoreCase: Makes the character class[aeiou]also match its uppercase equivalents, so words like “An” and “Apple” are included alongside lowercase ones.\w*: Allows the rest of the word to be any length, including zero additional characters, so a single-letter word starting with a vowel would still match.Regex.Matches(text, pattern, RegexOptions.IgnoreCase): Applies the case-insensitive option only to this particular search, without affecting how the pattern would behave elsewhere.
Exercise 8: Finding Isolated Symbols
Practice Problem: Find occurrences of the $ sign followed immediately by one or more digits.
Purpose: This exercise helps you practice anchoring a pattern to a specific literal character before applying a quantifier to the digits that follow it.
Given Input: text = "The total is $100, plus a $5 fee"
Expected Output:
$100 $5
▼ Hint
- Match a literal dollar sign using
\$, since$has a special meaning in regex and needs to be escaped to be treated literally. - Follow the escaped dollar sign with
\d+to require one or more digits immediately after it. - Use
Regex.Matchesto find every occurrence in the input, since there may be more than one. - Print each match’s
Valueto see the dollar sign and its digits together.
▼ Solution & Explanation
Explanation:
\$: Escapes the dollar sign, since an unescaped$normally represents the end-of-string anchor in regex rather than a literal character.\d+: Requires one or more digits to immediately follow the dollar sign, with no space or other character in between.Regex.Matches: Finds both occurrences in the sample text, “$100” and “$5”, even though they appear in different parts of the string.- No upper bound on digits:
\d+matches as many digits as are actually present, so this same pattern works for both “$100” and “$5” without any changes.
Exercise 9: Simple Variable Names
Practice Problem: Match a valid C# variable identifier: it must start with a letter or underscore, followed by letters, numbers, or underscores.
Purpose: This exercise helps you practice using different character classes for the first character versus the remaining characters of a token, since identifiers can’t start with a digit but can contain one later on.
Given Input: names = ["_userId", "data2", "2data"]
Expected Output:
_userId: True data2: True 2data: False
▼ Hint
- Match the first character using a character class that allows a letter or an underscore, but not a digit.
- Follow that with
\w*to allow any number of additional letters, digits, or underscores. - Anchor the pattern with
^and$so the entire string must form a valid identifier, not just part of it. - Test with a string that starts with a digit to confirm the pattern correctly rejects it.
▼ Solution & Explanation
Explanation:
^[A-Za-z_]: Requires the very first character to be a letter or an underscore, explicitly excluding digits from that position.\w*: Matches any number of additional letters, digits, or underscores after the first character, since\wincludes all three.$: Anchors the end of the match, ensuring the entire string conforms to this pattern rather than just its beginning.- Rejecting “2data”: Since the first character class doesn’t include digits, a string starting with a number fails to match right from the first character.
Exercise 10: Stripping Punctuation
Practice Problem: Find all punctuation characters (like ., ,, !, ?) in a paragraph so they can be removed.
Purpose: This exercise helps you practice defining a character class of specific punctuation symbols and using Regex.Replace to strip every occurrence from a string in one call.
Given Input: text = "Stop, look! Are you ready?"
Expected Output: Stop look Are you ready
▼ Hint
- Define a character class containing the specific punctuation marks you want to target, such as periods, commas, exclamation points, and question marks.
- Pass that pattern and the input string to
Regex.Replace, using an empty string as the replacement. - Since
Regex.Replaceremoves every match it finds, all instances of the listed punctuation marks are stripped in a single call. - Print the result to confirm the punctuation has been removed while the surrounding words remain intact.
▼ Solution & Explanation
Explanation:
[.,!?]: A character class listing the specific punctuation marks to match; inside a character class, a period doesn’t need to be escaped since it loses its special “any character” meaning there.Regex.Replace(text, pattern, ""): Replaces every character matching the class with an empty string, effectively deleting it from the result.- Single pass: All four types of punctuation are handled by one pattern and one
Replacecall, rather than needing a separate call for each symbol. - Spaces are preserved: Since the character class only targets punctuation, the spaces between words remain exactly as they were in the original text.
Exercise 11: Basic Email Validation
Practice Problem: Verify a basic email structure: alphanumeric characters, an @ symbol, a domain name, and a 2-4 letter TLD.
Purpose: This exercise helps you practice combining several character classes and literal characters into one pattern that models a structured, multi-part format like an email address.
Given Input: email = "test.user@domain.com"
Expected Output: True
▼ Hint
- Match the local part before the
@using a character class that allows letters, digits, and characters like periods or underscores, repeated one or more times. - Match a literal
@symbol right after the local part. - Match the domain name using a similar character class followed by a literal period.
- Match the top-level domain using a letter-only character class with a quantifier limiting it to between 2 and 4 characters.
▼ Solution & Explanation
Explanation:
[\w.]+before the@: Matches one or more letters, digits, underscores, or periods, covering common local-part formats like “test.user”.@: A literal character requiring exactly one@symbol between the local part and the domain.[\w.]+\.[A-Za-z]{2,4}: Matches the domain name followed by a literal period and a top-level domain of two to four letters, such as “.com” or “.org”.- Anchors
^and$: Ensure the entire string matches this structure, rather than just a portion of a longer string.
Exercise 12: Date Format Converter (MM/DD/YYYY)
Practice Problem: Validate and capture groups for a US date format.
Purpose: This exercise helps you practice using parentheses to define capture groups, letting you pull out the month, day, and year separately instead of just confirming the overall string is valid.
Given Input: date = "12/25/2026"
Expected Output:
Month: 12 Day: 25 Year: 2026
▼ Hint
- Wrap each of the month, day, and year sections in their own parentheses to create separate capture groups.
- Use
\d{2}for both the month and day sections, since US dates use two digits for each. - Use
\d{4}for the year section, since it’s written using four digits. - Separate each group with a literal forward slash, and access the captured values through the Match object’s
Groupscollection.
▼ Solution & Explanation
Explanation:
(\d{2})/(\d{2})/(\d{4}): Three separate capture groups, each wrapped in parentheses, capturing the month, day, and year sections individually.Regex.Match(date, pattern): Returns a singleMatchobject representing the first match found, along with access to each capture group.match.Groups[1],match.Groups[2],match.Groups[3]: Retrieve the captured text for the month, day, and year respectively, with index 0 representing the entire match.match.Success: Confirms a match was actually found before attempting to read from its groups, avoiding an error on invalid input.
Exercise 13: Extraction of href Attributes
Practice Problem: Extract the URL target from a standard HTML anchor tag link.
Purpose: This exercise helps you practice using a capture group alongside literal text that surrounds the value you actually want to extract, so only the inner portion gets pulled out.
Given Input: html = "<a href=\"https://example.com\">Visit</a>"
Expected Output: https://example.com
▼ Hint
- Match the literal text
href="that appears right before the URL. - Use a capture group with a character class like
[^"]+to match everything up to, but not including, the next double quote. - Match the closing double quote right after the capture group to mark the end of the URL.
- Access the captured URL through the first capture group of the resulting match.
▼ Solution & Explanation
Explanation:
href="([^"]+)": Matches the literal texthref="followed by a capture group, then a literal closing quote.[^"]+: A negated character class matching one or more characters that are not a double quote, capturing everything up until the next quote is found.match.Groups[1].Value: Retrieves just the captured URL, without the surroundinghref="and closing quote that were part of the overall match but not part of the group.- Literal double quotes in the pattern: Written as
""inside a verbatim string, since a verbatim string represents a literal double quote by doubling it.
Exercise 14: North American Phone Numbers
Practice Problem: Match standard phone formats: 123-456-7890 or (123) 456-7890.
Purpose: This exercise helps you practice using alternation to accept two different formats within a single pattern, since a valid phone number can be written more than one way.
Given Input: phones = ["555-867-5309", "(555) 867-5309", "555.867.5309"]
Expected Output:
555-867-5309: True (555) 867-5309: True 555.867.5309: False
▼ Hint
- Build one alternative for the dash-separated format, such as
\d{3}-\d{3}-\d{4}. - Build a second alternative for the parenthesized format, such as
\(\d{3}\) \d{3}-\d{4}, escaping the literal parentheses. - Combine both alternatives using the
|operator, wrapped in a group. - Anchor the whole pattern with
^and$so only a string matching one of the two exact formats passes.
▼ Solution & Explanation
Explanation:
\d{3}-\d{3}-\d{4}: The first alternative, matching three digits, a dash, three more digits, another dash, and four final digits.\(\d{3}\) \d{3}-\d{4}: The second alternative, with the parentheses escaped using\(and\), since unescaped parentheses would otherwise define a group rather than match literal characters.|: The alternation operator, allowing the overall pattern to match either the first format or the second format.- Rejecting other formats: A string using periods instead of dashes, like “555.867.5309”, doesn’t match either alternative, so it correctly returns false.
Exercise 15: Extracting Domain Names
Practice Problem: Parse a full URL and capture just the domain name via a capture group.
Purpose: This exercise helps you practice using a capture group nested within a larger pattern that also matches the surrounding parts of the URL you don’t need to keep.
Given Input: url = "https://github.com/features"
Expected Output: github
▼ Hint
- Match the literal protocol prefix
https://before the part you want to capture. - Wrap the domain name portion in a capture group using a character class that excludes periods and slashes.
- Match the rest of the URL, such as the top-level domain and any following path, without capturing it.
- Retrieve just the captured domain name through the match’s first group.
▼ Solution & Explanation
Explanation:
https://: Matches the literal protocol prefix at the start of the URL, ensuring the pattern only applies to URLs written this way.([^./]+): A capture group using a negated character class to match one or more characters that are not a period or a forward slash, isolating just the “github” portion.\.\w+: Matches the literal period followed by the top-level domain, such as “com”, without including it inside the capture group.match.Groups[1].Value: Extracts only the captured domain name, ignoring both the protocol prefix and the top-level domain that surround it in the full match.
Exercise 16: Duplicate Word Finder
Practice Problem: Identify cases where a writer accidentally typed the same word twice in a row.
Purpose: This exercise helps you practice using a backreference to require a captured group to repeat exactly, a pattern impossible to express with character classes or quantifiers alone.
Given Input: text = "This is the the end."
Expected Output: the the
▼ Hint
- Capture a word using a group like
(\w+). - Follow the capture group with whitespace, matched using
\s+. - Reference the same captured word again using a backreference like
\1, requiring it to repeat exactly. - Use word boundaries around the whole pattern so it only matches complete duplicate words, not partial overlaps.
▼ Solution & Explanation
Explanation:
(\w+): Captures a single word into the first group, storing whatever sequence of word characters is matched.\s+: Matches the whitespace separating the two occurrences of the word.\1: A backreference to the first capture group, requiring the exact same text captured earlier to appear again at this position.\bat both ends: Ensures the match starts and ends cleanly on word boundaries, so it identifies whole duplicated words rather than partial matches within longer words.
Exercise 17: Numeric Values with Optional Decimals
Practice Problem: Match integers or floating-point numbers, including optional leading negative signs.
Purpose: This exercise helps you practice making parts of a pattern optional using the ? quantifier, so the same pattern can match both whole numbers and numbers with a decimal portion.
Given Input: values = ["-45.67", "100", "3.14", "-8"]
Expected Output:
-45.67: True 100: True 3.14: True -8: True
▼ Hint
- Make a leading minus sign optional by following it with a
?quantifier. - Match one or more digits for the whole number portion using
\d+. - Make the decimal portion optional as a group, containing a literal period followed by one or more digits, then apply
?to the entire group. - Anchor the pattern with
^and$so it validates the entire string as a single number.
▼ Solution & Explanation
Explanation:
-?: Makes the leading minus sign optional, so both positive and negative numbers can match the same pattern.\d+: Requires one or more digits for the main integer portion of the number.(\.\d+)?: Groups a literal period followed by one or more digits, then makes the entire group optional, allowing the pattern to match whole numbers that have no decimal portion at all.- Anchors
^and$: Ensure the full string represents a single valid number, rather than just containing one somewhere within a longer string.
Exercise 18: Extracting Log Severity Levels
Practice Problem: Identify and extract tags like [INFO], [ERROR], or [WARNING] from the beginning of log file entries.
Purpose: This exercise helps you practice anchoring a pattern to the start of a string while matching a bracketed tag made entirely of uppercase letters.
Given Input: logEntry = "[ERROR] Connection failed."
Expected Output: [ERROR]
▼ Hint
- Anchor the pattern to the start of the string using
^. - Match a literal opening bracket, followed by one or more uppercase letters, followed by a literal closing bracket.
- Use a character class like
[A-Z]+to allow the tag name to be any length, covering both INFO and WARNING. - Retrieve the whole match directly, since the brackets are part of what you want to extract this time.
▼ Solution & Explanation
Explanation:
^: Anchors the match to the very beginning of the string, so the tag must appear as the first thing in the log entry.\[and\]: Escape the literal square brackets, since unescaped brackets normally define a character class in regex.[A-Z]+: Matches one or more uppercase letters between the brackets, covering tags of different lengths like INFO, ERROR, and WARNING with the same pattern.match.Value: Returns the entire match, including the brackets, since this exercise wants the tag exactly as it appears, not just the letters inside it.
Exercise 19: Password Strength Requirement
Practice Problem: Match a string that is between 8 and 16 characters long and contains at least one digit.
Purpose: This exercise helps you practice combining a length constraint with a lookahead, which lets you require a condition to exist somewhere in the string without consuming any of the characters it checks.
Given Input: password = "P@ssword123"
Expected Output: True
▼ Hint
- Start the pattern with a lookahead like
(?=.*\d)to require at least one digit somewhere in the string. - Follow the lookahead with
.{8,16}to constrain the total length of the string to between 8 and 16 characters. - Anchor the whole pattern with
^and$so the length and digit requirements apply to the entire string. - Remember that a lookahead checks for a condition without actually consuming any characters, so it can be placed before the length check.
▼ Solution & Explanation
Explanation:
(?=.*\d): A positive lookahead requiring at least one digit to appear somewhere later in the string, without consuming any characters itself..{8,16}: Matches the actual string content, requiring its total length to fall between 8 and 16 characters.- Order matters conceptually, not positionally: Since the lookahead doesn’t consume characters, it can sit right at the start and still check the digit requirement across the whole string that
.{8,16}later matches. - Anchors
^and$: Ensure both conditions, the digit requirement and the length range, apply to the complete password string rather than a substring of it.
Exercise 20: Matching Content Inside Quotes
Practice Problem: Extract text enclosed within standard double quotes without including the quotes themselves.
Purpose: This exercise helps you practice using a capture group between two literal quote characters to pull out just the enclosed text, leaving the quotes themselves out of the result.
Given Input: text = "He said \"Secret Message\"."
Expected Output: Secret Message
▼ Hint
- Match a literal double quote to mark the start of the quoted section.
- Wrap a character class like
[^"]+in a capture group to match everything up until the next double quote, without matching the quote itself. - Match a second literal double quote to mark the end of the quoted section.
- Retrieve just the captured text through the match’s first group, excluding both surrounding quotes.
▼ Solution & Explanation
Explanation:
""in a verbatim string: A doubled double-quote represents a single literal double-quote character within the pattern.([^"]+): A capture group matching one or more characters that are not a double quote, isolating the text found between the two quote marks.- Surrounding literal quotes: Match the opening and closing double quotes themselves, but since they sit outside the parentheses, they aren’t included in the captured group.
match.Groups[1].Value: Returns only the inner text, “Secret Message”, without the quote marks that surrounded it in the original string.
Exercise 21: Extracting Prices Preceded by Currency Text
Practice Problem: Use a lookbehind assertion to capture numeric prices only if they are preceded by a keyword like Price: .
Purpose: This exercise helps you practice using a lookbehind assertion, which checks for text immediately before the current position without including that text in the actual match.
Given Input: text = "Total Price: 49.99"
Expected Output: 49.99
▼ Hint
- Use a lookbehind like
(?<=Price: )right before the part of the pattern that matches the actual price. - Match the numeric price itself using something like
\d+\.\d+for a number with a decimal portion. - Remember that a lookbehind only checks that the preceding text exists; it doesn’t include that text in the match itself.
- Use
Regex.Matchand read the match’sValuedirectly, since nothing needs to be captured in a separate group this time.
▼ Solution & Explanation
Explanation:
(?<=Price: ): A lookbehind assertion requiring the literal text “Price: ” to appear immediately before the current position, without that text becoming part of the match.\d+\.\d+: Matches the numeric price itself, requiring digits, a literal decimal point, and more digits.match.Value: Contains only “49.99”, since the lookbehind’s text, “Price: “, was checked but never consumed or included in the result.- Positional requirement: This pattern would not match a number that isn’t immediately preceded by “Price: “, such as a quantity or a different label entirely.
Exercise 22: Stripping HTML Tags Safely
Practice Problem: Match all HTML open and close tags to sanitize a string into plain text.
Purpose: This exercise helps you practice matching a general tag-like structure using literal angle brackets and a wildcard for everything in between, without needing to know every possible tag name in advance.
Given Input: html = "<p>Hello</p>"
Expected Output: Hello
▼ Hint
- Match a literal opening angle bracket to mark the start of a tag.
- Use a character class like
[^>]+to match everything inside the tag, up until the closing angle bracket, without matching across multiple tags. - Match a literal closing angle bracket to mark the end of the tag.
- Use
Regex.Replaceto remove every matched tag, leaving just the surrounding plain text.
▼ Solution & Explanation
Explanation:
<[^>]+>: Matches a literal opening angle bracket, one or more characters that aren’t a closing angle bracket, and a literal closing angle bracket, capturing an entire tag as a single match.[^>]+: Prevents the match from accidentally spanning across two separate tags, since it stops as soon as it hits the first closing angle bracket.Regex.Replace(html, pattern, ""): Removes every matched tag from the string, sinceReplacewith an empty string effectively deletes whatever the pattern finds.- Result: Only the plain text content, “Hello”, remains once both the opening and closing tags have been stripped out.
Exercise 23: Parsing Key-Value Configurations
Practice Problem: Use named capture groups to break down configuration file lines into separate key and value pieces.
Purpose: This exercise helps you practice using named capture groups, which let you retrieve captured values by a descriptive name instead of a numeric index, making the code more readable.
Given Input: config = "Timeout=30"
Expected Output:
Key: Timeout Value: 30
▼ Hint
- Define a named capture group using the syntax
(?<name>pattern), giving each group a descriptive label. - Use
\w+inside both the key and value groups to match one or more word characters. - Separate the two groups with a literal equals sign.
- Retrieve each captured value by name using
match.Groups["key"]andmatch.Groups["value"], instead of a numeric index.
▼ Solution & Explanation
Explanation:
(?<key>\w+): Defines a named capture group called “key”, matching one or more word characters before the equals sign.(?<value>\w+): Defines a second named capture group called “value”, matching one or more word characters after the equals sign.match.Groups["key"]andmatch.Groups["value"]: Retrieve each captured piece of text using its descriptive name, rather than remembering which numeric index corresponds to which group.- Readability benefit: Named groups make patterns with several captured pieces much easier to work with correctly, especially as a pattern grows more complex.
Exercise 24: Validating IPv4 Addresses
Practice Problem: Match standard IP addresses, ensuring each octet is strictly between 0 and 255.
Purpose: This exercise helps you practice building a pattern where each segment has its own set of alternatives to correctly constrain a numeric range that a simple \d{1,3} wouldn’t enforce on its own.
Given Input: ip = "192.168.1.254"
Expected Output: True
▼ Hint
- Build a single octet pattern that separately allows 250-255, 200-249, 100-199, or 1-99 and single digits, using alternation.
- Reuse that same octet pattern four times, separated by literal periods.
- Wrap the octet pattern in a non-capturing group using
(?:...)so it can be reused without creating unnecessary capture groups. - Anchor the whole pattern with
^and$so the entire string must represent a valid IPv4 address.
▼ Solution & Explanation
Explanation:
25[0-5]: The first alternative, matching numbers from 250 to 255.2[0-4]\d: The second alternative, matching numbers from 200 to 249.1\d\d: The third alternative, matching any three-digit number from 100 to 199.[1-9]?\d: The final alternative, matching any one or two digit number from 0 to 99, with the leading digit optional so single-digit octets like “5” are still valid.
Exercise 25: Negative Lookahead Filtering
Practice Problem: Match words that start with “pro” unless they end with “ing”.
Purpose: This exercise helps you practice using a negative lookahead to exclude a specific ending from an otherwise matching pattern, without needing a separate step to filter results afterward.
Given Input: words = ["product", "programming", "profile"]
Expected Output:
product: True programming: False profile: True
▼ Hint
- Match the literal prefix
proat the start of each word. - Immediately after that, add a negative lookahead like
(?!.*ing$)to reject any word that ends in “ing”. - Follow the lookahead with
\w*to match the remaining letters of the word. - Anchor the pattern with
^and$so the whole word, not just the “pro” prefix, is evaluated.
▼ Solution & Explanation
Explanation:
^pro: Anchors the match to the start of the string and requires the literal prefix “pro”.(?!.*ing$): A negative lookahead checking that the rest of the string does not end in “ing”; if it does, the overall match fails at this position.\w*$: Matches the remainder of the word after the lookahead succeeds, anchored to the end of the string.- Rejecting “programming”: Even though it starts with “pro”, the negative lookahead detects the “ing” ending later in the string and prevents a match, while “product” and “profile” pass through since neither ends that way.
Exercise 26: Extracting MAC Addresses
Practice Problem: Match standard 12-digit hexadecimal MAC addresses separated by hyphens or colons.
Purpose: This exercise helps you practice reusing a repeated group structure with a fixed count while allowing a choice between two possible separator characters.
Given Input: mac = "00:1A:2B:3C:4D:5E"
Expected Output: True
▼ Hint
- Build a pair pattern matching exactly two hexadecimal digits, such as
[0-9A-Fa-f]{2}. - Follow that pair with a character class allowing either a colon or a hyphen as the separator.
- Repeat the pair-plus-separator combination four times, then add one final pair without a trailing separator.
- Anchor the whole pattern with
^and$so the entire string must represent a complete MAC address.
▼ Solution & Explanation
Explanation:
[0-9A-Fa-f]{2}: Matches exactly two hexadecimal digits, representing one byte of the MAC address.[:-]: A character class allowing either a colon or a hyphen as the separator between each pair of digits.([0-9A-Fa-f]{2}[:-]){5}: Groups the pair-and-separator combination together and repeats it exactly five times, covering the first five bytes of the address.[0-9A-Fa-f]{2}$: Matches the sixth and final pair of digits with no trailing separator, anchored to the end of the string.
Exercise 27: Non-Backtracking Verification
Practice Problem: Write a pattern to process large, chaotic blocks of user aliases safely by utilizing RegexOptions.NonBacktracking to prevent ReDoS (Regular Expression Denial of Service).
Purpose: This exercise helps you practice using a non-backtracking matching mode, which avoids the exponential slowdown that certain nested, repeating patterns can cause on carefully crafted or malformed input.
Given Input: A deliberately vulnerable pattern applied to a large block of repeated characters followed by a character that would normally trigger catastrophic backtracking in a naive engine.
Expected Output (illustrative; exact timing will vary by machine):
Match: False Completed in 2 ms
▼ Hint
- Construct the regular expression using the
RegexOptions.NonBacktrackingoption, which changes the underlying matching engine’s behavior. - Write the pattern as you normally would, since
NonBacktrackingchanges how the engine executes the pattern rather than the pattern’s syntax itself. - Test the pattern against a large, adversarial input that would take an extremely long time with a normal backtracking engine.
- Measure the elapsed time to confirm the match completes quickly rather than hanging.
▼ Solution & Explanation
Explanation:
RegexOptions.NonBacktracking: Switches the regex engine to a linear-time matching algorithm, which cannot exhibit the exponential slowdown that a classic backtracking engine can hit on patterns like nested repetition.(a+)+: A deliberately vulnerable pattern structure, an inner repetition wrapped in an outer repetition, that’s known to cause catastrophic backtracking against certain crafted inputs under a normal backtracking engine.Stopwatch: Measures how long the match takes, illustrating that the non-backtracking engine completes quickly even against an input designed to be adversarial.- Trade-off:
NonBacktrackingmode doesn’t support every regex feature, such as backreferences or lookaround, so it’s chosen specifically for patterns and use cases where guaranteed linear-time matching matters more than those features.
Exercise 28: Splitting CamelCase Words
Practice Problem: Find the boundaries where a lowercase letter transitions to an uppercase letter, so you can split a camelCase string into distinct words.
Purpose: This exercise helps you practice using a lookahead and lookbehind together to find a position between two characters, rather than matching the characters themselves, then using that position as a split point.
Given Input: text = "camelCaseExample"
Expected Output:
camel Case Example
▼ Hint
- Build a pattern that matches the empty position between a lowercase letter and an uppercase letter, using a lookbehind and a lookahead.
- Use
Regex.Splitwith that pattern, since it splits the string at every position the pattern matches without consuming any characters itself. - A lookahead like
(?=[A-Z])checks for an upcoming uppercase letter without including it in what gets matched. - Combine that with a preceding lowercase letter check to pinpoint exactly the lowercase-to-uppercase transition point.
▼ Solution & Explanation
Explanation:
(?<=[a-z]): A lookbehind confirming the character immediately before the current position is a lowercase letter.(?=[A-Z]): A lookahead confirming the character immediately after the current position is an uppercase letter.- Combined lookbehind and lookahead: Together they pinpoint the exact boundary between a lowercase letter and an uppercase letter without matching or consuming either character.
Regex.Split(text, pattern): Splits the original string at every position the pattern matches, producing “camel”, “Case”, and “Example” as three separate array elements.
Exercise 29: Parsing C# Comments
Practice Problem: Match both single-line and block comments in a source code string.
Purpose: This exercise helps you practice using alternation to combine two structurally different comment patterns into a single regular expression.
Given Input: code = "int x = 5; // set x\n/* TODO: fix */"
Expected Output:
// set x /* TODO: fix */
▼ Hint
- Build one alternative for single-line comments, matching two literal forward slashes followed by anything up to the end of the line.
- Build a second alternative for block comments, matching a literal
/*followed by any characters, followed by a literal*/, using a non-greedy quantifier so it stops at the first closing marker. - Combine both alternatives with the
|operator. - Use
Regex.Matchesto find every comment of either kind within a larger block of code.
▼ Solution & Explanation
Explanation:
//.*: Matches two literal forward slashes followed by any characters up to, but not including, the newline, capturing a single-line comment./\*.*?\*/: Matches a literal/*, followed by as few characters as possible, followed by a literal*/; the non-greedy.*?stops at the very first*/it finds, avoiding accidentally spanning multiple separate block comments.|: Combines the two alternatives, so the pattern matches either style of comment wherever it appears.- Default
.behavior: Since.doesn’t match a newline character by default, the single-line comment alternative naturally stops at the end of its line without needing an explicit anchor.
Exercise 30: Credit Card Number Masking
Practice Problem: Match the first 12 digits of a 16-digit credit card number separated by spaces or dashes, then use Regex.Replace to replace them with X’s while keeping the last 4 digits visible.
Purpose: This exercise helps you practice using a capture group to preserve part of a match while replacing the rest, so Regex.Replace can rebuild a masked version of the original string.
Given Input: cardNumber = "4111-2222-3333-4444"
Expected Output: XXXX-XXXX-XXXX-4444
▼ Hint
- Match the first three groups of four digits along with their separators, without needing to capture them since they’ll be replaced entirely.
- Capture the final separator and the last four digits together in a group, so that portion can be preserved in the replacement.
- Use
Regex.Replacewith a replacement string that inserts fixed X characters followed by a reference to the captured group, such as$1. - Test the pattern against the given input to confirm the first twelve digits are masked while the last four remain visible.
▼ Solution & Explanation
Explanation:
\d{4}[- ]\d{4}[- ]\d{4}: Matches the first three groups of four digits, each followed by a dash or a space, without wrapping them in a capture group since they’ll be discarded entirely from the output.([- ]\d{4}): Captures the final separator together with the last four digits, preserving that portion so it can be reused in the replacement."XXXX-XXXX-XXXX$1": The replacement string;$1inserts whatever was captured by the first group, appending the real final separator and last four digits after the fixed masked portion.- Result: The first twelve digits, along with their original separators, are replaced by literal X’s and dashes, while the actual last four digits from the input remain visible in the output.

Leave a Reply