Exemples de Traitement de Chaînes Web TypeScript

Exemples de traitement de chaînes Web TypeScript incluant la manipulation de chaînes, le correspondance de modèles et la transformation de texte

💻 Diviser et Joindre des Chaînes typescript

🟢 simple ⭐⭐

Diviser les chaînes en tableaux et joindre les tableaux en chaînes avec divers séparateurs et délimiteurs

⏱️ 25 min 🏷️ typescript, web, string processing
Prerequisites: Basic TypeScript
// Web TypeScript String Split and Join Examples
// Splitting strings into arrays and joining arrays back into strings

// 1. Basic String Splitting
class StringSplitter {
  // Split by single character
  static splitByChar(text: string, delimiter: string): string[] {
    return text.split(delimiter);
  }

  // Split by whitespace
  static splitByWhitespace(text: string): string[] {
    return text.split(/\s+/);
  }

  // Split into words (handles multiple spaces)
  static splitIntoWords(text: string): string[] {
    return text.trim().split(/\s+/).filter(word => word.length > 0);
  }

  // Split by line
  static splitByLine(text: string): string[] {
    return text.split(/\r?\n/);
  }

  // Split by length
  static splitByLength(text: string, length: number): string[] {
    const result: string[] = [];

    for (let i = 0; i < text.length; i += length) {
      result.push(text.slice(i, i + length));
    }

    return result;
  }

  // Split by multiple delimiters
  static splitByMultiple(text: string, delimiters: string[]): string[] {
    let result = text;

    for (const delimiter of delimiters) {
      result = result.split(delimiter).join(' ');
    }

    return result.split(/\s+/).filter(part => part.length > 0);
  }

  // Split keeping delimiters
  static splitKeepingDelimiters(text: string, delimiter: string): string[] {
    const parts: string[] = [];
    let lastIndex = 0;
    let index;

    while ((index = text.indexOf(delimiter, lastIndex)) !== -1) {
      parts.push(text.slice(lastIndex, index));
      parts.push(delimiter);
      lastIndex = index + delimiter.length;
    }

    parts.push(text.slice(lastIndex));
    return parts;
  }

  // Split and trim
  static splitAndTrim(text: string, delimiter: string): string[] {
    return text.split(delimiter).map(part => part.trim()).filter(part => part.length > 0);
  }

  // Split into chunks of balanced parentheses
  static splitBalanced(text: string, open: string, close: string): string[] {
    const chunks: string[] = [];
    let current = '';
    let depth = 0;

    for (const char of text) {
      if (char === open) {
        depth++;
        current += char;
      } else if (char === close) {
        depth--;
        current += char;

        if (depth === 0 && current.length > 0) {
          chunks.push(current);
          current = '';
        }
      } else {
        if (depth > 0) {
          current += char;
        }
      }
    }

    if (current.length > 0) {
      chunks.push(current);
    }

    return chunks;
  }
}

// 2. Advanced String Joining
class StringJoiner {
  // Join with separator
  static join(parts: string[], separator: string): string {
    return parts.join(separator);
  }

  // Join with new line
  static joinWithNewLine(parts: string[]): string {
    return parts.join('\n');
  }

  // Join with comma and space
  static joinWithComma(parts: string[]): string {
    return parts.join(', ');
  }

  // Join with custom prefix/suffix
  static joinWithWrapper(parts: string[], separator: string, prefix: string, suffix: string): string {
    return prefix + parts.join(separator) + suffix;
  }

  // Join with conditional separator
  static joinConditional(parts: string[], separator: string): string {
    return parts.filter(part => part !== null && part !== undefined && part.length > 0)
                .join(separator);
  }

  // Join and limit
  static joinWithLimit(parts: string[], separator: string, max: number, suffix: string = '...'): string {
    if (parts.length <= max) {
      return parts.join(separator);
    }

    return parts.slice(0, max).join(separator) + suffix;
  }

  // Join with last separator different
  static joinWithOxfordComma(parts: string[]): string {
    if (parts.length === 0) return '';
    if (parts.length === 1) return parts[0];
    if (parts.length === 2) return parts.join(' and ');

    return parts.slice(0, -1).join(', ') + ', and ' + parts[parts.length - 1];
  }

  // Join with numbering
  static joinWithNumbering(parts: string[], separator: string): string {
    return parts.map((part, index) => `${index + 1}. ${part}`).join(separator);
  }
}

// 3. String Tokenizer
class StringTokenizer {
  private tokens: string[] = [];
  private index: number = 0;

  constructor(input: string, delimiters: string = ' \t\n\r') {
    this.tokens = this.tokenize(input, delimiters);
  }

  private tokenize(input: string, delimiters: string): string[] {
    const result: string[] = [];
    let current = '';

    for (const char of input) {
      if (delimiters.includes(char)) {
        if (current.length > 0) {
          result.push(current);
          current = '';
        }
      } else {
        current += char;
      }
    }

    if (current.length > 0) {
      result.push(current);
    }

    return result;
  }

  hasMoreTokens(): boolean {
    return this.index < this.tokens.length;
  }

  nextToken(): string | null {
    if (this.hasMoreTokens()) {
      return this.tokens[this.index++];
    }
    return null;
  }

  countTokens(): number {
    return this.tokens.length - this.index;
  }

  getAllTokens(): string[] {
    return [...this.tokens];
  }

  reset(): void {
    this.index = 0;
  }
}

// 4. String Builder
class StringBuilder {
  private parts: string[] = [];

  // Append string
  append(text: string): this {
    this.parts.push(text);
    return this;
  }

  // Append line
  appendLine(text: string): this {
    this.parts.push(text);
    this.parts.push('\n');
    return this;
  }

  // Append multiple strings
  appendAll(...texts: string[]): this {
    this.parts.push(...texts);
    return this;
  }

  // Clear builder
  clear(): this {
    this.parts = [];
    return this;
  }

  // Get length
  get length(): number {
    return this.parts.join('').length;
  }

  // Check if empty
  isEmpty(): boolean {
    return this.parts.length === 0 || this.parts.every(p => p.length === 0);
  }

  // Build final string
  toString(): string {
    return this.parts.join('');
  }

  // Build with separator
  join(separator: string): string {
    return this.parts.join(separator);
  }
}

