{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "main_language": "python",
   "notebook_metadata_filter": "-all"
  },
  "kernelspec": {
   "display_name": ".NET (C#)",
   "language": "C#",
   "name": ".net-csharp"
  },
  "language_info": {
   "name": "csharp"
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "# Regular Expressions\n\nRegular expressions (regex) are patterns that describe text. They extend the string search and replace operations from the previous section, letting you match flexible, structured patterns instead of exact strings.\n\n> Regex lives in the `System.Text.RegularExpressions` namespace.",
   "id": "c3921bb0"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "using System.Text.RegularExpressions;",
   "id": "bfaec7ea"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Checking for a Match\n\n`Regex.IsMatch` returns `true` if the pattern appears anywhere in the string:",
   "id": "ac51b659"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "// Does the string contain any digit?\nbool hasDigit = Regex.IsMatch(\"Score: 98\", @\"\\d\");\nConsole.WriteLine(hasDigit);  // True\n\n// Does it start with a capital letter?\nbool startsUpper = Regex.IsMatch(\"Alice\", @\"^[A-Z]\");\nConsole.WriteLine(startsUpper);  // True",
   "id": "eebea810"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Common Pattern Elements\n\n| Pattern | Meaning | Example match |\n|---------|---------|---------------|\n| `\\d` | any digit | `7`, `0` |\n| `\\w` | word character (letter, digit, `_`) | `a`, `Z`, `3` |\n| `\\s` | whitespace | space, tab |\n| `.` | any character except newline | `x`, `!` |\n| `^` | start of string | `^Hello` matches `Hello world` |\n| `$` | end of string | `\\.txt$` matches `file.txt` |\n| `[abc]` | one of a, b, c | `[aeiou]` matches any vowel |\n| `[A-Z]` | range | any uppercase letter |\n| `+` | one or more | `\\d+` matches `42` |\n| `*` | zero or more | `\\d*` matches `` or `99` |\n| `?` | zero or one | `colou?r` matches `color` or `colour` |",
   "id": "119d0865"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Extracting a Match\n\n`Regex.Match` returns the first match object:",
   "id": "2abcccf3"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string line = \"Student: Alice, Score: 98\";\nMatch m = Regex.Match(line, @\"\\d+\");\n\nif (m.Success)\n    Console.WriteLine($\"First number found: {m.Value}\");  // 98",
   "id": "38c4e0cd"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Finding All Matches\n\n`Regex.Matches` returns every match:",
   "id": "07e353d3"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string data = \"Scores: 85, 92, 78, 90\";\nMatchCollection matches = Regex.Matches(data, @\"\\d+\");\n\nforeach (Match m in matches)\n    Console.WriteLine(m.Value);\n// 85  92  78  90",
   "id": "e548c0b6"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Replacing with a Pattern\n\n`Regex.Replace` substitutes every match:",
   "id": "6671644e"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "// Normalize multiple spaces to one\nstring messy = \"Alice,  98,   CS\";\nstring clean = Regex.Replace(messy, @\"\\s{2,}\", \" \");\nConsole.WriteLine(clean);  // Alice,  98,   CS -> \"Alice, 98, CS\"",
   "id": "2937a394"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "// Remove all non-digit characters\nstring phone = \"(312) 555-0190\";\nstring digits = Regex.Replace(phone, @\"[^\\d]\", \"\");\nConsole.WriteLine(digits);  // 3125550190",
   "id": "80c26c68"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Using Groups to Extract Fields\n\nParentheses create **capture groups** — named parts of a match:",
   "id": "46bf9c48"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string record = \"Alice,98,CS\";\nMatch m = Regex.Match(record, @\"^(?<name>\\w+),(?<score>\\d+),(?<major>\\w+)$\");\n\nif (m.Success)\n{\n    Console.WriteLine($\"Name:  {m.Groups[\"name\"].Value}\");\n    Console.WriteLine($\"Score: {m.Groups[\"score\"].Value}\");\n    Console.WriteLine($\"Major: {m.Groups[\"major\"].Value}\");\n}",
   "id": "d06ac1ef"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Validation Patterns\n\nCommon tasks well-suited to regex:",
   "id": "310a1a97"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "// Simple email check (not exhaustive)\nbool isEmail = Regex.IsMatch(\"user@example.com\", @\"^[\\w.+-]+@[\\w-]+\\.[a-z]{2,}$\");\nConsole.WriteLine($\"valid email: {isEmail}\");\n\n// Zip code (5 digits)\nbool isZip = Regex.IsMatch(\"60660\", @\"^\\d{5}$\");\nConsole.WriteLine($\"valid zip: {isZip}\");\n\n// Date format YYYY-MM-DD\nbool isDate = Regex.IsMatch(\"2026-03-17\", @\"^\\d{4}-\\d{2}-\\d{2}$\");\nConsole.WriteLine($\"valid date: {isDate}\");",
   "id": "ce80efc3"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## When to Use Regex vs. String Methods\n\n| Use `string` methods | Use regex |\n|---------------------|-----------|\n| Exact match or replace | Pattern-based match |\n| Fixed delimiter `Split(',')` | Variable whitespace or delimiters |\n| `Contains`, `StartsWith` | Character classes, repetition |\n| Performance-critical loops | One-time validation or extraction |\n\nRegex adds power but also complexity — prefer `Split`, `Trim`, and `Contains` when the pattern is simple and fixed.",
   "id": "3b6d3c49"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Practice\n\n1. Write a pattern to match a C# `int` or `double` literal (e.g. `42`, `-3`, `3.14`).\n2. Extract all words (sequences of `\\w+`) from a sentence and print each on its own line.\n3. Validate that a filename ends with `.cs` or `.txt`.\n4. Replace all tab characters in a file line with four spaces.",
   "id": "c2ceba6e"
  },
  {
   "cell_type": "markdown",
   "id": "footnotes",
   "metadata": {},
   "source": "```{rubric} Footnotes\n```\n"
  }
 ]
}
