{
 "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": "# Text Operations\n\nThis section covers the core string operations needed before and during text parsing:\ncleaning, splitting, searching, replacing, and reading structured records.",
   "id": "3c857646"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Text Basics\n\nBefore parsing text, clean it first.",
   "id": "2ee4387c"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string raw = \"   Alice,  93  \";\n\nstring trimmed = raw.Trim();                      // \"Alice,  93\"\nstring upper   = trimmed.ToUpper();               // \"ALICE,  93\"\nstring lower   = trimmed.ToLower();               // \"alice,  93\"\nbool   empty   = string.IsNullOrWhiteSpace(raw);  // false\n\nConsole.WriteLine($\"trimmed: '{trimmed}'\");\nConsole.WriteLine($\"upper:   '{upper}'\");\nConsole.WriteLine($\"lower:   '{lower}'\");\nConsole.WriteLine($\"empty:   {empty}\");",
   "id": "9d5b28f2"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "Use these rules:\n\n- Call `Trim()` before splitting fields\n- Call `IsNullOrWhiteSpace()` before parsing\n- Normalize case when comparisons should be case-insensitive",
   "id": "69f8e2bc"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Accessing Characters",
   "id": "0c30d7af"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string s = \"cat\";\n\nfor (int i = 0; i < s.Length; i++)\n{\n    Console.WriteLine($\"{i}: {s[i]}\");\n}\n\n// Never index a string unless bounds are known to be valid.",
   "id": "f76e30be"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## String Immutability\n\nStrings are immutable in C#. All string methods return **new** strings; the original is unchanged.",
   "id": "c2273cfc"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string a = \"hello\";\nstring b = a.ToUpper();  // b = \"HELLO\", a is still \"hello\"\n\nConsole.WriteLine($\"a = {a}\");\nConsole.WriteLine($\"b = {b}\");",
   "id": "49955072"
  },
  {
   "cell_type": "markdown",
   "id": "8fe10cc3",
   "metadata": {},
   "source": "---"
  },
  {
   "cell_type": "markdown",
   "id": "acddcb60",
   "metadata": {},
   "source": "## Splitting and Tokenizing\n\nSplitting turns one text line into meaningful fields."
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string line    = \"Ada,98,CS\";\nstring[] parts = line.Split(',');\n\nConsole.WriteLine(parts[0]);  // Ada\nConsole.WriteLine(parts[1]);  // 98\nConsole.WriteLine(parts[2]);  // CS",
   "id": "18e0436a"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "Input often has extra whitespace — trim each field after splitting:",
   "id": "acc2ea71"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string line = \" Ada , 98 , CS \";\nstring[] parts = line.Split(',');\n\nfor (int i = 0; i < parts.Length; i++)\n    parts[i] = parts[i].Trim();\n\nforeach (var p in parts)\n    Console.WriteLine($\"'{p}'\");",
   "id": "eef30e34"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "When input may contain repeated delimiters, remove empty entries:",
   "id": "bef45014"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string line = \"Ada,,CS\";\nstring[] parts = line.Split(',', StringSplitOptions.RemoveEmptyEntries);\n\nConsole.WriteLine(parts.Length);  // 2",
   "id": "82b18a5f"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "### Token Validation\n\nValidate the number of fields before accessing by index:",
   "id": "b9c2ac40"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string line = \"Ada,98,CS\";\nstring[] parts = line.Split(',');\n\nif (parts.Length != 3)\n{\n    Console.WriteLine(\"Invalid record: expected 3 fields.\");\n}\nelse\n{\n    Console.WriteLine($\"Name: {parts[0].Trim()}, Score: {parts[1].Trim()}, Major: {parts[2].Trim()}\");\n}",
   "id": "1c925a50"
  },
  {
   "cell_type": "markdown",
   "id": "6f4e1f40",
   "metadata": {},
   "source": "---"
  },
  {
   "cell_type": "markdown",
   "id": "c2fd1a85",
   "metadata": {},
   "source": "## Search and Replace\n\nSearch and replace is useful for cleanup and normalization pipelines."
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string message = \"error: invalid input\";\n\nbool hasError = message.Contains(\"error\",  StringComparison.OrdinalIgnoreCase);\nint  at       = message.IndexOf(\"invalid\", StringComparison.OrdinalIgnoreCase);\n\nConsole.WriteLine($\"hasError: {hasError}\");\nConsole.WriteLine($\"'invalid' starts at index: {at}\");",
   "id": "5f82788e"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "Use `StringComparison.OrdinalIgnoreCase` for consistent case-insensitive checks.",
   "id": "4bdcfce3"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string raw     = \"Name: Alice\\tScore: 98\";\nstring cleaned = raw.Replace(\"\\t\", \" \");\n\nConsole.WriteLine(cleaned);",
   "id": "85b5f5e1"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "### Safe Replacement Strategy\n\n1. Preserve the original text\n2. Create a cleaned copy\n3. Validate the output\n4. Write to a new file first",
   "id": "34fd8262"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string original = \"Score: N/A\";\nstring updated  = original.Replace(\"N/A\", \"0\");\n\nConsole.WriteLine($\"original: {original}\");\nConsole.WriteLine($\"updated:  {updated}\");",
   "id": "8dc6d75f"
  },
  {
   "cell_type": "markdown",
   "id": "b26e03a3",
   "metadata": {},
   "source": "---"
  },
  {
   "cell_type": "markdown",
   "id": "bc3006d7",
   "metadata": {},
   "source": "## Structured Lines\n\nStructured lines are text records with predictable field order.\n\nExample record: `Alice,98,CS`\n\nField model:\n1. name (`string`)\n2. score (`int`)\n3. major (`string`)"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "static bool TryParseStudent(string line, out string name, out int score, out string major)\n{\n    name  = \"\";\n    major = \"\";\n    score = 0;\n\n    if (string.IsNullOrWhiteSpace(line)) return false;\n\n    string[] parts = line.Split(',');\n    if (parts.Length != 3) return false;\n\n    name  = parts[0].Trim();\n    major = parts[2].Trim();\n\n    return int.TryParse(parts[1].Trim(), out score);\n}",
   "id": "53856479"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "string[] records = { \"Alice,98,CS\", \"Bob,abc,Math\", \"Carol,87,Physics\", \"\" };\n\nforeach (string line in records)\n{\n    if (TryParseStudent(line, out string name, out int score, out string major))\n        Console.WriteLine($\"{name} ({major}) scored {score}\");\n    else\n        Console.WriteLine($\"Skipped malformed line: '{line}'\");\n}",
   "id": "7f7f6d6b"
  },
  {
   "cell_type": "markdown",
   "id": "footnotes",
   "metadata": {},
   "source": "```{rubric} Footnotes\n```\n"
  }
 ]
}