// 5. Template Engine (Basic)
class TemplateEngine {
  // Simple variable substitution
  static render(template: string, data: Record<string, any>): string {
    return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
      return data[key] !== undefined ? String(data[key]) : match;
    });
  }

  // Template with conditionals
  static renderWithConditionals(
    template: string,
    data: Record<string, any>
  ): string {
    let result = template;

    // Handle {{#if var}}...{{/if}}
    result = result.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, varName, content) => {
      return data[varName] ? content : '';
    });

    // Handle {{#unless var}}...{{/unless}}
    result = result.replace(/\{\{#unless\s+(\w+)\}\}([\s\S]*?)\{\{\/unless\}\}/g, (match, varName, content) => {
      return !data[varName] ? content : '';
    });

    // Replace variables
    return this.render(result, data);
  }

  // Template with loops
  static renderWithLoops(
    template: string,
    data: Record<string, any>
  ): string {
    let result = template;

    // Handle {{#each items}}...{{/each}}
    result = result.replace(
      /\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g,
      (match, varName, itemTemplate) => {
        const items = data[varName] as any[];
        if (!Array.isArray(items)) return '';

        return items.map((item, index) => {
          let rendered = itemTemplate;

          // Replace {{this}} with current item
          rendered = rendered.replace(/\{\{this\}\}/g, String(item));

          // Replace {{@index}} with current index
          rendered = rendered.replace(/\{\{@index\}\}/g, String(index));

          return rendered;
        }).join('');
      }
    );

    return this.render(result, data);
  }
}

// 6. Text Formatter
class TextFormatter {
  // Capitalize first letter
  static capitalize(text: string): string {
    if (text.length === 0) return text;
    return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
  }

  // Capitalize each word
  static capitalizeWords(text: string): string {
    return text.split(/\s+/).map(word => this.capitalize(word)).join(' ');
  }

  // Convert to title case
  static toTitleCase(text: string): string {
    const minorWords = ['a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by'];

    return text.split(/\s+/).map((word, index) => {
      const lowerWord = word.toLowerCase();
      if (index === 0 || !minorWords.includes(lowerWord)) {
        return this.capitalize(lowerWord);
      }
      return lowerWord;
    }).join(' ');
  }

  // Convert to camel case
  static toCamelCase(text: string): string {
    return text.split(/[_\s-]+/)
      .map((word, index) => {
        if (index === 0) {
          return word.toLowerCase();
        }
        return this.capitalize(word.toLowerCase());
      })
      .join('');
  }

  // Convert to snake case
  static toSnakeCase(text: string): string {
    return text.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');
  }

  // Convert to kebab case
  static toKebabCase(text: string): string {
    return text.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
  }

  // Truncate text
  static truncate(text: string, maxLength: number, suffix: string = '...'): string {
    if (text.length <= maxLength) {
      return text;
    }

    return text.slice(0, maxLength - suffix.length) + suffix;
  }

  // Truncate words
  static truncateWords(text: string, maxWords: number, suffix: string = '...'): string {
    const words = text.split(/\s+/);

    if (words.length <= maxWords) {
      return text;
    }

    return words.slice(0, maxWords).join(' ') + suffix;
  }

  // Reverse string
  static reverse(text: string): string {
    return text.split('').reverse().join('');
  }

  // Shuffle words
  static shuffleWords(text: string): string {
    const words = text.split(/\s+/);
    const shuffled = [...words].sort(() => Math.random() - 0.5);
    return shuffled.join(' ');
  }

  // Justify text (add spaces to make each line the same width)
  static justify(text: string, lineWidth: number): string {
    const words = text.split(/\s+/);
    const lines: string[] = [];
    let currentLine: string[] = [];
    let currentLength = 0;

    for (const word of words) {
      if (currentLength + word.length + currentLine.length > lineWidth) {
        lines.push(currentLine.join(' '));
        currentLine = [];
        currentLength = 0;
      }

      currentLine.push(word);
      currentLength += word.length;
    }

    if (currentLine.length > 0) {
      lines.push(currentLine.join(' '));
    }

    return lines.join('\n');
  }
}

// 7. String Encoding/Decoding
class StringEncoder {
  // Base64 encode
  static toBase64(text: string): string {
    return btoa(unescape(encodeURIComponent(text)));
  }

  // Base64 decode
  static fromBase64(base64: string): string {
    return decodeURIComponent(escape(atob(base64)));
  }

  // URL encode
  static encodeURL(text: string): string {
    return encodeURIComponent(text);
  }

  // URL decode
  static decodeURL(text: string): string {
    return decodeURIComponent(text);
  }

