3 - String Manipulation Algorithms
String manipulation algorithms transform, modify, or analyze strings in various ways. These algorithms are fundamental in text processing, data transformation, and many other applications.
String Reversal
String reversal is the process of reversing the order of characters in a string.
Implementation
public static string ReverseString(string input)
{
if (string.IsNullOrEmpty(input))
return input;
char[] charArray = input.ToCharArray();
int left = 0;
int right = charArray.Length - 1;
while (left < right)
{
// Swap characters
char temp = charArray[left];
charArray[left] = charArray[right];
charArray[right] = temp;
// Move towards the center
left++;
right--;
}
return new string(charArray);
}
// Alternative implementation using LINQ
public static string ReverseStringLinq(string input)
{
if (string.IsNullOrEmpty(input))
return input;
return new string(input.Reverse().ToArray());
}
Time and Space Complexity
- Time Complexity: O(n) where n is the length of the string.
- Space Complexity: O(n) for the new string.
Applications
- Palindrome Checking: Determining if a string reads the same forward and backward.
- Text Transformation: Creating special text effects.
- Algorithm Building Block: Used in more complex string algorithms.
Case Conversion
Case conversion changes the case of characters in a string, such as converting to uppercase, lowercase, or title case.
Implementation
public static string ToUpperCase(string input)
{
if (string.IsNullOrEmpty(input))
return input;
return input.ToUpper();
}
public static string ToLowerCase(string input)
{
if (string.IsNullOrEmpty(input))
return input;
return input.ToLower();
}
public static string ToTitleCase(string input)
{
if (string.IsNullOrEmpty(input))
return input;
// Using TextInfo for proper title casing
TextInfo textInfo = new CultureInfo("en-US", false).TextInfo;
return textInfo.ToTitleCase(input.ToLower());
}
// Custom implementation of title case
public static string ToTitleCaseCustom(string input)
{
if (string.IsNullOrEmpty(input))
return input;
char[] charArray = input.ToLower().ToCharArray();
bool newWord = true;
for (int i = 0; i < charArray.Length; i++)
{
if (char.IsLetter(charArray[i]))
{
if (newWord)
{
charArray[i] = char.ToUpper(charArray[i]);
newWord = false;
}
}
else
{
newWord = true;
}
}
return new string(charArray);
}
Time and Space Complexity
- Time Complexity: O(n) where n is the length of the string.
- Space Complexity: O(n) for the new string.
Applications
- User Interface: Displaying text in consistent formatting.
- Data Normalization: Standardizing text data for comparison or storage.
- Text Styling: Applying different text styles in documents.
String Padding
String padding adds characters to a string to make it reach a specified length.
Implementation
public static string PadLeft(string input, int totalWidth, char paddingChar = ' ')
{
if (string.IsNullOrEmpty(input))
return new string(paddingChar, totalWidth);
if (input.Length >= totalWidth)
return input;
int paddingLength = totalWidth - input.Length;
return new string(paddingChar, paddingLength) + input;
}
public static string PadRight(string input, int totalWidth, char paddingChar = ' ')
{
if (string.IsNullOrEmpty(input))
return new string(paddingChar, totalWidth);
if (input.Length >= totalWidth)
return input;
int paddingLength = totalWidth - input.Length;
return input + new string(paddingChar, paddingLength);
}
public static string PadBoth(string input, int totalWidth, char paddingChar = ' ')
{
if (string.IsNullOrEmpty(input))
return new string(paddingChar, totalWidth);
if (input.Length >= totalWidth)
return input;
int paddingLength = totalWidth - input.Length;
int leftPadding = paddingLength / 2;
int rightPadding = paddingLength - leftPadding;
return new string(paddingChar, leftPadding) + input + new string(paddingChar, rightPadding);
}
Time and Space Complexity
- Time Complexity: O(n) where n is the total width.
- Space Complexity: O(n) for the new string.
Applications
- Text Alignment: Aligning text in columns or tables.
- Data Formatting: Formatting numbers or codes with leading zeros.
- Visual Presentation: Creating visually appealing text layouts.
String Trimming
String trimming removes specified characters (usually whitespace) from the beginning and/or end of a string.
Implementation
public static string Trim(string input, char[] charsToTrim = null)
{
if (string.IsNullOrEmpty(input))
return input;
return charsToTrim == null ? input.Trim() : input.Trim(charsToTrim);
}
public static string TrimStart(string input, char[] charsToTrim = null)
{
if (string.IsNullOrEmpty(input))
return input;
return charsToTrim == null ? input.TrimStart() : input.TrimStart(charsToTrim);
}
public static string TrimEnd(string input, char[] charsToTrim = null)
{
if (string.IsNullOrEmpty(input))
return input;
return charsToTrim == null ? input.TrimEnd() : input.TrimEnd(charsToTrim);
}
// Custom implementation of trim
public static string TrimCustom(string input)
{
if (string.IsNullOrEmpty(input))
return input;
int start = 0;
int end = input.Length - 1;
// Find the first non-whitespace character from the beginning
while (start <= end && char.IsWhiteSpace(input[start]))
start++;
// Find the first non-whitespace character from the end
while (end >= start && char.IsWhiteSpace(input[end]))
end--;
// If the string is all whitespace
if (start > end)
return string.Empty;
// Extract the trimmed substring
return input.Substring(start, end - start + 1);
}
Time and Space Complexity
- Time Complexity: O(n) where n is the length of the string.
- Space Complexity: O(n) for the new string.
Applications
- Data Cleaning: Removing unwanted whitespace from user input.
- Text Processing: Standardizing text format.
- Parsing: Preparing strings for further processing.
String Splitting and Joining
String splitting divides a string into substrings based on specified delimiters, while joining combines multiple strings into one with a specified separator.
Implementation
public static string[] Split(string input, char[] separators = null, StringSplitOptions options = StringSplitOptions.None)
{
if (string.IsNullOrEmpty(input))
return new string[0];
return separators == null ?
input.Split(new char[] { ' ', '\t', '\n', '\r' }, options) :
input.Split(separators, options);
}
public static string[] SplitByString(string input, string[] separators, StringSplitOptions options = StringSplitOptions.None)
{
if (string.IsNullOrEmpty(input))
return new string[0];
return input.Split(separators, options);
}
public static string Join(string separator, IEnumerable<string> values)
{
return string.Join(separator, values);
}
public static string Join<T>(string separator, IEnumerable<T> values)
{
return string.Join(separator, values);
}
// Custom implementation of split
public static List<string> SplitCustom(string input, char separator)
{
List<string> result = new List<string>();
if (string.IsNullOrEmpty(input))
return result;
int startIndex = 0;
for (int i = 0; i < input.Length; i++)
{
if (input[i] == separator)
{
result.Add(input.Substring(startIndex, i - startIndex));
startIndex = i + 1;
}
}
// Add the last substring
result.Add(input.Substring(startIndex));
return result;
}
Time and Space Complexity
- Time Complexity: O(n) where n is the length of the string.
- Space Complexity: O(n) for the new strings.
Applications
- Data Parsing: Breaking down structured text into components.
- CSV Processing: Parsing comma-separated values.
- Text Analysis: Breaking text into words or sentences.
- Data Formatting: Combining data elements into formatted strings.
String Replacement
String replacement substitutes occurrences of a substring or pattern with another string.
Implementation
public static string Replace(string input, string oldValue, string newValue)
{
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(oldValue))
return input;
return input.Replace(oldValue, newValue ?? string.Empty);
}
public static string ReplaceFirst(string input, string oldValue, string newValue)
{
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(oldValue))
return input;
int pos = input.IndexOf(oldValue);
if (pos < 0)
return input;
return input.Substring(0, pos) + (newValue ?? string.Empty) + input.Substring(pos + oldValue.Length);
}
public static string ReplaceLast(string input, string oldValue, string newValue)
{
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(oldValue))
return input;
int pos = input.LastIndexOf(oldValue);
if (pos < 0)
return input;
return input.Substring(0, pos) + (newValue ?? string.Empty) + input.Substring(pos + oldValue.Length);
}
public static string ReplaceRegex(string input, string pattern, string replacement)
{
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(pattern))
return input;
try
{
return Regex.Replace(input, pattern, replacement ?? string.Empty);
}
catch (ArgumentException ex)
{
Console.WriteLine($"Invalid regular expression: {ex.Message}");
return input;
}
}
Time and Space Complexity
- Time Complexity: O(n) for simple replacements, can be higher for complex regex patterns.
- Space Complexity: O(n) for the new string.
Applications
- Text Editing: Find and replace operations in documents.
- Data Cleaning: Standardizing or correcting text data.
- Template Processing: Filling in placeholders in templates.
- Code Generation: Generating code by replacing placeholders.
String Interpolation
String interpolation embeds expressions within string literals, creating a more readable way to construct strings.
Implementation
public static string Interpolate(string template, Dictionary<string, object> values)
{
if (string.IsNullOrEmpty(template) || values == null)
return template;
string result = template;
foreach (var entry in values)
{
string placeholder = $"{{{entry.Key}}}";
result = result.Replace(placeholder, entry.Value?.ToString() ?? string.Empty);
}
return result;
}
// Using C# string interpolation
public static string InterpolateNative(string firstName, string lastName, int age)
{
return $"Name: {firstName} {lastName}, Age: {age}";
}
// Using string.Format
public static string FormatString(string format, params object[] args)
{
return string.Format(format, args);
}
Time and Space Complexity
- Time Complexity: O(n × m) where n is the length of the template and m is the number of placeholders.
- Space Complexity: O(n) for the new string.
Applications
- User Interface: Displaying dynamic content.
- Logging: Creating log messages with variable data.
- Report Generation: Creating reports with dynamic data.
- Message Formatting: Creating formatted messages for various purposes.
String Rotation
String rotation shifts the characters of a string by a certain number of positions.
Implementation
public static string RotateLeft(string input, int positions)
{
if (string.IsNullOrEmpty(input) || positions % input.Length == 0)
return input;
positions = positions % input.Length;
if (positions < 0)
positions += input.Length;
return input.Substring(positions) + input.Substring(0, positions);
}
public static string RotateRight(string input, int positions)
{
if (string.IsNullOrEmpty(input) || positions % input.Length == 0)
return input;
positions = positions % input.Length;
if (positions < 0)
positions += input.Length;
int leftPartStart = input.Length - positions;
return input.Substring(leftPartStart) + input.Substring(0, leftPartStart);
}
// Check if one string is a rotation of another
public static bool IsRotation(string s1, string s2)
{
if (string.IsNullOrEmpty(s1) && string.IsNullOrEmpty(s2))
return true;
if (string.IsNullOrEmpty(s1) || string.IsNullOrEmpty(s2) || s1.Length != s2.Length)
return false;
string concatenated = s1 + s1;
return concatenated.Contains(s2);
}
Time and Space Complexity
- Time Complexity: O(n) where n is the length of the string.
- Space Complexity: O(n) for the new string.
Applications
- Cryptography: Simple encryption techniques.
- Data Transformation: Transforming data for specific algorithms.
- Pattern Recognition: Identifying rotated patterns.
String Compression
String compression reduces the size of a string by replacing repeated characters with a count and the character.
Implementation
public static string CompressRunLength(string input)
{
if (string.IsNullOrEmpty(input) || input.Length <= 2)
return input;
StringBuilder compressed = new StringBuilder();
char currentChar = input[0];
int count = 1;
for (int i = 1; i < input.Length; i++)
{
if (input[i] == currentChar)
{
count++;
}
else
{
compressed.Append(count).Append(currentChar);
currentChar = input[i];
count = 1;
}
}
compressed.Append(count).Append(currentChar);
return compressed.Length < input.Length ? compressed.ToString() : input;
}
public static string DecompressRunLength(string compressed)
{
if (string.IsNullOrEmpty(compressed) || compressed.Length < 2)
return compressed;
StringBuilder decompressed = new StringBuilder();
for (int i = 0; i < compressed.Length; i += 2)
{
if (i + 1 >= compressed.Length)
break;
int count = int.Parse(compressed[i].ToString());
char c = compressed[i + 1];
for (int j = 0; j < count; j++)
{
decompressed.Append(c);
}
}
return decompressed.ToString();
}
Time and Space Complexity
- Time Complexity: O(n) where n is the length of the string.
- Space Complexity: O(n) for the compressed or decompressed string.
Applications
- Data Compression: Reducing storage requirements for text data.
- Network Transmission: Reducing bandwidth usage.
- Image Processing: Run-length encoding for simple images.
String Tokenization
String tokenization breaks a string into tokens based on delimiters or patterns.
Implementation
public static List<string> Tokenize(string input, char[] delimiters)
{
if (string.IsNullOrEmpty(input))
return new List<string>();
return input.Split(delimiters, StringSplitOptions.RemoveEmptyEntries).ToList();
}
public static List<string> TokenizeByPattern(string input, string pattern)
{
if (string.IsNullOrEmpty(input))
return new List<string>();
try
{
return Regex.Split(input, pattern)
.Where(s => !string.IsNullOrEmpty(s))
.ToList();
}
catch (ArgumentException ex)
{
Console.WriteLine($"Invalid regular expression: {ex.Message}");
return new List<string> { input };
}
}
// Custom tokenizer for a simple expression language
public static List<string> TokenizeExpression(string expression)
{
if (string.IsNullOrEmpty(expression))
return new List<string>();
List<string> tokens = new List<string>();
StringBuilder currentToken = new StringBuilder();
for (int i = 0; i < expression.Length; i++)
{
char c = expression[i];
if (char.IsWhiteSpace(c))
{
if (currentToken.Length > 0)
{
tokens.Add(currentToken.ToString());
currentToken.Clear();
}
}
else if ("+-*/()".Contains(c))
{
if (currentToken.Length > 0)
{
tokens.Add(currentToken.ToString());
currentToken.Clear();
}
tokens.Add(c.ToString());
}
else
{
currentToken.Append(c);
}
}
if (currentToken.Length > 0)
{
tokens.Add(currentToken.ToString());
}
return tokens;
}
Time and Space Complexity
- Time Complexity: O(n) where n is the length of the string.
- Space Complexity: O(n) for the tokens.
Applications
- Compilers and Interpreters: Lexical analysis phase.
- Natural Language Processing: Breaking text into words or sentences.
- Command Parsing: Parsing command-line arguments.
- Data Extraction: Extracting structured data from text.
String Validation
String validation checks if a string meets certain criteria or follows a specific pattern.
Implementation
public static bool IsNumeric(string input)
{
if (string.IsNullOrEmpty(input))
return false;
return input.All(char.IsDigit);
}
public static bool IsAlphabetic(string input)
{
if (string.IsNullOrEmpty(input))
return false;
return input.All(char.IsLetter);
}
public static bool IsAlphanumeric(string input)
{
if (string.IsNullOrEmpty(input))
return false;
return input.All(c => char.IsLetterOrDigit(c));
}
public static bool IsEmail(string input)
{
if (string.IsNullOrEmpty(input))
return false;
try
{
// Simple regex for email validation
return Regex.IsMatch(input, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
}
catch
{
return false;
}
}
public static bool IsUrl(string input)
{
if (string.IsNullOrEmpty(input))
return false;
try
{
return Uri.TryCreate(input, UriKind.Absolute, out Uri uriResult) &&
(uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
}
catch
{
return false;
}
}
public static bool MatchesPattern(string input, string pattern)
{
if (string.IsNullOrEmpty(input))
return false;
try
{
return Regex.IsMatch(input, pattern);
}
catch
{
return false;
}
}
Time and Space Complexity
- Time Complexity: O(n) for simple validations, can be higher for complex regex patterns.
- Space Complexity: O(1) for most validations.
Applications
- Form Validation: Validating user input in forms.
- Data Validation: Ensuring data meets specific format requirements.
- Security: Validating input to prevent injection attacks.
- Data Quality: Ensuring data quality in databases.
Best Practices and Optimization
Efficient String Manipulation
- Use StringBuilder for Multiple Concatenations: When building strings through multiple concatenations, use
StringBuilder
instead of the+
operator.
// Inefficient
string result = "";
for (int i = 0; i < 1000; i++)
{
result += i.ToString();
}
// Efficient
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
sb.Append(i);
}
string result = sb.ToString();
-
Avoid Unnecessary String Creations: Reuse strings when possible and avoid creating new strings unnecessarily.
-
Use String Interning: For frequently used strings, consider using string interning to save memory.
string s1 = "Hello";
string s2 = string.Intern("Hello");
Console.WriteLine(object.ReferenceEquals(s1, s2)); // True
- Use Appropriate String Comparison: Specify the appropriate
StringComparison
enum value for string comparisons.
// Case-insensitive comparison
bool equals = string.Equals("hello", "HELLO", StringComparison.OrdinalIgnoreCase);
- Optimize Regular Expressions: For frequently used regex patterns, compile them and reuse the compiled object.
// Compiled regex for better performance
Regex emailRegex = new Regex(@"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.Compiled);
Memory Considerations
- String Pooling: .NET automatically pools string literals to save memory.
- Large String Handling: For very large strings, consider using streams or memory-mapped files.
- Substring Efficiency: In modern .NET,
Substring
creates a new string rather than sharing the underlying array. - Dispose Pattern: When working with large strings in temporary operations, consider using
using
blocks to ensure proper disposal.
Performance Tips
- Avoid Redundant Operations: Don't perform the same string operation multiple times.
- Use Built-in Methods: Prefer built-in string methods over custom implementations for better performance.
- Batch Processing: For large-scale string processing, consider processing in batches.
- Parallel Processing: For independent string operations on large datasets, consider parallel processing.
- Caching: Cache the results of expensive string operations that are performed frequently.
Comprehensive Example: Text Processing Utility
The following example demonstrates a practical application of string manipulation algorithms in a text processing utility. This utility provides various text transformation and analysis functions that might be used in a text editor or document processing application.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace TextProcessingUtility
{
/// <summary>
/// A comprehensive utility class for text processing and manipulation.
/// Demonstrates various string manipulation algorithms and techniques in C#.
/// </summary>
public class TextProcessor
{
/// <summary>
/// Main method demonstrating the text processing utility.
/// </summary>
public static void Main()
{
Console.WriteLine("Text Processing Utility");
Console.WriteLine("======================\n");
// Sample text for demonstration
string sampleText = @"The quick brown fox jumps over the lazy dog.
This pangram contains all the letters of the English alphabet.
THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.
123 456 7890 !@#$%^&*()";
Console.WriteLine("Original Text:");
Console.WriteLine("-------------");
Console.WriteLine(sampleText);
Console.WriteLine();
// Demonstrate various text transformations
DemonstrateTextTransformations(sampleText);
// Demonstrate text analysis
DemonstrateTextAnalysis(sampleText);
// Demonstrate practical applications
DemonstratePracticalApplications();
}
/// <summary>
/// Demonstrates various text transformation operations.
/// </summary>
/// <param name="text">The text to transform.</param>
private static void DemonstrateTextTransformations(string text)
{
Console.WriteLine("Text Transformations:");
Console.WriteLine("--------------------");
// Case transformations
Console.WriteLine($"Uppercase: {text.ToUpper()}");
Console.WriteLine($"Lowercase: {text.ToLower()}");
Console.WriteLine($"Title Case: {ToTitleCase(text)}");
Console.WriteLine();
// Reversal
Console.WriteLine($"Reversed: {ReverseString(text)}");
Console.WriteLine($"Reversed (words only): {ReverseWords(text)}");
Console.WriteLine();
// Rotation
Console.WriteLine($"Left Rotation (5 chars): {RotateLeft(text, 5)}");
Console.WriteLine($"Right Rotation (5 chars): {RotateRight(text, 5)}");
Console.WriteLine();
// Trimming and padding
string line = " Hello, World! ";
Console.WriteLine($"Original: '{line}'");
Console.WriteLine($"Trimmed: '{line.Trim()}'");
Console.WriteLine($"Left Trimmed: '{line.TrimStart()}'");
Console.WriteLine($"Right Trimmed: '{line.TrimEnd()}'");
Console.WriteLine($"Padded Left: '{line.PadLeft(25)}'");
Console.WriteLine($"Padded Right: '{line.PadRight(25, '*')}'");
Console.WriteLine();
// Replacement
Console.WriteLine($"Replace 'fox' with 'cat': {text.Replace("fox", "cat")}");
Console.WriteLine($"Replace first 'the': {ReplaceFirst(text, "the", "a")}");
Console.WriteLine($"Replace vowels with '*': {ReplaceVowels(text, '*')}");
Console.WriteLine();
// Splitting and joining
Console.WriteLine("Split by sentences:");
string[] sentences = text.Split(new[] { '.', '!', '?' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string sentence in sentences)
{
Console.WriteLine($" - {sentence.Trim()}");
}
Console.WriteLine("\nSplit by words:");
string[] words = text.Split(new[] { ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine($" Word count: {words.Length}");
Console.WriteLine($" First 5 words: {string.Join(", ", words.Take(5))}");
Console.WriteLine("\nJoin words with '-':");
Console.WriteLine($" {string.Join("-", words.Take(10))}");
Console.WriteLine();
}
/// <summary>
/// Demonstrates text analysis operations.
/// </summary>
/// <param name="text">The text to analyze.</param>
private static void DemonstrateTextAnalysis(string text)
{
Console.WriteLine("Text Analysis:");
Console.WriteLine("--------------");
// Character statistics
Dictionary<char, int> charFrequency = CountCharacters(text);
Console.WriteLine("Character frequency (top 5):");
foreach (var pair in charFrequency.OrderByDescending(p => p.Value).Take(5))
{
Console.WriteLine($" '{pair.Key}': {pair.Value}");
}
// Word statistics
Dictionary<string, int> wordFrequency = CountWords(text);
Console.WriteLine("\nWord frequency (top 5):");
foreach (var pair in wordFrequency.OrderByDescending(p => p.Value).Take(5))
{
Console.WriteLine($" '{pair.Key}': {pair.Value}");
}
// Text metrics
Console.WriteLine("\nText metrics:");
Console.WriteLine($" Character count: {text.Length}");
Console.WriteLine($" Word count: {CountWords(text).Sum(p => p.Value)}");
Console.WriteLine($" Sentence count: {CountSentences(text)}");
Console.WriteLine($" Average word length: {CalculateAverageWordLength(text):F2} characters");
Console.WriteLine($" Average sentence length: {CalculateAverageSentenceLength(text):F2} words");
// Readability
Console.WriteLine($" Flesch Reading Ease: {CalculateFleschReadingEase(text):F2}");
Console.WriteLine($" Estimated reading time: {EstimateReadingTime(text):F2} minutes");
Console.WriteLine();
}
/// <summary>
/// Demonstrates practical applications of string manipulation.
/// </summary>
private static void DemonstratePracticalApplications()
{
Console.WriteLine("Practical Applications:");
Console.WriteLine("----------------------");
// CSV parsing
string csvData = "Name,Age,City\nJohn,30,New York\nJane,25,Boston\nBob,45,Chicago";
Console.WriteLine("CSV Parsing:");
Console.WriteLine(csvData);
Console.WriteLine("\nParsed data:");
ParseCsv(csvData);
// Template processing
string template = "Dear {{Name}}, Thank you for your purchase of {{Product}} for {{Price}}. Your order will be shipped to {{Address}} within {{Days}} business days.";
Dictionary<string, string> values = new Dictionary<string, string>
{
{ "Name", "John Smith" },
{ "Product", "C# Programming Guide" },
{ "Price", "$49.99" },
{ "Address", "123 Main St, Anytown, USA" },
{ "Days", "3-5" }
};
Console.WriteLine("\nTemplate Processing:");
Console.WriteLine($"Template: {template}");
Console.WriteLine($"Processed: {ProcessTemplate(template, values)}");
// Text formatting
Console.WriteLine("\nText Formatting:");
FormatText("This is a sample text that will be formatted to fit within a specified width. The text will be wrapped to ensure that no line exceeds the maximum width.", 40);
// Code formatting (simple indentation)
string code = "if (condition) { DoSomething(); foreach (var item in collection) { Process(item); } } else { DoSomethingElse(); }";
Console.WriteLine("\nCode Formatting:");
Console.WriteLine("Original: " + code);
Console.WriteLine("Formatted:");
Console.WriteLine(FormatCode(code));
}
#region String Transformation Methods
/// <summary>
/// Converts a string to title case (first letter of each word capitalized).
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>The title-cased string.</returns>
public static string ToTitleCase(string input)
{
if (string.IsNullOrEmpty(input))
return input;
// Using TextInfo for proper title casing
TextInfo textInfo = new CultureInfo("en-US", false).TextInfo;
return textInfo.ToTitleCase(input.ToLower());
}
/// <summary>
/// Reverses a string.
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>The reversed string.</returns>
public static string ReverseString(string input)
{
if (string.IsNullOrEmpty(input))
return input;
char[] charArray = input.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
/// <summary>
/// Reverses the order of words in a string while maintaining the order of characters within each word.
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>The string with reversed word order.</returns>
public static string ReverseWords(string input)
{
if (string.IsNullOrEmpty(input))
return input;
// Split the string into words
string[] words = input.Split(new[] { ' ', '\t', '\n', '\r' }, StringSplitOptions.None);
// Reverse the array of words
Array.Reverse(words);
// Join the words back together
return string.Join(" ", words);
}
/// <summary>
/// Rotates a string to the left by a specified number of positions.
/// </summary>
/// <param name="input">The input string.</param>
/// <param name="positions">The number of positions to rotate.</param>
/// <returns>The rotated string.</returns>
public static string RotateLeft(string input, int positions)
{
if (string.IsNullOrEmpty(input) || positions % input.Length == 0)
return input;
// Ensure positions is within the range of the string length
positions = positions % input.Length;
// Perform the rotation
return input.Substring(positions) + input.Substring(0, positions);
}
/// <summary>
/// Rotates a string to the right by a specified number of positions.
/// </summary>
/// <param name="input">The input string.</param>
/// <param name="positions">The number of positions to rotate.</param>
/// <returns>The rotated string.</returns>
public static string RotateRight(string input, int positions)
{
if (string.IsNullOrEmpty(input) || positions % input.Length == 0)
return input;
// Ensure positions is within the range of the string length
positions = positions % input.Length;
// Perform the rotation
return input.Substring(input.Length - positions) + input.Substring(0, input.Length - positions);
}
/// <summary>
/// Replaces the first occurrence of a substring with another substring.
/// </summary>
/// <param name="input">The input string.</param>
/// <param name="oldValue">The substring to replace.</param>
/// <param name="newValue">The replacement substring.</param>
/// <returns>The modified string.</returns>
public static string ReplaceFirst(string input, string oldValue, string newValue)
{
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(oldValue))
return input;
int pos = input.IndexOf(oldValue, StringComparison.OrdinalIgnoreCase);
if (pos < 0)
return input;
return input.Substring(0, pos) + newValue + input.Substring(pos + oldValue.Length);
}
/// <summary>
/// Replaces all vowels in a string with a specified character.
/// </summary>
/// <param name="input">The input string.</param>
/// <param name="replacementChar">The character to replace vowels with.</param>
/// <returns>The modified string.</returns>
public static string ReplaceVowels(string input, char replacementChar)
{
if (string.IsNullOrEmpty(input))
return input;
// Define vowels (both lowercase and uppercase)
char[] vowels = { 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U' };
// Create a StringBuilder for efficient character replacement
StringBuilder sb = new StringBuilder(input);
// Replace each vowel with the replacement character
for (int i = 0; i < sb.Length; i++)
{
if (vowels.Contains(sb[i]))
{
sb[i] = replacementChar;
}
}
return sb.ToString();
}
#endregion
#region Text Analysis Methods
/// <summary>
/// Counts the frequency of each character in a string.
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>A dictionary mapping characters to their frequencies.</returns>
public static Dictionary<char, int> CountCharacters(string input)
{
Dictionary<char, int> frequency = new Dictionary<char, int>();
if (string.IsNullOrEmpty(input))
return frequency;
foreach (char c in input)
{
if (frequency.ContainsKey(c))
frequency[c]++;
else
frequency[c] = 1;
}
return frequency;
}
/// <summary>
/// Counts the frequency of each word in a string.
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>A dictionary mapping words to their frequencies.</returns>
public static Dictionary<string, int> CountWords(string input)
{
Dictionary<string, int> frequency = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrEmpty(input))
return frequency;
// Split the input into words
string[] words = input.Split(new[] { ' ', '\t', '\n', '\r', '.', ',', ';', ':', '!', '?' },
StringSplitOptions.RemoveEmptyEntries);
foreach (string word in words)
{
string cleanWord = word.Trim().ToLower();
if (frequency.ContainsKey(cleanWord))
frequency[cleanWord]++;
else
frequency[cleanWord] = 1;
}
return frequency;
}
/// <summary>
/// Counts the number of sentences in a string.
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>The number of sentences.</returns>
public static int CountSentences(string input)
{
if (string.IsNullOrEmpty(input))
return 0;
// Count sentence-ending punctuation
int count = 0;
for (int i = 0; i < input.Length; i++)
{
if (input[i] == '.' || input[i] == '!' || input[i] == '?')
{
// Skip multiple consecutive punctuation marks
while (i + 1 < input.Length && (input[i + 1] == '.' || input[i + 1] == '!' || input[i + 1] == '?'))
i++;
count++;
}
}
return Math.Max(1, count); // Ensure at least one sentence if there's text but no ending punctuation
}
/// <summary>
/// Calculates the average word length in a string.
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>The average word length.</returns>
public static double CalculateAverageWordLength(string input)
{
if (string.IsNullOrEmpty(input))
return 0;
string[] words = input.Split(new[] { ' ', '\t', '\n', '\r', '.', ',', ';', ':', '!', '?' },
StringSplitOptions.RemoveEmptyEntries);
if (words.Length == 0)
return 0;
int totalLength = words.Sum(word => word.Length);
return (double)totalLength / words.Length;
}
/// <summary>
/// Calculates the average sentence length in words.
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>The average sentence length in words.</returns>
public static double CalculateAverageSentenceLength(string input)
{
if (string.IsNullOrEmpty(input))
return 0;
string[] sentences = input.Split(new[] { '.', '!', '?' }, StringSplitOptions.RemoveEmptyEntries);
if (sentences.Length == 0)
return 0;
int totalWords = 0;
foreach (string sentence in sentences)
{
string[] words = sentence.Split(new[] { ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
totalWords += words.Length;
}
return (double)totalWords / sentences.Length;
}
/// <summary>
/// Calculates the Flesch Reading Ease score for a text.
/// Higher scores indicate easier readability (90-100: Very easy, 0-30: Very difficult).
/// </summary>
/// <param name="input">The input text.</param>
/// <returns>The Flesch Reading Ease score.</returns>
public static double CalculateFleschReadingEase(string input)
{
if (string.IsNullOrEmpty(input))
return 0;
int wordCount = CountWords(input).Sum(p => p.Value);
int sentenceCount = CountSentences(input);
if (wordCount == 0 || sentenceCount == 0)
return 0;
// Count syllables (simplified approach)
int syllableCount = CountSyllables(input);
// Calculate Flesch Reading Ease score
double averageSentenceLength = (double)wordCount / sentenceCount;
double averageSyllablesPerWord = (double)syllableCount / wordCount;
return 206.835 - (1.015 * averageSentenceLength) - (84.6 * averageSyllablesPerWord);
}
/// <summary>
/// Counts the approximate number of syllables in a text.
/// This is a simplified approach and may not be accurate for all words.
/// </summary>
/// <param name="input">The input text.</param>
/// <returns>The approximate number of syllables.</returns>
private static int CountSyllables(string input)
{
if (string.IsNullOrEmpty(input))
return 0;
// Split into words
string[] words = input.Split(new[] { ' ', '\t', '\n', '\r', '.', ',', ';', ':', '!', '?' },
StringSplitOptions.RemoveEmptyEntries);
int syllableCount = 0;
foreach (string word in words)
{
string cleanWord = word.Trim().ToLower();
// Count vowel groups
int count = 0;
bool prevIsVowel = false;
for (int i = 0; i < cleanWord.Length; i++)
{
bool isVowel = "aeiouy".Contains(cleanWord[i]);
if (isVowel && !prevIsVowel)
count++;
prevIsVowel = isVowel;
}
// Adjust for silent 'e' at the end
if (cleanWord.Length > 2 && cleanWord.EndsWith("e") && !cleanWord.EndsWith("le"))
count--;
// Ensure at least one syllable per word
syllableCount += Math.Max(1, count);
}
return syllableCount;
}
/// <summary>
/// Estimates the reading time for a text in minutes.
/// Assumes an average reading speed of 200 words per minute.
/// </summary>
/// <param name="input">The input text.</param>
/// <returns>The estimated reading time in minutes.</returns>
public static double EstimateReadingTime(string input)
{
if (string.IsNullOrEmpty(input))
return 0;
int wordCount = CountWords(input).Sum(p => p.Value);
// Assume average reading speed of 200 words per minute
const int wordsPerMinute = 200;
return (double)wordCount / wordsPerMinute;
}
#endregion
#region Practical Application Methods
/// <summary>
/// Parses CSV data and displays it in a tabular format.
/// </summary>
/// <param name="csvData">The CSV data to parse.</param>
public static void ParseCsv(string csvData)
{
if (string.IsNullOrEmpty(csvData))
return;
// Split into lines
string[] lines = csvData.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length == 0)
return;
// Parse header
string[] headers = lines[0].Split(',');
// Calculate column widths
int[] columnWidths = new int[headers.Length];
for (int i = 0; i < headers.Length; i++)
{
columnWidths[i] = headers[i].Length;
}
// Parse data rows and update column widths
string[][] data = new string[lines.Length - 1][];
for (int i = 1; i < lines.Length; i++)
{
data[i - 1] = lines[i].Split(',');
for (int j = 0; j < data[i - 1].Length && j < columnWidths.Length; j++)
{
columnWidths[j] = Math.Max(columnWidths[j], data[i - 1][j].Length);
}
}
// Print header
for (int i = 0; i < headers.Length; i++)
{
Console.Write("| " + headers[i].PadRight(columnWidths[i]) + " ");
}
Console.WriteLine("|");
// Print separator
for (int i = 0; i < headers.Length; i++)
{
Console.Write("+-" + new string('-', columnWidths[i]) + "-");
}
Console.WriteLine("+");
// Print data rows
foreach (string[] row in data)
{
for (int i = 0; i < row.Length && i < columnWidths.Length; i++)
{
Console.Write("| " + row[i].PadRight(columnWidths[i]) + " ");
}
Console.WriteLine("|");
}
}
/// <summary>
/// Processes a template by replacing placeholders with values.
/// </summary>
/// <param name="template">The template string with placeholders in the format {{Name}}.</param>
/// <param name="values">A dictionary mapping placeholder names to their values.</param>
/// <returns>The processed template with placeholders replaced by their values.</returns>
public static string ProcessTemplate(string template, Dictionary<string, string> values)
{
if (string.IsNullOrEmpty(template) || values == null)
return template;
string result = template;
foreach (var entry in values)
{
string placeholder = "{{" + entry.Key + "}}";
result = result.Replace(placeholder, entry.Value);
}
return result;
}
/// <summary>
/// Formats text to fit within a specified width by wrapping lines.
/// </summary>
/// <param name="text">The text to format.</param>
/// <param name="maxWidth">The maximum width of each line.</param>
public static void FormatText(string text, int maxWidth)
{
if (string.IsNullOrEmpty(text) || maxWidth <= 0)
return;
// Split text into words
string[] words = text.Split(' ');
StringBuilder currentLine = new StringBuilder();
foreach (string word in words)
{
// Check if adding this word would exceed the max width
if (currentLine.Length + word.Length + 1 > maxWidth)
{
// Print the current line and start a new one
Console.WriteLine(currentLine.ToString());
currentLine.Clear();
currentLine.Append(word);
}
else
{
// Add the word to the current line
if (currentLine.Length > 0)
currentLine.Append(" ");
currentLine.Append(word);
}
}
// Print the last line
if (currentLine.Length > 0)
Console.WriteLine(currentLine.ToString());
}
/// <summary>
/// Formats code by adding indentation based on braces.
/// This is a simplified formatter for demonstration purposes.
/// </summary>
/// <param name="code">The code to format.</param>
/// <returns>The formatted code.</returns>
public static string FormatCode(string code)
{
if (string.IsNullOrEmpty(code))
return code;
StringBuilder formatted = new StringBuilder();
int indentLevel = 0;
bool inString = false;
for (int i = 0; i < code.Length; i++)
{
char c = code[i];
// Handle string literals
if (c == '"' && (i == 0 || code[i - 1] != '\\'))
inString = !inString;
if (!inString)
{
// Handle indentation based on braces
if (c == '{')
{
formatted.Append(c);
formatted.AppendLine();
indentLevel++;
formatted.Append(new string(' ', indentLevel * 4));
continue;
}
else if (c == '}')
{
formatted.AppendLine();
indentLevel = Math.Max(0, indentLevel - 1);
formatted.Append(new string(' ', indentLevel * 4));
formatted.Append(c);
continue;
}
else if (c == ';')
{
formatted.Append(c);
formatted.AppendLine();
formatted.Append(new string(' ', indentLevel * 4));
continue;
}
}
formatted.Append(c);
}
return formatted.ToString();
}
#endregion
}
}
Step-by-Step Explanation
This comprehensive example demonstrates a text processing utility that implements various string manipulation algorithms and techniques in C#. Let's break it down:
-
Program Structure:
- The program is organized into a class called
TextProcessor
with aMain
method as the entry point. - It includes methods for string transformations, text analysis, and practical applications.
- The program is organized into a class called
-
String Transformation Methods:
- Case Conversion: Converting text to uppercase, lowercase, and title case.
- String Reversal: Reversing the order of characters in a string.
- Word Reversal: Reversing the order of words while maintaining character order within words.
- String Rotation: Shifting characters to the left or right by a specified number of positions.
- String Replacement: Replacing substrings, including first occurrence only and pattern-based replacements.
-
Text Analysis Methods:
- Character Frequency: Counting occurrences of each character.
- Word Frequency: Counting occurrences of each word.
- Sentence Counting: Determining the number of sentences in a text.
- Readability Metrics: Calculating average word length, sentence length, and Flesch Reading Ease score.
- Reading Time Estimation: Estimating how long it would take to read a text.
-
Practical Applications:
- CSV Parsing: Parsing and displaying CSV data in a tabular format.
- Template Processing: Replacing placeholders in a template with actual values.
- Text Formatting: Wrapping text to fit within a specified width.
- Code Formatting: Adding indentation to code based on braces.
Key Concepts Demonstrated
-
String Immutability: The example shows how string operations create new strings rather than modifying existing ones.
-
Efficient String Manipulation: Using
StringBuilder
for operations that would otherwise create many intermediate strings. -
String Parsing and Tokenization: Breaking strings into components based on delimiters.
-
Text Analysis Algorithms: Implementing algorithms to analyze text characteristics.
-
Practical String Processing: Demonstrating real-world applications of string manipulation.
Performance Considerations
-
StringBuilder: Used for operations that involve multiple string modifications to avoid creating intermediate strings.
-
Efficient Algorithms: Implementing algorithms with appropriate time and space complexity.
-
Dictionary Lookups: Using dictionaries for efficient character and word frequency counting.
-
Culture-Aware Operations: Using
TextInfo
for proper title casing that respects cultural rules.
Common Pitfalls and Best Practices
-
Input Validation: Always check for null or empty strings before processing.
-
Case Sensitivity: Be explicit about case sensitivity in string operations.
-
Culture Considerations: Be aware of culture-specific behavior in string operations.
-
Boundary Conditions: Handle edge cases like empty strings, single characters, or very long strings.
-
Performance Optimization: Choose appropriate algorithms and data structures based on the specific requirements.
Conclusion
String manipulation algorithms are essential tools for processing and transforming text data. By understanding the characteristics, strengths, and limitations of each algorithm, you can choose the most appropriate one for your specific requirements.
Whether you're reversing strings, splitting and joining text, or performing complex replacements, these algorithms provide efficient solutions for manipulating strings in various ways. The best practices and optimizations discussed can help you write more efficient and effective string manipulation code in C#.
The example program demonstrates how to implement these algorithms in C# and how to use them in practical applications. By studying and experimenting with these implementations, you can gain a deeper understanding of string manipulation algorithms and their applications in software development.