Elysia Tools
Navigation
C#
Windows String Processing - C# Samples
Comprehensive C# string processing examples for Windows platform including string manipulation, splitting, joining, regex operations, and text analysis
Samples
Entries inside this sample collection
String Splitting and Joining Operations
Advanced string splitting and joining techniques with different separators, delimiters, and custom parsing logic
Difficulty
3/10
Estimated time
20 min
Tags
csharp, string, split, join, parsing, windows
Prerequisites
Basic C# string operations, LINQ basics
using System;
using System.Text;
using System.Linq;
class StringSplitJoin
{
// 1. Basic string splitting
public static void BasicStringSplitting()
{
Console.WriteLine("=== Basic String Splitting ===");
string text = "Apple,Banana,Cherry,Date,Elderberry";
string[] fruits = text.Split(',');
Console.WriteLine($"Original text: {text}");
Console.WriteLine("Split fruits:");
foreach (string fruit in fruits)
{
Console.WriteLine($" {fruit}");
}
Console.WriteLine($"Total fruits: {fruits.Length}");
}
// 2. Splitting with multiple delimiters
public static void MultipleDelimiterSplitting()
{
Console.WriteLine("\n=== Multiple Delimiter Splitting ===");
string data = "John,Doe;25|New York|Jane,Smith;30|Los Angeles";
// Define delimiters
char[] delimiters = { ',', ';', '|' };
string[] parts = data.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine($"Original data: {data}");
Console.WriteLine("Split parts:");
for (int i = 0; i < parts.Length; i++)
{
Console.WriteLine($" {i}: {parts[i]}");
}
// Parse into structured format
Console.WriteLine("\nParsed records:");
for (int i = 0; i < parts.Length; i += 3)
{
if (i + 2 < parts.Length)
{
Console.WriteLine($" Name: {parts[i]} {parts[i+1]}, Age: {parts[i+2]}, City: {parts[i+3]}");
}
}
}
// 3. Advanced splitting with StringSplitOptions
public static void AdvancedSplitOptions()
{
Console.WriteLine("\n=== Advanced Split Options ===");
string textWithEmptyEntries = "Apple,,Banana,,,Cherry,Date,,";
Console.WriteLine($"Original text: '{textWithEmptyEntries}'");
// Split including empty entries
string[] withEmpty = textWithEmptyEntries.Split(',');
Console.WriteLine($"
Split with empty entries (length: {withEmpty.Length}):");
for (int i = 0; i < withEmpty.Length; i++)
{
Console.WriteLine($" [{i}]: '{withEmpty[i]}'");
}
// Split removing empty entries
string[] withoutEmpty = textWithEmptyEntries.Split(',', StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine($"
Split without empty entries (length: {withoutEmpty.Length}):");
for (int i = 0; i < withoutEmpty.Length; i++)
{
Console.WriteLine($" [{i}]: '{withoutEmpty[i]}'");
}
// Split with trimming entries
string[] trimmedEntries = textWithEmptyEntries.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.ToArray();
Console.WriteLine($"
Split with trimmed entries (length: {trimmedEntries.Length}):");
for (int i = 0; i < trimmedEntries.Length; i++)
{
Console.WriteLine($" [{i}]: '{trimmedEntries[i]}'");
}
}
// 4. String.Join operations
public static void StringJoining()
{
Console.WriteLine("\n=== String Joining ===");
string[] words = { "Hello", "World", "in", "C#" };
// Basic joining
string joined = string.Join(" ", words);
Console.WriteLine($"Basic join: '{joined}'");
// Join with different separators
string commaJoined = string.Join(", ", words);
Console.WriteLine($"Comma join: '{commaJoined}'");
string pipeJoined = string.Join(" | ", words);
Console.WriteLine($"Pipe join: '{pipeJoined}'");
// Join with prefix and suffix
string bracketed = $"[{string.Join("][", words)}]";
Console.WriteLine($"Bracketed: '{bracketed}'");
// Join with numbers
int[] numbers = { 1, 2, 3, 4, 5 };
string numberString = string.Join("-", numbers);
Console.WriteLine($"Numbers joined: '{numberString}'");
// Join with custom formatting
string[] items = { "Apple", 1.99, "Orange", 2.49 };
string formatted = string.Join(" | ", items.Select(item => item.ToString().PadLeft(10)));
Console.WriteLine($"Formatted join: '{formatted}'");
}
// 5. Split and Join with complex parsing
public static void ComplexParsing()
{
Console.WriteLine("\n=== Complex Parsing ===");
string csvData = @"Name,Age,City,Salary
John Doe,25,New York,50000
Jane Smith,30,Los Angeles,65000
Bob Johnson,35,Chicago,75000
Alice Brown,28,Houston,58000";
string[] lines = csvData.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine("CSV Data Parsing:");
Console.WriteLine(csvData);
Console.WriteLine();
Console.WriteLine("Parsed records:");
string[] headers = lines[0].Split(',');
for (int i = 1; i < lines.Length; i++)
{
string[] fields = lines[i].Split(',');
if (fields.Length == headers.Length)
{
Console.WriteLine($"Record {i}:");
for (int j = 0; j < headers.Length; j++)
{
Console.WriteLine($" {headers[j]}: {fields[j]}");
}
Console.WriteLine();
}
}
// Reconstruct with modified data
Console.WriteLine("Reconstructed with modified salaries (+10%):");
var modifiedLines = lines.Select((line, index) =>
{
if (index == 0) return line; // Keep header
string[] fields = line.Split(',');
if (fields.Length >= 4 && double.TryParse(fields[3], out double salary))
{
double newSalary = salary * 1.1;
fields[3] = newSalary.ToString("F2");
}
return string.Join(",", fields);
});
string reconstructed = string.Join("\n", modifiedLines);
Console.WriteLine(reconstructed);
}
// 6. Split with string separators (not just characters)
public static void StringSeparatorSplitting()
{
Console.WriteLine("\n=== String Separator Splitting ===");
string text = "Item1---Separator---Item2---Separator---Item3";
string[] parts = text.Split(new string[] { "---Separator---" }, StringSplitOptions.None);
Console.WriteLine($"Original: {text}");
Console.WriteLine("Split with string separator:");
for (int i = 0; i < parts.Length; i++)
{
Console.WriteLine($" Part {i + 1}: '{parts[i]}'");
}
// Example with HTML tags
string html = "<div>Content1</div><div>Content2</div><div>Content3</div>";
string[] contentParts = html.Split(new string[] { "<div>", "</div>" }, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine($"\nOriginal HTML: {html}");
Console.WriteLine("Extracted content:");
for (int i = 0; i < contentParts.Length; i++)
{
Console.WriteLine($" Content {i + 1}: '{contentParts[i]}'");
}
}
// 7. Joining with StringBuilder for performance
public static void StringBuilderJoining()
{
Console.WriteLine("\n=== StringBuilder Joining ===");
// Large array for performance testing
string[] largeArray = Enumerable.Range(1, 10000)
.Select(i => $"Item{i}")
.ToArray();
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
// Method 1: string.Join (fast for small arrays)
stopwatch.Restart();
string result1 = string.Join(", ", largeArray);
stopwatch.Stop();
Console.WriteLine($"string.Join: {stopwatch.ElapsedMilliseconds}ms");
// Method 2: StringBuilder (better for custom formatting)
stopwatch.Restart();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < largeArray.Length; i++)
{
if (i > 0) sb.Append(", ");
sb.Append(largeArray[i]);
}
string result2 = sb.ToString();
stopwatch.Stop();
Console.WriteLine($"StringBuilder: {stopwatch.ElapsedMilliseconds}ms");
// Method 3: StringBuilder with capacity optimization
stopwatch.Restart();
StringBuilder optimizedSb = new StringBuilder(largeArray.Length * 10); // Estimate capacity
for (int i = 0; i < largeArray.Length; i++)
{
if (i > 0) optimizedSb.Append(", ");
optimizedSb.Append(largeArray[i]);
}
string result3 = optimizedSb.ToString();
stopwatch.Stop();
Console.WriteLine($"Optimized StringBuilder: {stopwatch.ElapsedMilliseconds}ms");
// Verify results are the same
Console.WriteLine($"\nResults are equal: {result1 == result2 && result2 == result3}");
Console.WriteLine($"Result length: {result1.Length:N0} characters");
}
// 8. Splitting with count limit
public static void SplitWithLimit()
{
Console.WriteLine("\n=== Splitting with Count Limit ===");
string text = "One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten";
Console.WriteLine($"Original text: {text}");
// Split with limit
string[] limitedSplit = text.Split(',', 4);
Console.WriteLine("\nSplit with max 4 parts:");
for (int i = 0; i < limitedSplit.Length; i++)
{
Console.WriteLine($" Part {i + 1}: '{limitedSplit[i]}'");
}
// Split with limit and remove empty
string textWithEmpty = "A,B,,D,E,,F";
string[] limitedAndClean = textWithEmpty.Split(new[] { ',' }, 5, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine($"
Original: '{textWithEmpty}'");
Console.WriteLine($"Split with max 5 parts, removing empty:");
for (int i = 0; i < limitedAndClean.Length; i++)
{
Console.WriteLine($" Part {i + 1}: '{limitedAndClean[i]}'");
}
}
// 9. Custom split and join functions
public static void CustomSplitJoinFunctions()
{
Console.WriteLine("\n=== Custom Split and Join Functions ===");
string text = "word1 word2 word3 word4
word5 word6";
Console.WriteLine($"Original text: '{text}'");
// Custom split by whitespace
string[] words = CustomSplitByWhitespace(text);
Console.WriteLine("\nCustom whitespace split:");
for (int i = 0; i < words.Length; i++)
{
Console.WriteLine($" Word {i + 1}: '{words[i]}'");
}
// Custom join with prefix
string prefixed = CustomJoinWithPrefix(words, "=> ", " <=");
Console.WriteLine($"\nCustom prefixed join: '{prefixed}'");
// Custom split with quote handling
string csvWithQuotes = "\"John Doe\",\"New York\",\"Engineer\",\"75000\",\"Jane Smith\",\"Boston\",\"Designer\",\"65000\"";
string[] csvFields = CustomCsvSplit(csvWithQuotes);
Console.WriteLine($"
CSV with quotes: '{csvWithQuotes}'");
Console.WriteLine("Parsed fields:");
for (int i = 0; i < csvFields.Length; i++)
{
Console.WriteLine($" Field {i + 1}: '{csvFields[i]}'");
}
}
// Helper methods
static string[] CustomSplitByWhitespace(string text)
{
return text.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
}
static string CustomJoinWithPrefix(string[] items, string prefix, string suffix)
{
if (items.Length == 0) return string.Empty;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < items.Length; i++)
{
if (i > 0) sb.Append(" ");
sb.Append(prefix).Append(items[i]).Append(suffix);
}
return sb.ToString();
}
static string[] CustomCsvSplit(string csv)
{
var fields = new System.Collections.Generic.List<string>();
bool inQuotes = false;
StringBuilder currentField = new StringBuilder();
for (int i = 0; i < csv.Length; i++)
{
char c = csv[i];
if (c == '"')
{
if (inQuotes && i + 1 < csv.Length && csv[i + 1] == '"')
{
// Escaped quote
currentField.Append('"');
i++; // Skip next quote
}
else
{
// Toggle quote state
inQuotes = !inQuotes;
}
}
else if (c == ',' && !inQuotes)
{
// Field separator
fields.Add(currentField.ToString());
currentField.Clear();
}
else
{
currentField.Append(c);
}
}
// Add last field
fields.Add(currentField.ToString());
return fields.ToArray();
}
// 10. Real-world example: Log file processing
public static void LogFileProcessing()
{
Console.WriteLine("\n=== Log File Processing Example ===");
string logContent = @"2024-01-15 10:30:45 [INFO] User login successful: john_doe
2024-01-15 10:31:12 [WARN] Password expiring soon: jane_smith
2024-01-15 10:32:01 [ERROR] Database connection failed: TimeoutException
2024-01-15 10:32:15 [INFO] User logout: john_doe
2024-01-15 10:33:22 [DEBUG] Cache cleared: 256 entries removed
2024-01-15 10:34:05 [ERROR] File not found: /path/to/missing/file.txt";
Console.WriteLine("Log File Content:");
Console.WriteLine(logContent);
Console.WriteLine();
// Parse log entries
string[] logLines = logContent.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries);
var logEntries = logLines.Select(line =>
{
string[] parts = line.Split(new[] { ' ' }, 4);
if (parts.Length >= 4)
{
return new
{
Timestamp = $"{parts[0]} {parts[1]}",
Level = parts[2].Trim('[', ']'),
Message = parts[3]
};
}
return null;
}).Where(entry => entry != null).ToList();
Console.WriteLine("Parsed Log Entries:");
foreach (var entry in logEntries)
{
Console.WriteLine($"[{entry.Timestamp}] {entry.Level}: {entry.Message}");
}
// Group by log level
Console.WriteLine("\nLog Summary by Level:");
var groupedLogs = logEntries.GroupBy(e => e.Level)
.Select(g => new { Level = g.Key, Count = g.Count() })
.OrderByDescending(g => g.Count);
foreach (var group in groupedLogs)
{
Console.WriteLine($"{group.Level}: {group.Count} entries");
}
// Export to CSV
string csvHeader = "Timestamp,Level,Message";
string csvRows = string.Join("\n", logEntries.Select(e =>
$"\{e.Timestamp}\,\{e.Level}\,\{e.Message.Replace("\"", "\"\"")}\""));
string csvOutput = csvHeader + "\n" + csvRows;
Console.WriteLine("\nCSV Export:");
Console.WriteLine(csvOutput);
}
static void Main(string[] args)
{
Console.WriteLine("=== C# String Splitting and Joining Demo ===\n");
try
{
BasicStringSplitting();
MultipleDelimiterSplitting();
AdvancedSplitOptions();
StringJoining();
ComplexParsing();
StringSeparatorSplitting();
StringBuilderJoining();
SplitWithLimit();
CustomSplitJoinFunctions();
LogFileProcessing();
Console.WriteLine("\nAll string splitting and joining examples completed successfully!");
}
catch (Exception ex)
{
Console.WriteLine($"Error in string operations demo: {ex.Message}");
}
}
}Regular Expressions and Pattern Matching
Advanced regex operations including pattern matching, validation, extraction, and replacement in C#
Difficulty
6/10
Estimated time
30 min
Tags
csharp, regex, pattern matching, text processing, windows
Prerequisites
C# string basics, Regex fundamentals
using System;
using System.Text.RegularExpressions;
using System.Linq;
class RegexPatternMatching
{
// 1. Basic regex pattern matching
public static void BasicRegexMatching()
{
Console.WriteLine("=== Basic Regex Pattern Matching ===");
string text = "The quick brown fox jumps over the lazy dog. The fox is clever.";
string pattern = @"fox";
Console.WriteLine($"Text: {text}");
Console.WriteLine($"Pattern: {pattern}");
// Check if pattern exists
bool containsPattern = Regex.IsMatch(text, pattern);
Console.WriteLine($"Contains 'fox': {containsPattern}");
// Find all matches
MatchCollection matches = Regex.Matches(text, pattern);
Console.WriteLine($"\nFound {matches.Count} matches:");
foreach (Match match in matches)
{
Console.WriteLine($" Match at position {match.Index}: '{match.Value}'");
}
// Find first match
Match firstMatch = Regex.Match(text, pattern);
if (firstMatch.Success)
{
Console.WriteLine($"
First match: '{firstMatch.Value}' at position {firstMatch.Index}");
}
}
// 2. Email validation with regex
public static void EmailValidation()
{
Console.WriteLine("\n=== Email Validation ===");
string emailPattern = @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$";
string[] testEmails = {
"[email protected]",
"[email protected]",
"invalid.email",
"user@localhost",
"[email protected]",
"[email protected]",
"[email protected]",
"user@domain."
};
Console.WriteLine("Testing email validation:");
foreach (string email in testEmails)
{
bool isValid = Regex.IsMatch(email, emailPattern);
Console.WriteLine($" {email,-30} => {(isValid ? "Valid" : "Invalid")}");
}
}
// 3. Phone number validation and formatting
public static void PhoneNumberProcessing()
{
Console.WriteLine("\n=== Phone Number Processing ===");
// Various phone number formats
string[] phoneNumbers = {
"123-456-7890",
"(123) 456-7890",
"123.456.7890",
"1234567890",
"+1 (123) 456-7890",
"1-123-456-7890",
"invalid-number"
};
// Regex pattern for US phone numbers
string phonePattern = @"^(+?1[s-]?)?(?([0-9]{3}))?[s-]?([0-9]{3})[s-]?([0-9]{4})$";
Console.WriteLine("Phone number validation and formatting:");
foreach (string phone in phoneNumbers)
{
Match match = Regex.Match(phone, phonePattern);
if (match.Success)
{
string areaCode = match.Groups[2].Value;
string prefix = match.Groups[3].Value;
string lineNumber = match.Groups[4].Value;
string formatted = $"({areaCode}) {prefix}-{lineNumber}";
Console.WriteLine($" {phone,-20} => Valid -> {formatted}");
}
else
{
Console.WriteLine($" {phone,-20} => Invalid");
}
}
}
// 4. Password strength validation
public static void PasswordStrengthValidation()
{
Console.WriteLine("\n=== Password Strength Validation ===");
string[] passwords = {
"password",
"Password123",
"MySecurePass!",
"ComplexP@ssw0rd#2024",
"12345678",
"Abc123",
"VeryLongPasswordWithNumbers123AndSymbols!"
};
// Password strength requirements
string minLengthPattern = @".{8,}"; // At least 8 characters
string uppercasePattern = @"[A-Z]"; // At least one uppercase
string lowercasePattern = @"[a-z]"; // At least one lowercase
string digitPattern = @"[0-9]"; // At least one digit
string specialPattern = @"[!@#$%^&*()_+-=[]{};':""\|,.<>/?]"; // At least one special char
Console.WriteLine("Password strength analysis:");
foreach (string password in passwords)
{
bool hasMinLength = Regex.IsMatch(password, minLengthPattern);
bool hasUppercase = Regex.IsMatch(password, uppercasePattern);
bool hasLowercase = Regex.IsMatch(password, lowercasePattern);
bool hasDigit = Regex.IsMatch(password, digitPattern);
bool hasSpecial = Regex.IsMatch(password, specialPattern);
int strength = 0;
if (hasMinLength) strength++;
if (hasUppercase) strength++;
if (hasLowercase) strength++;
if (hasDigit) strength++;
if (hasSpecial) strength++;
string strengthLevel = strength switch
{
5 => "Very Strong",
4 => "Strong",
3 => "Medium",
2 => "Weak",
1 => "Very Weak",
_ => "Invalid"
};
Console.WriteLine($" {password,-35} => {strengthLevel}");
Console.WriteLine($" Min Length: {hasMinLength}, Upper: {hasUppercase}, Lower: {hasLowercase}, Digit: {hasDigit}, Special: {hasSpecial}");
}
}
// 5. Extracting data from text
public static void DataExtraction()
{
Console.WriteLine("\n=== Data Extraction ===");
string text = @"Contact Information:
John Doe - Phone: (555) 123-4567, Email: [email protected]
Jane Smith - Phone: 555.987.6543, Email: [email protected]
Bob Johnson - Phone: +1 555 234 5678, Email: [email protected]";
Console.WriteLine("Original text:");
Console.WriteLine(text);
// Extract emails
string emailPattern = @"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Z|a-z]{2,}";
MatchCollection emailMatches = Regex.Matches(text, emailPattern);
Console.WriteLine("\nExtracted emails:");
foreach (Match match in emailMatches)
{
Console.WriteLine($" {match.Value}");
}
// Extract phone numbers
string phonePattern = @"(?([0-9]{3}))?[s-]?([0-9]{3})[s-]?([0-9]{4})";
MatchCollection phoneMatches = Regex.Matches(text, phonePattern);
Console.WriteLine("\nExtracted phone numbers:");
foreach (Match match in phoneMatches)
{
string formattedPhone = $"({match.Groups[1]}) {match.Groups[2]}-{match.Groups[3]}";
Console.WriteLine($" {match.Value} -> {formattedPhone}");
}
// Extract names (simple pattern)
string namePattern = @"([A-Z][a-z]+ [A-Z][a-z]+)";
MatchCollection nameMatches = Regex.Matches(text, namePattern);
Console.WriteLine("\nExtracted names:");
foreach (Match match in nameMatches)
{
Console.WriteLine($" {match.Value}");
}
}
// 6. String replacement with regex
public static void StringReplacement()
{
Console.WriteLine("\n=== String Replacement ===");
string text = "Visit our website at http://example.com or email us at [email protected]. Check out https://secure.example.com for secure access.";
Console.WriteLine("Original text:");
Console.WriteLine(text);
// Replace URLs with HTML links
string urlPattern = @"(https?://[^s]+)";
string withLinks = Regex.Replace(text, urlPattern, "<a href='$1'>$1</a>");
Console.WriteLine("\nWith HTML links:");
Console.WriteLine(withLinks);
// Censor bad words
string badWordsPattern = @"(stupid|idiot|dumb)";
string cleanText = "That was a stupid idea, not idiotic, but definitely not dumb!";
string censoredText = Regex.Replace(cleanText, badWordsPattern, m => new string('*', m.Length), RegexOptions.IgnoreCase);
Console.WriteLine($"\nOriginal: {cleanText}");
Console.WriteLine($"Censored: {censoredText}");
// Format currency
string numbersText = "Prices: $1000, $25000, $500000, $7500000";
string formattedNumbers = Regex.Replace(numbersText, @"$(d+)", m =>
{
if (decimal.TryParse(m.Groups[1].Value, out decimal amount))
{
return $"${amount:N0}";
}
return m.Value;
});
Console.WriteLine($"\nNumbers: {numbersText}");
Console.WriteLine($"Formatted: {formattedNumbers}");
}
// 7. Advanced pattern: HTML tag processing
public static void HtmlTagProcessing()
{
Console.WriteLine("\n=== HTML Tag Processing ===");
string html = @"<div class='container'>
<h1>Welcome to <span style='color: red'>Our Website</span></h1>
<p>This is a <strong>paragraph</strong> with <em>emphasis</em>.</p>
<a href='https://example.com'>Click here</a>
<img src='image.jpg' alt='Sample Image' />
</div>";
Console.WriteLine("HTML content:");
Console.WriteLine(html);
// Extract all tag names
string tagPattern = @"<s*(w+)(?:s+[^>]*)?>";
MatchCollection tagMatches = Regex.Matches(html, tagPattern);
Console.WriteLine("\nAll HTML tags found:");
foreach (Match match in tagMatches)
{
Console.WriteLine($" {match.Groups[1].Value}");
}
// Extract attribute values
string attributePattern = @"(w+)s*=s*['""](.*?)['""]";
MatchCollection attributeMatches = Regex.Matches(html, attributePattern);
Console.WriteLine("\nHTML attributes:");
foreach (Match match in attributeMatches)
{
Console.WriteLine($" {match.Groups[1].Value} = '{match.Groups[2].Value}'");
}
// Remove all HTML tags (get plain text)
string plainText = Regex.Replace(html, @"<[^>]*>", "");
plainText = Regex.Replace(plainText, @"s+", " ").Trim();
Console.WriteLine($"\nPlain text: {plainText}");
}
// 8. Log file parsing with regex
public static void LogFileParsing()
{
Console.WriteLine("\n=== Log File Parsing ===");
string[] logLines = {
"2024-01-15 10:30:45.123 [INFO] User login: john_doe from 192.168.1.100",
"2024-01-15 10:31:12.456 [ERROR] Database connection failed: Timeout after 30s",
"2024-01-15 10:32:01.789 [WARN] High CPU usage: 95% on server-web-01",
"2024-01-15 10:33:22.012 [INFO] User logout: jane_smith session ended"
};
// Log pattern: timestamp [level] message
string logPattern = @"^(d{4}-d{2}-d{2}sd{2}:d{2}:d{2}.d{3})s[([A-Z]+)]s(.*)$";
Console.WriteLine("Parsed log entries:");
foreach (string line in logLines)
{
Match match = Regex.Match(line, logPattern);
if (match.Success)
{
string timestamp = match.Groups[1].Value;
string level = match.Groups[2].Value;
string message = match.Groups[3].Value;
Console.WriteLine($" Timestamp: {timestamp}");
Console.WriteLine($" Level: {level}");
Console.WriteLine($" Message: {message}");
// Extract IP addresses if present
if (Regex.IsMatch(message, @"d{1,3}.d{1,3}.d{1,3}.d{1,3}"))
{
string ipPattern = @"(d{1,3}.d{1,3}.d{1,3}.d{1,3})";
Match ipMatch = Regex.Match(message, ipPattern);
Console.WriteLine($" IP Address: {ipMatch.Groups[1].Value}");
}
// Extract percentages if present
if (Regex.IsMatch(message, @"d+%"))
{
string percentPattern = @"(d+)%";
Match percentMatch = Regex.Match(message, percentPattern);
Console.WriteLine($" Percentage: {percentMatch.Groups[1].Value}%");
}
Console.WriteLine();
}
}
}
// 9. Performance considerations
public static void RegexPerformance()
{
Console.WriteLine("\n=== Regex Performance Considerations ===");
string largeText = string.Join(" ", Enumerable.Range(1, 10000)
.Select(i => $"Word{i} testing{Math.Pow(i, 2)} pattern{i%100}"));
// Pattern to find all words ending with numbers
string pattern = @"w+d+";
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
// Test 1: Compiled regex (reuse)
stopwatch.Restart();
Regex compiledRegex = new Regex(pattern, RegexOptions.Compiled);
MatchCollection compiledMatches = compiledRegex.Matches(largeText);
stopwatch.Stop();
Console.WriteLine($"Compiled regex: {stopwatch.ElapsedMilliseconds}ms, Found {compiledMatches.Count} matches");
// Test 2: Static regex method (compiled internally)
stopwatch.Restart();
MatchCollection staticMatches = Regex.Matches(largeText, pattern);
stopwatch.Stop();
Console.WriteLine($"Static regex: {stopwatch.ElapsedMilliseconds}ms, Found {staticMatches.Count} matches");
// Test 3: Non-compiled regex
stopwatch.Restart();
Regex nonCompiledRegex = new Regex(pattern);
MatchCollection nonCompiledMatches = nonCompiledRegex.Matches(largeText);
stopwatch.Stop();
Console.WriteLine($"Non-compiled regex: {stopwatch.ElapsedMilliseconds}ms, Found {nonCompiledMatches.Count} matches");
// Test 4: RegexOptions.IgnoreCase impact
string textWithCases = "Hello hello HELLO HeLLo";
string casePattern = "hello";
stopwatch.Restart();
bool caseSensitive = Regex.IsMatch(textWithCases, casePattern);
stopwatch.Stop();
Console.WriteLine($"
Case-sensitive: {stopwatch.ElapsedTicks} ticks, Found: {caseSensitive}");
stopwatch.Restart();
bool caseInsensitive = Regex.IsMatch(textWithCases, casePattern, RegexOptions.IgnoreCase);
stopwatch.Stop();
Console.WriteLine($"Case-insensitive: {stopwatch.ElapsedTicks} ticks, Found: {caseInsensitive}");
}
// 10. Custom regex patterns
public static void CustomRegexPatterns()
{
Console.WriteLine("\n=== Custom Regex Patterns ===");
// Credit card number validation
string creditCardPattern = @"(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3[0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})";
string[] creditCards = { "4111111111111111", "5105105105105100", "378282246310005", "1234567890123456", "invalid-card" };
Console.WriteLine("Credit card validation:");
foreach (string card in creditCards)
{
bool isValid = Regex.IsMatch(card, creditCardPattern);
string masked = card.Length > 4 ? "****-****-****-" + card.Substring(card.Length - 4) : card;
Console.WriteLine($" {masked} => {(isValid ? "Valid" : "Invalid")}");
}
// Social Security Number (US) format
string ssnPattern = @"d{3}-d{2}-d{4}";
string text = "John's SSN: 123-45-6789, Jane's SSN: 987-65-4321";
Console.WriteLine($"
SSN extraction from: {text}");
MatchCollection ssnMatches = Regex.Matches(text, ssnPattern);
foreach (Match match in ssnMatches)
{
string ssn = match.Value;
string masked = ssn.Substring(0, 7) + "****";
Console.WriteLine($" {masked}");
}
// Hex color codes
string hexColorPattern = @"#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})";
string cssText = "colors: #FF0000, #00FF00, #0000FF, #ABC, #123456, #invalid";
Console.WriteLine($"
Hex color codes in: {cssText}");
MatchCollection colorMatches = Regex.Matches(cssText, hexColorPattern);
foreach (Match match in colorMatches)
{
Console.WriteLine($" {match.Value}");
}
}
static void Main(string[] args)
{
Console.WriteLine("=== C# Regular Expressions Demo ===\n");
try
{
BasicRegexMatching();
EmailValidation();
PhoneNumberProcessing();
PasswordStrengthValidation();
DataExtraction();
StringReplacement();
HtmlTagProcessing();
LogFileParsing();
RegexPerformance();
CustomRegexPatterns();
Console.WriteLine("\nAll regex pattern matching examples completed successfully!");
}
catch (Exception ex)
{
Console.WriteLine($"Error in regex demo: {ex.Message}");
}
}
}String Replacement and Transformation
Advanced string replacement techniques including formatting, case conversion, encoding, and text transformation
Difficulty
5/10
Estimated time
25 min
Tags
csharp, string, replacement, formatting, text processing, windows
Prerequisites
C# string operations, Regular expressions basics, LINQ
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
class StringReplacement
{
// 1. Basic string replacement
public static void BasicStringReplacement()
{
Console.WriteLine("=== Basic String Replacement ===");
string text = "The quick brown fox jumps over the lazy dog. The fox is clever.";
Console.WriteLine($"Original text: {text}");
// Replace simple string
string replaced = text.Replace("fox", "cat");
Console.WriteLine($"Replace 'fox' with 'cat': {replaced}");
// Replace case-insensitive
string caseInsensitive = Regex.Replace(text, "the", "a", RegexOptions.IgnoreCase);
Console.WriteLine($"Replace 'the' (case-insensitive) with 'a': {caseInsensitive}");
// Replace only whole words
string wholeWord = Regex.Replace(text, @"the", "a", RegexOptions.IgnoreCase);
Console.WriteLine($"Replace whole word 'the' with 'a': {wholeWord}");
}
// 2. Case conversion operations
public static void CaseConversion()
{
Console.WriteLine("\n=== Case Conversion ===");
string text = "Hello World! This is a TEST of cAsE ConVerSiOn.";
Console.WriteLine($"Original: {text}");
Console.WriteLine($"ToUpper(): {text.ToUpper()}");
Console.WriteLine($"ToLower(): {text.ToLower()}");
Console.WriteLine($"ToTitleCase(): {ToTitleCase(text)}");
// Custom case conversion
Console.WriteLine($"\nCustom conversions:");
Console.WriteLine($"Toggle Case: {ToggleCase(text)}");
Console.WriteLine($"Camel Case: {ToCamelCase(text)}");
Console.WriteLine($"Snake Case: {ToSnakeCase(text)}");
Console.WriteLine($"Kebab Case: {ToKebabCase(text)}");
}
// 3. String formatting and template replacement
public static void StringFormatting()
{
Console.WriteLine("\n=== String Formatting ===");
// String.Format
string name = "John";
int age = 30;
string city = "New York";
string formatted = string.Format("Name: {0}, Age: {1}, City: {2}", name, age, city);
Console.WriteLine($"String.Format: {formatted}");
// String interpolation
string interpolated = $"Name: {name}, Age: {age}, City: {city}";
Console.WriteLine($"Interpolation: {interpolated}");
// Numeric formatting
double price = 1234.56789;
Console.WriteLine($"Price formatted: {price:C2}"); // Currency
Console.WriteLine($"Price formatted: {price:N2}"); // Number with separators
Console.WriteLine($"Price formatted: {price:F3}"); // Fixed decimal
Console.WriteLine($"Price formatted: {price:P1}"); // Percentage
// Date formatting
DateTime now = DateTime.Now;
Console.WriteLine($"Date: {now:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"Date: {now:dddd, MMMM d, yyyy}");
// Custom template replacement
string template = "Dear {{name}}, your order #{{order}} for {{quantity}} {{product}} is {{status}}.";
Dictionary<string, string> values = new Dictionary<string, string>
{
{ "name", "Jane Doe" },
{ "order", "12345" },
{ "quantity", "3" },
{ "product", "widgets" },
{ "status", "shipped" }
};
string result = ReplaceTemplate(template, values);
Console.WriteLine($"\nTemplate replacement: {result}");
}
// 4. String padding and alignment
public static void StringPaddingAlignment()
{
Console.WriteLine("\n=== String Padding and Alignment ===");
string[] words = { "Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig" };
Console.WriteLine("Left-aligned (width 12):");
foreach (string word in words)
{
Console.WriteLine($"'{word.PadRight(12)}'");
}
Console.WriteLine("\nRight-aligned (width 12):");
foreach (string word in words)
{
Console.WriteLine($"'{word.PadLeft(12)}'");
}
Console.WriteLine("\nCenter-aligned (width 12):");
foreach (string word in words)
{
Console.WriteLine($"'{PadCenter(word, 12)}'");
}
// Numeric formatting with alignment
double[] numbers = { 123.45, 67.89, 1234.56, 1.23, 98765.43 };
Console.WriteLine("\nNumbers aligned:");
Console.WriteLine("Aligned right:");
foreach (double num in numbers)
{
Console.WriteLine($"{num,10:N2}");
}
Console.WriteLine("Aligned left:");
foreach (double num in numbers)
{
Console.WriteLine($"{num,-10:N2}");
}
}
// 5. String cleaning and normalization
public static void StringCleaningNormalization()
{
Console.WriteLine("\n=== String Cleaning and Normalization ===");
string dirtyText = " Hello World! \n\t This has extra spaces. ";
Console.WriteLine($"Original: '{dirtyText}'");
// Trim and normalize spaces
string cleaned = string.Join(" ", dirtyText.Split(new[] { ' ', '\t', '\n', '\r' },
StringSplitOptions.RemoveEmptyEntries));
Console.WriteLine($"Cleaned: '{cleaned}'");
// Remove all non-alphanumeric characters
string text = "Hello, World! 123 @#$%^&*()";
string alphanumeric = new string(text.Where(char.IsLetterOrDigit).ToArray());
Console.WriteLine($"\nOriginal: '{text}'");
Console.WriteLine($"Alphanumeric only: '{alphanumeric}'");
// Remove diacritics (accents)
string accented = "Café naïve résumé";
string normalized = RemoveDiacritics(accented);
Console.WriteLine($"\nWith accents: '{accented}'");
Console.WriteLine($"Without accents: '{normalized}'");
// Remove HTML tags
string html = "<p>This is <strong>bold</strong> and <em>italic</em> text.</p>";
string plainText = Regex.Replace(html, @"<[^>]*>", "");
Console.WriteLine($"\nHTML: '{html}'");
Console.WriteLine($"Plain text: '{plainText}'");
}
// 6. Advanced replacement with callbacks
public static void AdvancedReplacement()
{
Console.WriteLine("\n=== Advanced Replacement ===");
string text = "I have 2 apples, 3 bananas, and 5 oranges.";
Console.WriteLine($"Original: {text}");
// Replace numbers with their squares
string squared = Regex.Replace(text, @"d+", m =>
{
int number = int.Parse(m.Value);
return (number * number).ToString();
});
Console.WriteLine($"Numbers squared: {squared}");
// Replace words with their lengths
string wordLengths = Regex.Replace(text, @"[a-zA-Z]+", m =>
{
return m.Value.Length.ToString();
});
Console.WriteLine($"Word lengths: {wordLengths}");
// Censor bad words with dynamic replacement
string sentence = "This is stupid and dumb, but not idiotic.";
string censored = Regex.Replace(sentence, @"(stupid|dumb|idiotic)", m =>
{
return new string('*', m.Length);
}, RegexOptions.IgnoreCase);
Console.WriteLine($"\nOriginal: {sentence}");
Console.WriteLine($"Censored: {censored}");
// Format currency
string moneyText = "I have $1000, you have $25000, they have $500000";
string formatted = Regex.Replace(moneyText, @"$([d,]+)", m =>
{
string cleanNumber = m.Groups[1].Value.Replace(",", "");
if (decimal.TryParse(cleanNumber, out decimal amount))
{
return $"${amount:N0}";
}
return m.Value;
});
Console.WriteLine($"\nMoney text: {moneyText}");
Console.WriteLine($"Formatted: {formatted}");
}
// 7. String encoding and decoding
public static void StringEncodingDecoding()
{
Console.WriteLine("\n=== String Encoding and Decoding ===");
string original = "Hello, 世界! 🌍";
Console.WriteLine($"Original: {original}");
// Convert to bytes and back
byte[] utf8Bytes = Encoding.UTF8.GetBytes(original);
string fromUtf8 = Encoding.UTF8.GetString(utf8Bytes);
Console.WriteLine($"UTF-8 round-trip: {fromUtf8}");
// URL encoding
string urlText = "Hello World! @#$%^&*()";
string urlEncoded = Uri.EscapeDataString(urlText);
string urlDecoded = Uri.UnescapeDataString(urlEncoded);
Console.WriteLine($"
URL: {urlText}");
Console.WriteLine($"Encoded: {urlEncoded}");
Console.WriteLine($"Decoded: {urlDecoded}");
// Base64 encoding
string base64Encoded = Convert.ToBase64String(utf8Bytes);
byte[] base64Decoded = Convert.FromBase64String(base64Encoded);
string fromBase64 = Encoding.UTF8.GetString(base64Decoded);
Console.WriteLine($"
Base64 encoded: {base64Encoded}");
Console.WriteLine($"Base64 decoded: {fromBase64}");
// HTML encoding
string htmlText = "<script>alert('XSS');</script>";
string htmlEncoded = System.Web.HttpUtility.HtmlEncode(htmlText);
string htmlDecoded = System.Web.HttpUtility.HtmlDecode(htmlEncoded);
Console.WriteLine($"
HTML: {htmlText}");
Console.WriteLine($"HTML encoded: {htmlEncoded}");
Console.WriteLine($"HTML decoded: {htmlDecoded}");
}
// 8. String comparison and similarity
public static void StringComparisonSimilarity()
{
Console.WriteLine("\n=== String Comparison and Similarity ===");
string str1 = "Hello World";
string str2 = "hello world";
string str3 = "Hello World!";
string str4 = "Hello Universe";
Console.WriteLine($"str1: '{str1}'");
Console.WriteLine($"str2: '{str2}'");
Console.WriteLine($"str3: '{str3}'");
Console.WriteLine($"str4: '{str4}'");
// Different comparison methods
Console.WriteLine($"
Comparisons:");
Console.WriteLine($"str1 == str2: {str1 == str2}");
Console.WriteLine($"str1.Equals(str2): {str1.Equals(str2)}");
Console.WriteLine($"str1.Equals(str2, StringComparison.OrdinalIgnoreCase): {str1.Equals(str2, StringComparison.OrdinalIgnoreCase)}");
// Levenshtein distance (edit distance)
Console.WriteLine($"
Levenshtein distances:");
Console.WriteLine($"str1 vs str2: {LevenshteinDistance(str1, str2)}");
Console.WriteLine($"str1 vs str3: {LevenshteinDistance(str1, str3)}");
Console.WriteLine($"str1 vs str4: {LevenshteinDistance(str1, str4)}");
// Jaccard similarity
Console.WriteLine($"
Jaccard similarities:");
Console.WriteLine($"str1 vs str2: {JaccardSimilarity(str1, str2):F2}");
Console.WriteLine($"str1 vs str3: {JaccardSimilarity(str1, str3):F2}");
Console.WriteLine($"str1 vs str4: {JaccardSimilarity(str1, str4):F2}");
}
// 9. String tokenization and parsing
public static void StringTokenizationParsing()
{
Console.WriteLine("\n=== String Tokenization and Parsing ===");
// CSV parsing with quotes
string csv = "\"John Doe\",\"New York\",\"Engineer\",\"75000\",\"Jane Smith\",\"Boston\",\"Designer\",\"65000\"";
var csvFields = ParseCsv(csv);
Console.WriteLine($"CSV: {csv}");
Console.WriteLine("Parsed fields:");
for (int i = 0; i < csvFields.Length; i++)
{
Console.WriteLine($" {i}: '{csvFields[i]}'");
}
// JSON-like parsing (simplified)
string jsonLike = "name:\"John\", age:30, city:\"New York\", active:true";
var dict = ParseJsonLike(jsonLike);
Console.WriteLine($"
JSON-like: {jsonLike}");
Console.WriteLine("Parsed key-value pairs:");
foreach (var kvp in dict)
{
Console.WriteLine($" {kvp.Key}: {kvp.Value}");
}
// Tokenize by multiple separators
string text = "Word1,word2;word3 word4|word5";
char[] separators = { ',', ';', ' ', '|' };
var tokens = text.Split(separators, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine($"
Original: {text}");
Console.WriteLine("Tokens:");
for (int i = 0; i < tokens.Length; i++)
{
Console.WriteLine($" {i}: '{tokens[i]}'");
}
}
// 10. Real-world example: Template email generator
public static void EmailTemplateGenerator()
{
Console.WriteLine("\n=== Email Template Generator ===");
string template = @"
Dear {{customer_name}},
Thank you for your order #{{order_id}} placed on {{order_date}}.
Order Details:
{{#items}}
- {{name}} (Quantity: {{quantity}}, Price: ${{price}})
{{/items}}
Subtotal: ${{subtotal}}
Tax ({{tax_rate}}%): ${{tax}}
Total: ${{total}}
Your order will be shipped to:
{{shipping_address}}
{{#tracking_number}}
Tracking Number: {{tracking_number}}
{{/tracking_number}}
Best regards,
{{company_name}}
{{#support_email}}
Support: {{support_email}}
{{/support_email}}
";
// Template data
var data = new System.Collections.Generic.Dictionary<string, object>
{
{ "customer_name", "John Doe" },
{ "order_id", "ORD-2024-001" },
{ "order_date", DateTime.Now.ToString("yyyy-MM-dd") },
{ "items", new[]
{
new { name = "Laptop", quantity = 1, price = "999.99" },
new { name = "Mouse", quantity = 2, price = "25.50" },
new { name = "Keyboard", quantity = 1, price = "75.00" }
}
},
{ "subtotal", "1125.99" },
{ "tax_rate", "8.25" },
{ "tax", "92.89" },
{ "total", "1218.88" },
{ "shipping_address", "123 Main St\nNew York, NY 10001" },
{ "tracking_number", "1Z999AA10123456784" },
{ "company_name", "TechStore Inc." },
{ "support_email", "[email protected]" }
};
string email = ProcessEmailTemplate(template, data);
Console.WriteLine("Generated Email:");
Console.WriteLine(email);
}
// Helper methods
static string ToTitleCase(string input)
{
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
return ti.ToTitleCase(input.ToLower());
}
static string ToggleCase(string input)
{
return new string(input.Select(c => char.IsUpper(c) ? char.ToLower(c) : char.ToUpper(c)).ToArray());
}
static string ToCamelCase(string input)
{
string[] words = Regex.Split(input, @"[^a-zA-Z0-9]");
return string.Concat(words.Where(w => w.Length > 0)
.Select((w, i) => i == 0 ? w.ToLower() : char.ToUpper(w[0]) + w.Substring(1).ToLower()));
}
static string ToSnakeCase(string input)
{
return string.Join("_", Regex.Split(input, @"(?=[A-Z])").Select(w => w.ToLower()));
}
static string ToKebabCase(string input)
{
return string.Join("-", Regex.Split(input, @"(?=[A-Z])").Select(w => w.ToLower()));
}
static string PadCenter(string input, int totalWidth)
{
int padding = totalWidth - input.Length;
int leftPadding = padding / 2;
int rightPadding = padding - leftPadding;
return input.PadLeft(input.Length + leftPadding).PadRight(totalWidth);
}
static string ReplaceTemplate(string template, Dictionary<string, string> values)
{
string result = template;
foreach (var kvp in values)
{
result = result.Replace($"{{{kvp.Key}}}", kvp.Value);
}
return result;
}
static string RemoveDiacritics(string input)
{
string normalized = input.Normalize(NormalizationForm.FormD);
return new string(normalized.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark).ToArray());
}
static int LevenshteinDistance(string s1, string s2)
{
int[,] matrix = new int[s1.Length + 1, s2.Length + 1];
for (int i = 0; i <= s1.Length; i++)
matrix[i, 0] = i;
for (int j = 0; j <= s2.Length; j++)
matrix[0, j] = j;
for (int i = 1; i <= s1.Length; i++)
{
for (int j = 1; j <= s2.Length; j++)
{
int cost = s1[i - 1] == s2[j - 1] ? 0 : 1;
matrix[i, j] = Math.Min(
Math.Min(matrix[i - 1, j] + 1, matrix[i, j - 1] + 1),
matrix[i - 1, j - 1] + cost);
}
}
return matrix[s1.Length, s2.Length];
}
static double JaccardSimilarity(string s1, string s2)
{
var set1 = new HashSet<char>(s1.ToLower());
var set2 = new HashSet<char>(s2.ToLower());
var intersection = new HashSet<char>(set1);
intersection.IntersectWith(set2);
var union = new HashSet<char>(set1);
union.UnionWith(set2);
return union.Count > 0 ? (double)intersection.Count / union.Count : 0;
}
static string[] ParseCsv(string csv)
{
var fields = new List<string>();
bool inQuotes = false;
StringBuilder currentField = new StringBuilder();
for (int i = 0; i < csv.Length; i++)
{
char c = csv[i];
if (c == '"')
{
if (inQuotes && i + 1 < csv.Length && csv[i + 1] == '"')
{
currentField.Append('"');
i++;
}
else
{
inQuotes = !inQuotes;
}
}
else if (c == ',' && !inQuotes)
{
fields.Add(currentField.ToString());
currentField.Clear();
}
else
{
currentField.Append(c);
}
}
fields.Add(currentField.ToString());
return fields.ToArray();
}
static Dictionary<string, string> ParseJsonLike(string json)
{
var dict = new Dictionary<string, string>();
var pairs = json.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string pair in pairs)
{
var parts = pair.Split(new[] { ':' }, 2);
if (parts.Length == 2)
{
string key = parts[0].Trim();
string value = parts[1].Trim().Trim('"');
dict[key] = value;
}
}
return dict;
}
static string ProcessEmailTemplate(string template, Dictionary<string, object> data)
{
string result = template;
// Simple variable replacement
foreach (var kvp in data)
{
if (kvp.Value is string || kvp.Value.GetType().IsPrimitive)
{
result = result.Replace($"{{{{{kvp.Key}}}}}", kvp.Value.ToString());
}
}
// Handle simple loops (simplified)
result = Regex.Replace(result, @"{{#(\w+)}}.*?{{/\1}}", m =>
{
string key = m.Groups[1].Value;
if (data.ContainsKey(key))
{
// Simplified handling - just show that there are items
var items = data[key] as Array;
return items?.Length > 0 ? $"
[{items.Length} items in {key}]
" : "";
}
return "";
});
return result;
}
static void Main(string[] args)
{
Console.WriteLine("=== C# String Replacement and Transformation Demo ===\n");
try
{
BasicStringReplacement();
CaseConversion();
StringFormatting();
StringPaddingAlignment();
StringCleaningNormalization();
AdvancedReplacement();
StringEncodingDecoding();
StringComparisonSimilarity();
StringTokenizationParsing();
EmailTemplateGenerator();
Console.WriteLine("\nAll string replacement examples completed successfully!");
}
catch (Exception ex)
{
Console.WriteLine($"Error in string replacement demo: {ex.Message}");
}
}
}Tools
Tools frequently paired with this sample
Related