  // HTML encode
  static encodeHTML(text: string): string {
    const htmlEntities: Record<string, string> = {
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;',
      '"': '&quot;',
      "'": '&#039;'
    };

    return text.replace(/[&<>"']/g, char => htmlEntities[char]);
  }

  // HTML decode
  static decodeHTML(text: string): string {
    const htmlEntities: Record<string, string> = {
      '&amp;': '&',
      '&lt;': '<',
      '&gt;': '>',
      '&quot;': '"',
      '&#039;': "'"
    };

    return text.replace(/&amp;|&lt;|&gt;|&quot;|&#039;/g, entity => htmlEntities[entity]);
  }
}

// 8. String Analyzer
class StringAnalyzer {
  // Count characters
  static countCharacters(text: string, includeSpaces: boolean = true): number {
    if (includeSpaces) {
      return text.length;
    }
    return text.replace(/\s/g, '').length;
  }

  // Count words
  static countWords(text: string): number {
    return text.trim().split(/\s+/).filter(word => word.length > 0).length;
  }

  // Count lines
  static countLines(text: string): number {
    return text.split(/\r?\n/).length;
  }

  // Count paragraphs
  static countParagraphs(text: string): number {
    return text.split(/\n\s*\n/).filter(para => para.trim().length > 0).length;
  }

  // Get character frequency
  static characterFrequency(text: string, caseSensitive: boolean = false): Map<string, number> {
    const processed = caseSensitive ? text : text.toLowerCase();
    const freq = new Map<string, number>();

    for (const char of processed) {
      if (char.match(/\w/)) {
        freq.set(char, (freq.get(char) || 0) + 1);
      }
    }

    return freq;
  }

  // Get word frequency
  static wordFrequency(text: string): Map<string, number> {
    const words = text.toLowerCase().split(/\s+/);
    const freq = new Map<string, number>();

    for (const word of words) {
      if (word.match(/\w+/)) {
        freq.set(word, (freq.get(word) || 0) + 1);
      }
    }

    return freq;
  }

  // Find longest word
  static findLongestWord(text: string): string {
    const words = text.split(/\s+/);
    return words.reduce((longest, word) => word.length > longest.length ? word : longest, '');
  }

  // Find shortest word
  static findShortestWord(text: string): string {
    const words = text.split(/\s+/).filter(w => w.length > 0);
    return words.reduce((shortest, word) => word.length < shortest.length ? word : shortest, words[0] || '');
  }

  // Average word length
  static averageWordLength(text: string): number {
    const words = text.split(/\s+/).filter(w => w.length > 0);

    if (words.length === 0) return 0;

    const totalLength = words.reduce((sum, word) => sum + word.length, 0);
    return totalLength / words.length;
  }
}

// Usage Examples
async function demonstrateStringSplitJoin() {
  console.log('=== Web TypeScript String Split and Join Examples ===\n');

  // 1. Basic splitting
  console.log('--- 1. Basic Splitting ---');
  const text = 'Hello World TypeScript Programming';

  console.log(`Split by space: ${StringSplitter.splitByChar(text, ' ')}`);
  console.log(`Split into words: ${StringSplitter.splitIntoWords(text)}`);
  console.log(`Split by length (5): ${StringSplitter.splitByLength(text, 5)}`);

  const multiline = 'Line 1\nLine 2\nLine 3';
  console.log(`Split by line: ${StringSplitter.splitByLine(multiline)}`);

  // 2. Advanced splitting
  console.log('\n--- 2. Advanced Splitting ---');
  const complex = 'one,two;three:four';
  console.log(`Split by multiple: ${StringSplitter.splitByMultiple(complex, [',', ';', ':'])}`);

  const csv = 'apple, banana, cherry, date';
  console.log(`Split and trim: ${StringSplitter.splitAndTrim(csv, ',')}`);

  // 3. Joining
  console.log('\n--- 3. Joining ---');
  const fruits = ['apple', 'banana', 'cherry'];

  console.log(`Join with comma: ${StringJoiner.joinWithComma(fruits)}`);
  console.log(`Join with comma and space: ${StringJoiner.join(fruits, ', ')}`);
  console.log(`Join with new line: ${StringJoiner.joinWithNewLine(fruits)}`);

  console.log(`Oxford comma: ${StringJoiner.joinWithOxfordComma(fruits)}`);

  // 4. String builder
  console.log('\n--- 4. String Builder ---');
  const builder = new StringBuilder();
  builder.append('Hello').append(' ').append('World').appendLine('!');
  builder.append('This is TypeScript');

  console.log(`Built string: ${builder.toString()}`);
  console.log(`Length: ${builder.length}`);

  // 5. Tokenizer
  console.log('\n--- 5. Tokenizer ---');
  const tokenizer = new StringTokenizer('This is a test string', ' ');

  console.log('Tokens:');
  while (tokenizer.hasMoreTokens()) {
    console.log(`  ${tokenizer.nextToken()}`);
  }

  // 6. Template engine
  console.log('\n--- 6. Template Engine ---');
  const template = 'Hello {{name}}, welcome to {{place}}!';
  const data = { name: 'Alice', place: 'Wonderland' };

  console.log(`Rendered: ${TemplateEngine.render(template, data)}`);

  // 7. Text formatting
  console.log('\n--- 7. Text Formatting ---');
  console.log(`Capitalize: ${TextFormatter.capitalize('hello world')}`);
  console.log(`Title case: ${TextFormatter.toTitleCase('the quick brown fox')}`);
  console.log(`Camel case: ${TextFormatter.toCamelCase('hello_world_text')}`);
  console.log(`Snake case: ${TextFormatter.toSnakeCase('helloWorldText')}`);
  console.log(`Kebab case: ${TextFormatter.toKebabCase('helloWorldText')}`);

  // 8. Encoding/Decoding
  console.log('\n--- 8. Encoding/Decoding ---');
  const original = 'Hello World!';
  const encoded = StringEncoder.toBase64(original);
  const decoded = StringEncoder.fromBase64(encoded);

  console.log(`Original: ${original}`);
  console.log(`Base64: ${encoded}`);
  console.log(`Decoded: ${decoded}`);

  // 9. String analyzer
  console.log('\n--- 9. String Analyzer ---');
  const longText = 'The quick brown fox jumps over the lazy dog. The dog was not amused!';

  console.log(`Characters: ${StringAnalyzer.countCharacters(longText)}`);
  console.log(`Characters (no spaces): ${StringAnalyzer.countCharacters(longText, false)}`);
  console.log(`Words: ${StringAnalyzer.countWords(longText)}`);
  console.log(`Lines: ${StringAnalyzer.countLines(longText)}`);
  console.log(`Longest word: ${StringAnalyzer.findLongestWord(longText)}`);
  console.log(`Shortest word: ${StringAnalyzer.findShortestWord(longText)}`);
  console.log(`Average word length: ${StringAnalyzer.averageWordLength(longText).toFixed(2)}`);

  console.log('\n=== All String Split and Join Examples Completed ===');
}

// Export classes
export { StringSplitter, StringJoiner, StringTokenizer, StringBuilder, TemplateEngine, TextFormatter, StringEncoder, StringAnalyzer };
export { demonstrateStringSplitJoin };

💻 Remplacement de Chaînes typescript

🟢 simple ⭐⭐⭐

Remplacer, substituer et transformer des chaînes en utilisant diverses méthodes et stratégies

⏱️ 25 min 🏷️ typescript, web, string processing, replace
Prerequisites: Basic TypeScript
// Web TypeScript String Replace Examples
// Comprehensive string replacement and transformation techniques

// 1. Basic String Replacement
class StringReplacer {
  // Replace first occurrence
  static replaceFirst(text: string, search: string, replacement: string): string {
    return text.replace(search, replacement);
  }

  // Replace all occurrences
  static replaceAll(text: string, search: string, replacement: string): string {
    return text.split(search).join(replacement);
  }

  // Replace all (case insensitive)
  static replaceAllIgnoreCase(text: string, search: string, replacement: string): string {
    const regex = new RegExp(search, 'gi');
    return text.replace(regex, replacement);
  }

  // Replace at index
  static replaceAt(text: string, index: number, length: number, replacement: string): string {
    return text.slice(0, index) + replacement + text.slice(index + length);
  }

  // Replace between indices
  static replaceBetween(text: string, start: number, end: number, replacement: string): string {
    return text.slice(0, start) + replacement + text.slice(end);
  }
}

// 2. Advanced String Replacement
class AdvancedReplacer {
  // Replace using callback function
  static replaceWithFunction(
    text: string,
    pattern: string | RegExp,
    callback: (match: string, ...groups: string[]) => string
  ): string {
    return text.replace(pattern, callback);
  }

  // Replace multiple values at once
  static replaceMultiple(
    text: string,
    replacements: Record<string, string>
  ): string {
    let result = text;

    for (const [search, replacement] of Object.entries(replacements)) {
      result = result.split(search).join(replacement);
    }

    return result;
  }

  // Replace with template
  static replaceWithTemplate(
    text: string,
    pattern: string | RegExp,
    template: string
  ): string {
    return text.replace(pattern, template);
  }

  // Replace preserving case
  static replacePreservingCase(text: string, search: string, replacement: string): string {
    return text.replace(new RegExp(search, 'gi'), (match) => {
      if (match === match.toUpperCase()) {
        return replacement.toUpperCase();
      } else if (match === match.toLowerCase()) {
        return replacement.toLowerCase();
      } else if (match[0] === match[0].toUpperCase()) {
        return replacement.charAt(0).toUpperCase() + replacement.slice(1).toLowerCase();
      }
      return replacement;
    });
  }

  // Replace nth occurrence
  static replaceNth(
    text: string,
    search: string,
    replacement: string,
    n: number
  ): string {
    const parts = text.split(search);

    if (n < 0 || n >= parts.length - 1) {
      return text;
    }

    parts[n] = replacement;
    return parts.join(search);
  }

  // Replace first and last
  static replaceFirstAndLast(
    text: string,
    search: string,
    replacement: string
  ): string {
    const parts = text.split(search);

    if (parts.length <= 1) {
      return text;
    }

    parts[0] = replacement;
    parts[parts.length - 1] = replacement;

    return parts.join(search);
  }
}

// 3. Pattern-Based Replacement
class PatternReplacer {
  // Replace using regex
  static replaceWithRegex(
    text: string,
    pattern: RegExp,
    replacement: string
  ): string {
    return text.replace(pattern, replacement);
  }

  // Replace words
  static replaceWords(
    text: string,
    words: string[],
    replacement: string
  ): string {
    const pattern = new RegExp(`\\b(${words.join('|')})\\b`, 'gi');
    return text.replace(pattern, replacement);
  }

  // Replace vowels
  static replaceVowels(text: string, replacement: string): string {
    return text.replace(/[aeiou]/gi, replacement);
  }

  // Replace consonants
  static replaceConsonants(text: string, replacement: string): string {
    return text.replace(/[^aeiou\s]/gi, replacement);
  }

  // Replace digits
  static replaceDigits(text: string, replacement: string): string {
    return text.replace(/\d/g, replacement);
  }

  // Replace whitespace
  static replaceWhitespace(text: string, replacement: string): string {
    return text.replace(/\s+/g, replacement);
  }

  // Replace line breaks
  static replaceLineBreaks(text: string, replacement: string): string {
    return text.replace(/\r?\n/g, replacement);
  }
}

// 4. Text Transformation
class TextTransformer {
  // To uppercase
  static toUpperCase(text: string): string {
    return text.toUpperCase();
  }

  // To lowercase
  static toLowerCase(text: string): string {
    return text.toLowerCase();
  }

  // To title case
  static toTitleCase(text: string): string {
    return text.replace(/\w\S*/g, (word) => {
      return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
    });
  }

  // To sentence case
  static toSentenceCase(text: string): string {
    return text.toLowerCase().replace(/(\.\s*)([a-z])/g, (_, prefix, letter) => {
      return prefix + letter.toUpperCase();
    });
  }

  // To alternating case
  static toAlternatingCase(text: string): string {
    return text.split('').map((char, index) => {
      return index % 2 === 0 ? char.toUpperCase() : char.toLowerCase();
    }).join('');
  }

  // To inverse case
  static toInverseCase(text: string): string {
    return text.split('').map(char => {
      return char === char.toUpperCase() ? char.toLowerCase() : char.toUpperCase();
    }).join('');
  }

  // Capitalize first letter
  static capitalize(text: string): string {
    return text.charAt(0).toUpperCase() + text.slice(1);
  }

  // Capitalize all words
  static capitalizeWords(text: string): string {
    return text.replace(/\b\w/g, char => char.toUpperCase());
  }
}

// 5. String Template Replacement
class TemplateReplacer {
  // Replace {{variable}} placeholders
  static replaceVariables(
    template: string,
    variables: Record<string, any>
  ): string {
    return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
      return variables[key] !== undefined ? String(variables[key]) : match;
    });
  }

  // Replace ${variable} placeholders
  static replaceES6Variables(
    template: string,
    variables: Record<string, any>
  ): string {
    return template.replace(/\$\{(\w+)\}/g, (match, key) => {
      return variables[key] !== undefined ? String(variables[key]) : match;
    });
  }

  // Replace %s placeholders (printf style)
  static replacePrintfStyle(template: string, ...values: any[]): string {
    let index = 0;

    return template.replace(/%s/g, () => {
      return index < values.length ? String(values[index++]) : '%s';
    });
  }

  // Replace ? placeholders (SQL style)
  static replaceQuestionMarks(template: string, ...values: any[]): string {
    let index = 0;

    return template.replace(/\?/g, () => {
      return index < values.length ? String(values[index++]) : '?';
    });
  }

  // Replace with numbered placeholders {0}, {1}, etc.
  static replaceNumbered(template: string, ...values: any[]): string {
    return template.replace(/\{(\d+)\}/g, (match, index) => {
      const i = parseInt(index);
      return i < values.length ? String(values[i]) : match;
    });
  }
}

// 6. Masking and Censoring
class StringMasker {
  // Mask email
  static maskEmail(email: string): string {
    const [username, domain] = email.split('@');

    if (username.length <= 2) {
      return `${username[0]}***@${domain}`;
    }

    return `${username[0]}${'*'.repeat(username.length - 2)}${username[username.length - 1]}@${domain}`;
  }

  // Mask phone number
  static maskPhone(phone: string): string {
    const cleaned = phone.replace(/\D/g, '');

    if (cleaned.length < 4) {
      return '*'.repeat(cleaned.length);
    }

    return cleaned.slice(0, -4).replace(/\d/g, '*') + cleaned.slice(-4);
  }

  // Mask credit card
  static maskCreditCard(card: string): string {
    const cleaned = card.replace(/\s/g, '');

    if (cleaned.length < 4) {
      return cleaned;
    }

    return '*'.repeat(cleaned.length - 4) + cleaned.slice(-4);
  }

  // Mask string partially
  static maskPartial(text: string, visibleStart: number = 2, visibleEnd: number = 2): string {
    if (text.length <= visibleStart + visibleEnd) {
      return text;
    }

    return text.slice(0, visibleStart) +
           '*'.repeat(text.length - visibleStart - visibleEnd) +
           text.slice(-visibleEnd);
  }

  // Censor words
  static censorWords(text: string, words: string[], censorChar: string = '*'): string {
    const pattern = new RegExp(`\\b(${words.join('|')})\\b`, 'gi');
    return text.replace(pattern, (match) => {
      return censorChar.repeat(match.length);
    });
  }

  // Censor profanity
  static censorProfanity(text: string, censorChar: string = '*'): string {
    const profanityList = ['damn', 'hell', 'shit', 'fuck']; // Add more as needed
    return this.censorWords(text, profanityList, censorChar);
  }
}

// 7. String Cleanup
class StringCleaner {
  // Trim whitespace
  static trim(text: string): string {
    return text.trim();
  }

  // Trim left
  static trimLeft(text: string): string {
    return text.trimLeft();
  }

  // Trim right
  static trimRight(text: string): string {
    return text.trimRight();
  }

  // Remove extra spaces
  static collapseWhitespace(text: string): string {
    return text.replace(/\s+/g, ' ').trim();
  }

  // Remove all whitespace
  static removeAllWhitespace(text: string): string {
    return text.replace(/\s/g, '');
  }

  // Remove line breaks
  static removeLineBreaks(text: string): string {
    return text.replace(/\r?\n/g, ' ');
  }

  // Remove punctuation
  static removePunctuation(text: string): string {
    return text.replace(/[.,\/#!$%^&*;:{}=\-_`~()]/g, '');
  }

  // Remove special characters
  static removeSpecialChars(text: string): string {
    return text.replace(/[^a-zA-Z0-9\s]/g, '');
  }

  // Remove duplicate characters
  static removeDuplicateChars(text: string): string {
    return text.split('').filter((char, index, self) => {
      return self.indexOf(char) === index;
    }).join('');
  }

  // Remove duplicate words
  static removeDuplicateWords(text: string): string {
    return text.replace(/\b(\w+)\b(\s+\1\b)+/gi, '$1');
  }
}

// 8. String Normalization
class StringNormalizer {
  // Normalize whitespace
  static normalizeWhitespace(text: string): string {
    return text.replace(/\s+/g, ' ').trim();
  }

  // Normalize line endings
  static normalizeLineEndings(text: string, lineEnding: '\n' | '\r\n' = '\n'): string {
    return text.replace(/\r?\n/g, lineEnding);
  }

  // Normalize case to lowercase
  static normalizeToLower(text: string): string {
    return text.toLowerCase();
  }

  // Normalize case to uppercase
  static normalizeToUpper(text: string): string {
    return text.toUpperCase();
  }

  // Normalize quotes
  static normalizeQuotes(text: string): string {
    return text
      .replace(/[\u2018\u2019]/g, "'")
      .replace(/[\u201C\u201D]/g, '"');
  }

  // Normalize dashes
  static normalizeDashes(text: string): string {
    return text.replace(/[\u2010-\u2015\u2212]/g, '-');
  }

  // Normalize accented characters
  static normalizeAccents(text: string): string {
    return text.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
  }
}

// Usage Examples
async function demonstrateStringReplace() {
  console.log('=== Web TypeScript String Replace Examples ===\n');

  // 1. Basic replacement
  console.log('--- 1. Basic Replacement ---');
  const text = 'Hello World! World is beautiful.';

  console.log(`Original: ${text}`);
  console.log(`Replace first 'World': ${StringReplacer.replaceFirst(text, 'World', 'Earth')}`);
  console.log(`Replace all 'World': ${StringReplacer.replaceAll(text, 'World', 'Earth')}`);
  console.log(`Replace all case insensitive: ${StringReplacer.replaceAllIgnoreCase(text, 'world', 'Earth')}`);

  // 2. Advanced replacement
  console.log('\n--- 2. Advanced Replacement ---');
  const multiText = 'The quick brown fox jumps over the lazy dog.';

  console.log(`Original: ${multiText}`);

  const replaced = AdvancedReplacer.replaceMultiple(multiText, {
    'quick': 'slow',
    'brown': 'white',
    'lazy': 'energetic'
  });
  console.log(`Replace multiple: ${replaced}`);

  console.log(`Replace 2nd 'the': ${AdvancedReplacer.replaceNth(multiText.toLowerCase(), 'the', 'a', 2)}`);
  console.log(`Replace preserving case: ${AdvancedReplacer.replacePreservingCase(multiText, 'the', 'a')}`);

  // 3. Pattern-based replacement
  console.log('\n--- 3. Pattern-Based Replacement ---');
  const patternText = 'Hello 123 World 456 Test 789';

  console.log(`Original: ${patternText}`);
  console.log(`Replace digits: ${PatternReplacer.replaceDigits(patternText, '#')}`);
  console.log(`Replace words: ${PatternReplacer.replaceWords(patternText, ['Hello', 'World'], 'Hi')}`);
  console.log(`Replace whitespace: ${PatternReplacer.replaceWhitespace(patternText, '-')}`);

  // 4. Text transformation
  console.log('\n--- 4. Text Transformation ---');
  const transformText = 'hello world this is typescript';

  console.log(`Original: ${transformText}`);
  console.log(`Uppercase: ${TextTransformer.toUpperCase(transformText)}`);
  console.log(`Lowercase: ${TextTransformer.toLowerCase(transformText)}`);
  console.log(`Title case: ${TextTransformer.toTitleCase(transformText)}`);
  console.log(`Sentence case: ${TextTransformer.toSentenceCase(transformText)}`);
  console.log(`Alternating case: ${TextTransformer.toAlternatingCase('hello')}`);
  console.log(`Inverse case: ${TextTransformer.toInverseCase('HeLLo WoRLd')}`);

  // 5. Template replacement
  console.log('\n--- 5. Template Replacement ---');
  const template = 'Hello {{name}}, welcome to {{place}}! You have {{count}} messages.';
  const variables = { name: 'Alice', place: 'Wonderland', count: 5 };

  console.log(`Template: ${template}`);
  console.log(`Replaced: ${TemplateReplacer.replaceVariables(template, variables)}`);

  const printfTemplate = 'Hello %s, you are %s years old.';
  console.log(`Printf style: ${TemplateReplacer.replacePrintfStyle(printfTemplate, 'Bob', 30)}`);

  const numberedTemplate = 'Dear {0}, your order {1} is {2}.';
  console.log(`Numbered: ${TemplateReplacer.replaceNumbered(numberedTemplate, 'John', '12345', 'ready')}`);

  // 6. Masking
  console.log('\n--- 6. Masking ---');
  console.log(`Mask email: ${StringMasker.maskEmail('[email protected]')}`);
  console.log(`Mask phone: ${StringMasker.maskPhone('(555) 123-4567')}`);
  console.log(`Mask credit card: ${StringMasker.maskCreditCard('4532 1234 5678 9010')}`);
  console.log(`Mask partial: ${StringMasker.maskPartial('SecretPassword', 2, 2)}`);

  const censorText = 'This damn hell is really shitty';
  console.log(`Censor: ${StringMasker.censorProfanity(censorText)}`);

  // 7. Cleanup
  console.log('\n--- 7. Cleanup ---');
  const messy = '  This    is   a  messy   text  \n with   extra   spaces  ';

  console.log(`Original: '${messy}'`);
  console.log(`Trim: '${StringCleaner.trim(messy)}'`);
  console.log(`Collapse whitespace: '${StringCleaner.collapseWhitespace(messy)}'`);
  console.log(`Remove all whitespace: '${StringCleaner.removeAllWhitespace(messy)}'`);
  console.log(`Remove line breaks: '${StringCleaner.removeLineBreaks(messy)}'`);

  // 8. Normalization
  console.log('\n--- 8. Normalization ---');
  const unnormalized = 'Hello   World!\n\rThis  is  a  test.\n';

  console.log(`Original: '${unnormalized}'`);
  console.log(`Normalized whitespace: '${StringNormalizer.normalizeWhitespace(unnormalized)}'`);
  console.log(`Normalized line endings: '${StringNormalizer.normalizeLineEndings(unnormalized)}'`);

  const quotes = 'Hello "world" and 'goodbye'';
  console.log(`Normalize quotes: ${StringNormalizer.normalizeQuotes(quotes)}`);

  const accented = 'café résumé';
  console.log(`Normalize accents: ${StringNormalizer.normalizeAccents(accented)}`);

  console.log('\n=== All String Replace Examples Completed ===');
}

// Export classes
export { StringReplacer, AdvancedReplacer, PatternReplacer, TextTransformer, TemplateReplacer, StringMasker, StringCleaner, StringNormalizer };
export { demonstrateStringReplace };

💻 Expressions Régulières typescript

🟡 intermediate ⭐⭐⭐

Utiliser des expressions régulières pour la correspondance de modèles, la validation, la recherche et le remplacement de texte

⏱️ 30 min 🏷️ typescript, web, string processing, regex
Prerequisites: Intermediate TypeScript, Regular Expressions
// Web TypeScript Regular Expressions Examples
// Pattern matching, validation, searching, and text replacement with regex

// 1. Pattern Matching
class PatternMatcher {
  // Check if pattern matches
  static test(text: string, pattern: string): boolean {
    const regex = new RegExp(pattern);
    return regex.test(text);
  }

  // Find all matches
  static findAll(text: string, pattern: string, flags: string = 'g'): RegExpMatchArray | null {
    const regex = new RegExp(pattern, flags);
    return text.match(regex);
  }

  // Find first match
  static findFirst(text: string, pattern: string): RegExpExecArray | null {
    const regex = new RegExp(pattern);
    return regex.exec(text);
  }

  // Extract groups from match
  static extractGroups(text: string, pattern: string): Record<string, string> | null {
    const regex = new RegExp(pattern);
    const match = regex.exec(text);

    if (!match) return null;

    const groups: Record<string, string> = {};

    if (match.groups) {
      for (const [key, value] of Object.entries(match.groups)) {
        groups[key] = value;
      }
    }

    return groups;
  }

  // Match all with capturing groups
  static matchWithGroups(text: string, pattern: string): Array<Record<string, string>> {
    const regex = new RegExp(pattern, 'g');
    const results: Array<Record<string, string>> = [];
    let match;

    while ((match = regex.exec(text)) !== null) {
      const groups: Record<string, string> = {};

      if (match.groups) {
        for (const [key, value] of Object.entries(match.groups)) {
          groups[key] = value;
        }
      }

      results.push(groups);
    }

    return results;
  }

  // Check if string matches pattern entirely
  static matchesEntirely(text: string, pattern: string): boolean {
    const regex = new RegExp(`^${pattern}$`);
    return regex.test(text);
  }
}

// 2. Common Validators
class Validators {
  // Email validation
  static isValidEmail(email: string): boolean {
    const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
    return pattern.test(email);
  }

  // URL validation
  static isValidURL(url: string): boolean {
    const pattern = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?$/;
    return pattern.test(url);
  }

  // Phone number validation (US format)
  static isValidPhone(phone: string): boolean {
    const pattern = /^\+?(\d{1,3})?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
    return pattern.test(phone);
  }

  // IP address validation (IPv4)
  static isValidIPv4(ip: string): boolean {
    const pattern = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
    return pattern.test(ip);
  }

  // Date validation (YYYY-MM-DD)
  static isValidDate(date: string): boolean {
    const pattern = /^(\d{4})-(\d{2})-(\d{2})$/;
    if (!pattern.test(date)) return false;

    const [, year, month, day] = date.match(pattern)!;
    const dateObj = new Date(`${year}-${month}-${day}`);

    return dateObj.getFullYear() === parseInt(year) &&
           dateObj.getMonth() === parseInt(month) - 1 &&
           dateObj.getDate() === parseInt(day);
  }

  // Credit card validation (Luhn algorithm)
  static isValidCreditCard(cardNumber: string): boolean {
    const cleaned = cardNumber.replace(/\s+/g, '');
    if (!/^\d{13,19}$/.test(cleaned)) return false;

    let sum = 0;
    let isEven = false;

    for (let i = cleaned.length - 1; i >= 0; i--) {
      let digit = parseInt(cleaned[i]);

      if (isEven) {
        digit *= 2;
        if (digit > 9) {
          digit -= 9;
        }
      }

      sum += digit;
      isEven = !isEven;
    }

    return sum % 10 === 0;
  }

  // Username validation
  static isValidUsername(username: string): boolean {
    const pattern = /^[a-zA-Z0-9_]{3,16}$/;
    return pattern.test(username);
  }

  // Password strength validation
  static getPasswordStrength(password: string): 'weak' | 'medium' | 'strong' {
    if (password.length < 8) return 'weak';

    let score = 0;

    if (/[a-z]/.test(password)) score++;
    if (/[A-Z]/.test(password)) score++;
    if (/[0-9]/.test(password)) score++;
    if (/[^a-zA-Z0-9]/.test(password)) score++;

    if (score <= 2) return 'weak';
    if (score === 3) return 'medium';
    return 'strong';
  }

  // Hex color validation
  static isValidHexColor(color: string): boolean {
    const pattern = /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/;
    return pattern.test(color);
  }
}

// 3. Text Search with Regex
class RegexSearcher {
  // Find all occurrences of pattern
  static findAll(text: string, pattern: string): { match: string; index: number }[] {
    const regex = new RegExp(pattern, 'gi');
    const results: { match: string; index: number }[] = [];
    let match;

    while ((match = regex.exec(text)) !== null) {
      results.push({
        match: match[0],
        index: match.index
      });
    }

    return results;
  }

  // Find words matching pattern
  static findWords(text: string, pattern: string): string[] {
    const regex = new RegExp(`\\b${pattern}\\b`, 'gi');
    const matches = text.match(regex);
    return matches ? Array.from(new Set(matches.map(m => m.toLowerCase()))) : [];
  }

  // Find sentences containing pattern
  static findSentences(text: string, pattern: string): string[] {
    const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
    const regex = new RegExp(pattern, 'gi');

    return sentences.filter(sentence => regex.test(sentence));
  }

  // Context search (find pattern with surrounding text)
  static findWithContext(text: string, pattern: string, contextChars: number = 20): Array<{
    match: string;
    context: string;
    index: number;
  }> {
    const regex = new RegExp(pattern, 'gi');
    const results: Array<{ match: string; context: string; index: number }> = [];
    let match;

    while ((match = regex.exec(text)) !== null) {
      const start = Math.max(0, match.index - contextChars);
      const end = Math.min(text.length, match.index + match[0].length + contextChars);

      results.push({
        match: match[0],
        context: text.slice(start, end),
        index: match.index
      });
    }

    return results;
  }
}

// 4. Text Replacement with Regex
class RegexReplacer {
  // Replace all occurrences
  static replaceAll(text: string, pattern: string, replacement: string): string {
    const regex = new RegExp(pattern, 'g');
    return text.replace(regex, replacement);
  }

  // Replace with function
  static replaceWithFunction(
    text: string,
    pattern: string,
    replacer: (match: string, ...groups: string[]) => string
  ): string {
    const regex = new RegExp(pattern, 'g');
    return text.replace(regex, replacer);
  }

  // Replace but preserve case
  static replacePreservingCase(text: string, pattern: string, replacement: string): string {
    return text.replace(new RegExp(pattern, 'gi'), (match) => {
      if (match === match.toUpperCase()) {
        return replacement.toUpperCase();
      } else if (match === match.toLowerCase()) {
        return replacement.toLowerCase();
      } else if (match[0] === match[0].toUpperCase()) {
        return replacement.charAt(0).toUpperCase() + replacement.slice(1).toLowerCase();
      }
      return replacement;
    });
  }

  // Remove all matches
  static removeAll(text: string, pattern: string): string {
    const regex = new RegExp(pattern, 'g');
    return text.replace(regex, '');
  }

  // Remove duplicate words
  static removeDuplicateWords(text: string): string {
    return text.replace(/\b(\w+)\b(\s+\1\b)+/gi, '$1');
  }

  // Remove extra whitespace
  static normalizeWhitespace(text: string): string {
    return text.replace(/\s+/g, ' ').trim();
  }
}

// 5. Text Extraction
class TextExtractor {
  // Extract email addresses
  static extractEmails(text: string): string[] {
    const pattern = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
    const matches = text.match(pattern);
    return matches ? Array.from(new Set(matches)) : [];
  }

  // Extract URLs
  static extractURLs(text: string): string[] {
    const pattern = /https?:\/\/[^\s<>"{}|\\^\[\]]+/g;
    const matches = text.match(pattern);
    return matches ? Array.from(new Set(matches)) : [];
  }

  // Extract hashtags
  static extractHashtags(text: string): string[] {
    const pattern = /#\w+/g;
    const matches = text.match(pattern);
    return matches || [];
  }

  // Extract mentions
  static extractMentions(text: string): string[] {
    const pattern = /@\w+/g;
    const matches = text.match(pattern);
    return matches || [];
  }

  // Extract numbers
  static extractNumbers(text: string): number[] {
    const pattern = /-?\d+(\.\d+)?/g;
    const matches = text.match(pattern);
    return matches ? matches.map(m => parseFloat(m)) : [];
  }

  // Extract dates (YYYY-MM-DD format)
  static extractDates(text: string): string[] {
    const pattern = /\d{4}-\d{2}-\d{2}/g;
    const matches = text.match(pattern);
    return matches || [];
  }

  // Extract phone numbers
  static extractPhones(text: string): string[] {
    const pattern = /\+?\d{1,3}?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/g;
    const matches = text.match(pattern);
    return matches || [];
  }
}

// 6. Text Sanitization
class TextSanitizer {
  // Remove HTML tags
  static stripHTMLTags(text: string): string {
    return text.replace(/<[^>]*>/g, '');
  }

  // Escape HTML
  static escapeHTML(text: string): string {
    const htmlEntities: Record<string, string> = {
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;',
      '"': '&quot;',
      "'": '&#039;'
    };

    return text.replace(/[&<>"']/g, char => htmlEntities[char]);
  }

  // Remove special characters
  static removeSpecialChars(text: string, keepSpaces: boolean = true): string {
    const pattern = keepSpaces ? /[^a-zA-Z0-9\s]/g : /[^a-zA-Z0-9]/g;
    return text.replace(pattern, '');
  }

  // Remove accents
  static removeAccents(text: string): string {
    return text.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
  }

  // Sanitize filename
  static sanitizeFilename(filename: string): string {
    return filename.replace(/[^a-zA-Z0-9._-]/g, '_');
  }
}

// 7. Regex Builder
class RegexBuilder {
  private pattern: string = '';
  private flags: string = '';

  // Match literal
  literal(text: string): this {
    this.pattern += text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    return this;
  }

  // Match any character
  anyChar(): this {
    this.pattern += '.';
    return this;
  }

  // Match digit
  digit(): this {
    this.pattern += '\\d';
    return this;
  }

  // Match word character
  wordChar(): this {
    this.pattern += '\\w';
    return this;
  }

  // Match whitespace
  whitespace(): this {
    this.pattern += '\\s';
    return this;
  }

  // Start of string
  startOfLine(): this {
    this.pattern += '^';
    return this;
  }

  // End of string
  endOfLine(): this {
    this.pattern += '$';
    return this;
  }

  // Match one or more
  oneOrMore(): this {
    this.pattern += '+';
    return this;
  }

  // Match zero or more
  zeroOrMore(): this {
    this.pattern += '*';
    return this;
  }

  // Match zero or one
  zeroOrOne(): this {
    this.pattern += '?';
    return this;
  }

  // Match range
  range(min: number, max?: number): this {
    if (max === undefined) {
      this.pattern += `{${min}}`;
    } else {
      this.pattern += `{${min},${max}}`;
    }
    return this;
  }

  // Any of
  anyOf(...chars: string[]): this {
    this.pattern += `[${chars.join('')}]`;
    return this;
  }

  // Capture group
  capture(): this {
    this.pattern += '(';
    return this;
  }

  // End capture group
  endCapture(): this {
    this.pattern += ')';
    return this;
  }

  // Named capture group
  namedCapture(name: string): this {
    this.pattern += `(?<${name}>`;
    return this;
  }

  // Or
  or(): this {
    this.pattern += '|';
    return this;
  }

  // Set flags
  setFlags(flags: string): this {
    this.flags = flags;
    return this;
  }

  // Build regex
  build(): RegExp {
    return new RegExp(this.pattern, this.flags);
  }

  // Get pattern string
  getPattern(): string {
    return this.pattern;
  }
}

// Usage Examples
async function demonstrateRegularExpressions() {
  console.log('=== Web TypeScript Regular Expressions Examples ===\n');

  // 1. Pattern matching
  console.log('--- 1. Pattern Matching ---');
  const text = 'Contact us at [email protected] or [email protected]';

  console.log(`Text: ${text}`);
  console.log(`Has email pattern: ${PatternMatcher.test(text, '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}')}`);

  const matches = PatternMatcher.findAll(text, '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}');
  console.log(`Emails found: ${matches ? matches.join(', ') : 'none'}`);

  // 2. Validators
  console.log('\n--- 2. Validators ---');
  console.log(`Valid email: ${Validators.isValidEmail('[email protected]')}`);
  console.log(`Valid URL: ${Validators.isValidURL('https://www.example.com')}`);
  console.log(`Valid phone: ${Validators.isValidPhone('(555) 123-4567')}`);
  console.log(`Valid IPv4: ${Validators.isValidIPv4('192.168.1.1')}`);
  console.log(`Valid date: ${Validators.isValidDate('2024-01-15')}`);
  console.log(`Valid credit card: ${Validators.isValidCreditCard('4532 1234 5678 9010')}`);
  console.log(`Valid username: ${Validators.isValidUsername('user_123')}`);
  console.log(`Password strength: ${Validators.getPasswordStrength('Abc123!@')}`);

  // 3. Regex search
  console.log('\n--- 3. Regex Search ---');
  const searchText = 'The quick brown fox jumps over the lazy dog. The fox is very fast.';

  const occurrences = RegexSearcher.findAll(searchText, 'fox');
  console.log(`'fox' occurrences: ${occurrences.length}`);
  occurrences.forEach(occ => console.log(`  Found at index ${occ.index}: ${occ.match}`));

  const words = RegexSearcher.findWords(searchText, 'f.*');
  console.log(`Words starting with 'f': ${words.join(', ')}`);

  const withContext = RegexSearcher.findWithContext(searchText, 'fox', 15);
  console.log('With context:');
  withContext.forEach(ctx => console.log(`  ... ${ctx.context} ...`));

  // 4. Text replacement
  console.log('\n--- 4. Text Replacement ---');
  const originalText = 'Hello world, World is beautiful. WORLD is amazing.';

  console.log(`Original: ${originalText}`);
  console.log(`Replace all 'world': ${RegexReplacer.replaceAll(originalText, 'world', 'earth')}`);
  console.log(`Replace preserving case: ${RegexReplacer.replacePreservingCase(originalText, 'world', 'earth')}`);

  const messy = 'This    is    a    messy   text.';
  console.log(`Normalize whitespace: ${RegexReplacer.normalizeWhitespace(messy)}`);

  // 5. Text extraction
  console.log('\n--- 5. Text Extraction ---');
  const extractText = 'Contact us at [email protected] or visit https://example.com. Call +1-555-123-4567. #tech @user';

  console.log(`Text: ${extractText}`);
  console.log(`Emails: ${TextExtractor.extractEmails(extractText).join(', ')}`);
  console.log(`URLs: ${TextExtractor.extractURLs(extractText).join(', ')}`);
  console.log(`Hashtags: ${TextExtractor.extractHashtags(extractText).join(', ')}`);
  console.log(`Mentions: ${TextExtractor.extractMentions(extractText).join(', ')}`);
  console.log(`Phones: ${TextExtractor.extractPhones(extractText).join(', ')}`);

  // 6. Text sanitization
  console.log('\n--- 6. Text Sanitization ---');
  const html = '<p>Hello <b>World</b>!</p>';
  console.log(`Original: ${html}`);
  console.log(`Strip HTML: ${TextSanitizer.stripHTMLTags(html)}`);
  console.log(`Escape HTML: ${TextSanitizer.escapeHTML(html)}`);

  const special = 'Hello, World! @#$ %^&*()';
  console.log(`Remove special: ${TextSanitizer.removeSpecialChars(special)}`);

  const accented = 'café résumé naïve';
  console.log(`Remove accents: ${TextSanitizer.removeAccents(accented)}`);

  const filename = 'my/file:name*.txt';
  console.log(`Sanitize filename: ${TextSanitizer.sanitizeFilename(filename)}`);

  // 7. Regex builder
  console.log('\n--- 7. Regex Builder ---');
  const emailRegex = new RegexBuilder()
    .startOfLine()
    .capture().wordChar().oneOrMore().endCapture()
    .literal('@')
    .capture().wordChar().oneOrMore().endCapture()
    .literal('.')
    .capture().wordChar().range(2, 4).endCapture()
    .endOfLine()
    .build();

  console.log(`Built pattern: ${emailRegex.source}`);
  console.log(`Test email: ${emailRegex.test('[email protected]')}`);

  const numberRegex = new RegexBuilder()
    .startOfLine()
    .capture().digit().oneOrMore().endCapture()
    .literal('.')
    .capture().digit().range(2).endCapture()
    .endOfLine()
    .build();

  console.log(`Price pattern: ${numberRegex.source}`);
  console.log(`Test price: ${numberRegex.test('19.99')}`);

  console.log('\n=== All Regular Expressions Examples Completed ===');
}

// Export classes
export { PatternMatcher, Validators, RegexSearcher, RegexReplacer, TextExtractor, TextSanitizer, RegexBuilder };
export { demonstrateRegularExpressions };