{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "94582234",
   "metadata": {},
   "source": [
    "# 第 7 讲｜Python 文件处理：文本读写与数据格式化\n",
    "\n",
    "变量中的数据会随着程序结束而消失，文件可以把数据持久保存。本课按照“概念 → 示例代码 →\n",
    "代码解读 → 动手练习”学习文本文件、CSV 和 JSON，并使用 `示例数据` 文件夹中的小型文件完成练习。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b66cf9a",
   "metadata": {},
   "source": [
    "## 训练路线\n",
    "\n",
    "| 阶段 | 学习内容 | 目标 |\n",
    "|---|---|---|\n",
    "| 一 | 文件路径、类型与打开方式 | 能安全找到并打开文本文件 |\n",
    "| 二 | 写入、读取与文件指针 | 能保存和读取文本内容 |\n",
    "| 三 | 文件与目录查看 | 能确认当前工作目录和文件位置 |\n",
    "| 四 | 一维、二维和多维数据 | 能使用分隔文本、CSV 和 JSON |\n",
    "| 五 | 综合实训 | 能完成备份和数字文件处理 |\n",
    "\n",
    "所有读写示例都使用 `示例数据` 子文件夹中以 `7f_demo_` 开头的文件，避免覆盖其他文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8df6199",
   "metadata": {},
   "source": [
    "## 教材内容取舍\n",
    "\n",
    "依据 7f 教材，本课重点掌握 with open、UTF-8 编码、r/w/a 模式、read/readline/readlines、\n",
    "write/writelines、文件遍历、tell/seek、目录查看、一维和二维数据以及 JSON。\n",
    "\n",
    "二进制文件、r+/w+/a+ 混合模式、相对文件末尾的 seek、XML，以及 remove、rename、\n",
    "rmdir、chdir 等可能删除或改变文件位置的操作，暂列为阅读拓展，不放入可执行示例。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b138f697",
   "metadata": {},
   "source": [
    "## 1. 文件标识、相对路径与文件类型\n",
    "\n",
    "**学习目标：**能说出一个文件标识中的路径、文件名主干和扩展名。\n",
    "\n",
    "相对路径从当前工作目录出发；绝对路径从磁盘或系统根目录开始。\n",
    "文本文件保存可阅读字符，例如 TXT、CSV、JSON；图片、音频、视频等通常属于二进制文件。\n",
    "\n",
    "本课主要处理文本文件，默认使用 UTF-8 编码。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "caf7dc10",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2413501",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.040613Z",
     "iopub.status.busy": "2026-07-30T06:58:25.040329Z",
     "iopub.status.idle": "2026-07-30T06:58:25.049222Z",
     "shell.execute_reply": "2026-07-30T06:58:25.048572Z"
    }
   },
   "outputs": [],
   "source": [
    "relative_path = \"data/score.csv\"\n",
    "path_parts = relative_path.split(\"/\")\n",
    "file_name = path_parts[-1]\n",
    "name_parts = file_name.split(\".\")\n",
    "\n",
    "print(\"相对路径：\", relative_path)\n",
    "print(\"所在目录：\", path_parts[0])\n",
    "print(\"完整文件名：\", file_name)\n",
    "print(\"文件名主干：\", name_parts[0])\n",
    "print(\"扩展名：\", name_parts[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41df0d75",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "relative_path 只描述从当前目录到文件的位置，因此是相对路径。\n",
    "第一次 split 按斜杠拆出目录和文件名；第二次 split 按点号拆出文件名主干和扩展名。\n",
    "这里仅分析路径文字，不要求该文件真实存在。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e39ecb3b",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "62118a77",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.052971Z",
     "iopub.status.busy": "2026-07-30T06:58:25.052695Z",
     "iopub.status.idle": "2026-07-30T06:58:25.055325Z",
     "shell.execute_reply": "2026-07-30T06:58:25.054975Z"
    }
   },
   "outputs": [],
   "source": [
    "# file_path = \"reports/sales.json\"\n",
    "# path_parts =                         # 按 / 拆分\n",
    "# file_name =                          # 读取最后一项\n",
    "# name_parts =                         # 按 . 拆分\n",
    "# print(\"目录：\", path_parts[0])\n",
    "# print(\"主干：\", name_parts[0])\n",
    "# print(\"扩展名：\", name_parts[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47dcd078",
   "metadata": {},
   "source": [
    "## 2. with open：安全打开并自动关闭文件\n",
    "\n",
    "**学习目标：**使用 with open 完成一次完整的写入和读取。\n",
    "\n",
    "`open(文件名, 模式, encoding=\"utf-8\")` 打开文本文件并返回文件对象。\n",
    "`with` 代码块结束时会自动关闭文件，不需要手动调用 `close`。\n",
    "`write` 写入字符串，`read` 读取字符串；后续知识点会分别展开。\n",
    "\n",
    "下面先用 `data_dir = \"示例数据/\"` 保存示例文件夹路径，后面的代码继续使用这个变量。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e64c9e4e",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ee32fe9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.057004Z",
     "iopub.status.busy": "2026-07-30T06:58:25.056858Z",
     "iopub.status.idle": "2026-07-30T06:58:25.060243Z",
     "shell.execute_reply": "2026-07-30T06:58:25.059900Z"
    }
   },
   "outputs": [],
   "source": [
    "data_dir = \"示例数据/\"\n",
    "\n",
    "file_name = data_dir + \"7f_demo_basic.txt\"\n",
    "\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    file.write(\"Python 文件训练\")\n",
    "\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    content = file.read()\n",
    "\n",
    "print(\"读取内容：\", content)\n",
    "print(\"文件对象已经由 with 自动关闭\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1ef42d5",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "第一次 with 使用 w 模式创建或覆盖文件，并用 write 写入文字。\n",
    "第二次 with 使用 r 模式重新打开同一文件，read 读取全部内容。\n",
    "两个 with 代码块结束后，对应文件对象都会自动关闭。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af9b02da",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "89279ec6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.062073Z",
     "iopub.status.busy": "2026-07-30T06:58:25.061941Z",
     "iopub.status.idle": "2026-07-30T06:58:25.063761Z",
     "shell.execute_reply": "2026-07-30T06:58:25.063455Z"
    }
   },
   "outputs": [],
   "source": [
    "# file_name = data_dir + \"7f_demo_practice.txt\"\n",
    "# with open(file_name,       , encoding=\"utf-8\") as file:\n",
    "#     file.write(\"数据分析训练\")\n",
    "#\n",
    "# with open(file_name,       , encoding=\"utf-8\") as file:\n",
    "#     content = file.read()\n",
    "# print(content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "971a4a12",
   "metadata": {},
   "source": [
    "## 3. 三种基本模式：r、w 与 a\n",
    "\n",
    "**学习目标：**根据任务选择读取、覆盖写入或末尾追加。\n",
    "\n",
    "- r：只读；文件不存在时无法打开；\n",
    "- w：覆盖写入；已有内容会被清空，文件不存在时会创建；\n",
    "- a：追加写入；内容添加到末尾，不清空原文件。\n",
    "\n",
    "使用 w 前必须确认目标文件，防止覆盖重要数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e08b1540",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0ab8614",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.065345Z",
     "iopub.status.busy": "2026-07-30T06:58:25.065218Z",
     "iopub.status.idle": "2026-07-30T06:58:25.068342Z",
     "shell.execute_reply": "2026-07-30T06:58:25.068048Z"
    }
   },
   "outputs": [],
   "source": [
    "file_name = data_dir + \"7f_demo_modes.txt\"\n",
    "\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    file.write(\"第一行：使用 w 写入\\n\")\n",
    "\n",
    "with open(file_name, \"a\", encoding=\"utf-8\") as file:\n",
    "    file.write(\"第二行：使用 a 追加\\n\")\n",
    "\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    content = file.read()\n",
    "\n",
    "print(content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68415c5e",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "w 模式先建立确定的初始内容；a 模式没有清空第一行，而是在文件末尾加入第二行；\n",
    "r 模式只读取，不修改文件。换行符 \\n 让两段文字分别占一行。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46592c54",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17a135ec",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.069973Z",
     "iopub.status.busy": "2026-07-30T06:58:25.069861Z",
     "iopub.status.idle": "2026-07-30T06:58:25.071903Z",
     "shell.execute_reply": "2026-07-30T06:58:25.071591Z"
    }
   },
   "outputs": [],
   "source": [
    "# file_name = data_dir + \"7f_demo_mode_practice.txt\"\n",
    "# with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "#     file.write(\"销售记录 1\\n\")\n",
    "# with open(file_name,       , encoding=\"utf-8\") as file:\n",
    "#     file.write(\"销售记录 2\\n\")       # 应当追加\n",
    "# with open(file_name,       , encoding=\"utf-8\") as file:\n",
    "#     print(file.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0935cd91",
   "metadata": {},
   "source": [
    "## 4. 写入方法：write 与 writelines\n",
    "\n",
    "**学习目标：**写入一个字符串或一组字符串。\n",
    "\n",
    "write(字符串) 写入一个字符串，并返回写入的字符数。\n",
    "writelines(字符串列表) 按顺序写入列表中的所有字符串，但不会自动添加换行符；\n",
    "需要换行时，字符串中必须包含 \\n。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "731d02cc",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "09db172f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.073611Z",
     "iopub.status.busy": "2026-07-30T06:58:25.073478Z",
     "iopub.status.idle": "2026-07-30T06:58:25.076579Z",
     "shell.execute_reply": "2026-07-30T06:58:25.076290Z"
    }
   },
   "outputs": [],
   "source": [
    "file_name = data_dir + \"7f_demo_lines.txt\"\n",
    "lines = [\n",
    "    \"商品,销量\\n\",\n",
    "    \"双肩包,125\\n\",\n",
    "    \"旅行箱,80\\n\",\n",
    "]\n",
    "\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    first_size = file.write(\"数据文件示例\\n\")\n",
    "    file.writelines(lines)\n",
    "\n",
    "print(\"第一段写入字符数：\", first_size)\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    print(file.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68115613",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "write 只写入第一段文字，并把字符数保存到 first_size。\n",
    "writelines 接收字符串列表 lines，逐项写入。每项末尾已经写有 \\n，\n",
    "因此读取时能看到多行内容。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "952b408d",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb5a1287",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.078167Z",
     "iopub.status.busy": "2026-07-30T06:58:25.078045Z",
     "iopub.status.idle": "2026-07-30T06:58:25.079828Z",
     "shell.execute_reply": "2026-07-30T06:58:25.079491Z"
    }
   },
   "outputs": [],
   "source": [
    "# rows = [\"姓名,成绩\\n\", \"小林,88\\n\", \"小周,92\\n\"]\n",
    "# with open(data_dir + \"7f_demo_write_practice.txt\", \"w\", encoding=\"utf-8\") as file:\n",
    "#     file.                             # 使用 writelines\n",
    "# with open(data_dir + \"7f_demo_write_practice.txt\", \"r\", encoding=\"utf-8\") as file:\n",
    "#     print(file.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d2f92ca",
   "metadata": {},
   "source": [
    "## 5. 读取方法：read、readline 与 readlines\n",
    "\n",
    "**学习目标：**区分读取全部内容、一行内容和全部行列表。\n",
    "\n",
    "- read()：读取剩余的全部内容，返回字符串；\n",
    "- readline()：读取下一行，返回字符串；\n",
    "- readlines()：读取全部行，返回字符串列表。\n",
    "\n",
    "同一个文件对象连续读取时，会从上一次结束的位置继续。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "633a8735",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "de8f81ba",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.081373Z",
     "iopub.status.busy": "2026-07-30T06:58:25.081253Z",
     "iopub.status.idle": "2026-07-30T06:58:25.084976Z",
     "shell.execute_reply": "2026-07-30T06:58:25.084662Z"
    }
   },
   "outputs": [],
   "source": [
    "file_name = data_dir + \"7f_demo_reading.txt\"\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    file.writelines([\"第一行\\n\", \"第二行\\n\", \"第三行\\n\"])\n",
    "\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    first_line = file.readline()\n",
    "    second_line = file.readline()\n",
    "print(\"readline：\", first_line.strip(), second_line.strip())\n",
    "\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    all_lines = file.readlines()\n",
    "print(\"readlines：\", all_lines)\n",
    "\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    all_text = file.read()\n",
    "print(\"read：\")\n",
    "print(all_text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1802d1d",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "第一次打开文件后连续调用两次 readline，分别得到第一行和第二行。\n",
    "为了重新从文件开头读取，后两种方法各自重新打开文件。\n",
    "strip 用于显示时去掉行末换行符，不会修改文件内容。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ae731cc",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c888822b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.086424Z",
     "iopub.status.busy": "2026-07-30T06:58:25.086314Z",
     "iopub.status.idle": "2026-07-30T06:58:25.088012Z",
     "shell.execute_reply": "2026-07-30T06:58:25.087770Z"
    }
   },
   "outputs": [],
   "source": [
    "# file_name = data_dir + \"7f_demo_reading.txt\"\n",
    "# with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "#     first_line =                         # 读取一行\n",
    "# print(first_line.strip())\n",
    "#\n",
    "# with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "#     lines =                              # 读取为行列表\n",
    "# print(lines)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed30160d",
   "metadata": {},
   "source": [
    "## 6. 逐行遍历文件\n",
    "\n",
    "**学习目标：**使用 for 循环逐行处理文本文件。\n",
    "\n",
    "文件对象可以直接放入 for 循环，每次得到一行。\n",
    "这种方式不需要一次把整个大文件读入内存，是数据清洗中最常见的基础读法。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84c2a810",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6069c749",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.089464Z",
     "iopub.status.busy": "2026-07-30T06:58:25.089366Z",
     "iopub.status.idle": "2026-07-30T06:58:25.092285Z",
     "shell.execute_reply": "2026-07-30T06:58:25.092012Z"
    }
   },
   "outputs": [],
   "source": [
    "file_name = data_dir + \"7f_demo_iteration.txt\"\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    file.writelines([\n",
    "        \"# 注释行\\n\",\n",
    "        \"双肩包,125\\n\",\n",
    "        \"旅行箱,80\\n\",\n",
    "    ])\n",
    "\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    for line in file:\n",
    "        clean_line = line.strip()\n",
    "        if clean_line.startswith(\"#\"):\n",
    "            continue\n",
    "        print(\"有效记录：\", clean_line)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d19e87a2",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "for line in file 每次读取一行；strip 删除行末换行和两端空白；\n",
    "startswith(\"#\") 判断是否为注释行。遇到注释时 continue 跳过本轮，\n",
    "因此只输出两条有效记录。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5647766",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99e92d5d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.093845Z",
     "iopub.status.busy": "2026-07-30T06:58:25.093731Z",
     "iopub.status.idle": "2026-07-30T06:58:25.095409Z",
     "shell.execute_reply": "2026-07-30T06:58:25.095135Z"
    }
   },
   "outputs": [],
   "source": [
    "# 目标：逐行读取文件，跳过空行。\n",
    "# with open(data_dir + \"7f_demo_iteration.txt\", \"r\", encoding=\"utf-8\") as file:\n",
    "#     for line in file:\n",
    "#         clean_line = line.\n",
    "#         if clean_line == \"\":\n",
    "#             continue\n",
    "#         print(clean_line)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a33118d",
   "metadata": {},
   "source": [
    "## 7. 文件指针：tell 与 seek\n",
    "\n",
    "**学习目标：**查看当前读取位置，并回到文件开头。\n",
    "\n",
    "tell() 返回当前文件指针位置；seek(0) 把指针移回文件开头。\n",
    "文本文件位置按字节计算，中文 UTF-8 字符可能占多个字节，因此示例使用英文字符。\n",
    "从当前位置或文件末尾进行复杂偏移暂不作为必做。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d8b538e7",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27a96995",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.096892Z",
     "iopub.status.busy": "2026-07-30T06:58:25.096780Z",
     "iopub.status.idle": "2026-07-30T06:58:25.099813Z",
     "shell.execute_reply": "2026-07-30T06:58:25.099541Z"
    }
   },
   "outputs": [],
   "source": [
    "file_name = data_dir + \"7f_demo_pointer.txt\"\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    file.write(\"abcdefghij\")\n",
    "\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    print(\"初始位置：\", file.tell())\n",
    "    print(\"读取三个字符：\", file.read(3))\n",
    "    print(\"读取后位置：\", file.tell())\n",
    "\n",
    "    file.seek(0)\n",
    "    print(\"回到开头：\", file.tell())\n",
    "    print(\"再次读取两个字符：\", file.read(2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "159692e9",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "文件刚打开时指针位置为 0。read(3) 读取 abc 后，位置变成 3。\n",
    "seek(0) 把指针移回开头，因此下一次 read(2) 又从 a 开始，得到 ab。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e4f489d",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0da195ed",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.101268Z",
     "iopub.status.busy": "2026-07-30T06:58:25.101174Z",
     "iopub.status.idle": "2026-07-30T06:58:25.102858Z",
     "shell.execute_reply": "2026-07-30T06:58:25.102594Z"
    }
   },
   "outputs": [],
   "source": [
    "# with open(data_dir + \"7f_demo_pointer.txt\", \"r\", encoding=\"utf-8\") as file:\n",
    "#     file.read(5)\n",
    "#     print(file.                    )       # 查看当前位置\n",
    "#     file.                          # 回到文件开头\n",
    "#     print(file.read(1))                    # 预期：a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aef7972c",
   "metadata": {},
   "source": [
    "## 8. 查看当前目录与文件列表\n",
    "\n",
    "**学习目标：**使用 os 模块确认相对路径的起点和目录内容。\n",
    "\n",
    "`import os` 导入 Python 自带的操作系统工具。\n",
    "`os.getcwd()` 返回当前工作目录；`os.listdir(data_dir)` 返回 `示例数据` 文件夹中的名称列表。\n",
    "本节只查看目录，不执行删除、重命名或切换目录。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4af2287",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8882c6bc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.104304Z",
     "iopub.status.busy": "2026-07-30T06:58:25.104197Z",
     "iopub.status.idle": "2026-07-30T06:58:25.106584Z",
     "shell.execute_reply": "2026-07-30T06:58:25.106278Z"
    }
   },
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "current_directory = os.getcwd()\n",
    "names = os.listdir(data_dir)\n",
    "\n",
    "print(\"当前工作目录：\", current_directory)\n",
    "print(\"示例数据文件夹中的演示文件：\")\n",
    "for name in sorted(names):\n",
    "    if name.startswith(\"7f_demo_\"):\n",
    "        print(name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a85b29e",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "相对路径会从 `os.getcwd()` 显示的当前目录开始查找。\n",
    "`os.listdir(data_dir)` 查看 `示例数据` 文件夹；示例按名称排序后，\n",
    "只输出以 `7f_demo_` 开头的文件，便于确认前面代码生成了哪些文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b93d0729",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d394c96c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.108078Z",
     "iopub.status.busy": "2026-07-30T06:58:25.107965Z",
     "iopub.status.idle": "2026-07-30T06:58:25.109554Z",
     "shell.execute_reply": "2026-07-30T06:58:25.109274Z"
    }
   },
   "outputs": [],
   "source": [
    "# import os\n",
    "# current_directory =                         # 获取当前目录\n",
    "# names = os.listdir(__________)              # 获取示例数据文件夹中的名称列表\n",
    "# print(current_directory)\n",
    "# for name in names:\n",
    "#     if name.endswith(\".txt\"):\n",
    "#         print(name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0135d7a0",
   "metadata": {},
   "source": [
    "## 9. 一维、二维与多维数据\n",
    "\n",
    "**学习目标：**根据数据关系判断维度和适合的 Python 结构。\n",
    "\n",
    "一维数据是一组对等元素，可用一维列表表示；二维数据由多行多列构成，\n",
    "可用列表嵌套列表表示；多维数据包含更复杂的层级和键值关系，可用字典与列表嵌套表示。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d00bfe9",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82239cc8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.110924Z",
     "iopub.status.busy": "2026-07-30T06:58:25.110815Z",
     "iopub.status.idle": "2026-07-30T06:58:25.113506Z",
     "shell.execute_reply": "2026-07-30T06:58:25.113158Z"
    }
   },
   "outputs": [],
   "source": [
    "cities = [\"成都\", \"杭州\", \"重庆\"]\n",
    "\n",
    "score_table = [\n",
    "    [\"姓名\", \"语文\", \"数学\"],\n",
    "    [\"小红\", 124, 137],\n",
    "    [\"小明\", 116, 143],\n",
    "]\n",
    "\n",
    "student = {\n",
    "    \"姓名\": \"小红\",\n",
    "    \"成绩\": {\"语文\": 124, \"数学\": 137},\n",
    "    \"标签\": [\"班长\", \"竞赛组\"],\n",
    "}\n",
    "\n",
    "print(\"一维数据：\", cities)\n",
    "print(\"二维数据第二行：\", score_table[1])\n",
    "print(\"多维数据中的数学成绩：\", student[\"成绩\"][\"数学\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81bbfb65",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "cities 中的城市地位对等，是一维数据；score_table 的外层列表保存多行，\n",
    "每个内层列表保存一行字段，是二维数据；student 同时包含字典和列表，\n",
    "需要通过多层键访问，因此属于多维结构。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4bc3f02d",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4027ddf9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.114916Z",
     "iopub.status.busy": "2026-07-30T06:58:25.114812Z",
     "iopub.status.idle": "2026-07-30T06:58:25.116625Z",
     "shell.execute_reply": "2026-07-30T06:58:25.116323Z"
    }
   },
   "outputs": [],
   "source": [
    "# one_dimension =                         # 三个商品名称\n",
    "# two_dimension = [                       # 表头和两行商品记录\n",
    "#     [\"商品\", \"销量\"],\n",
    "# ]\n",
    "# multi_dimension = {                     # 店铺名称和商品列表\n",
    "#     \"店铺\": \"A店\",\n",
    "#     \"商品\": [],\n",
    "# }\n",
    "# print(one_dimension)\n",
    "# print(two_dimension)\n",
    "# print(multi_dimension)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a861d83",
   "metadata": {},
   "source": [
    "## 10. 一维数据存储：join 与 split\n",
    "\n",
    "**学习目标：**把列表转换为分隔文本，再从文本恢复列表。\n",
    "\n",
    "分隔符必须在同一文件中保持一致，并且尽量不要出现在数据内容中。\n",
    "分隔符.join(字符串列表) 用于拼接；字符串.split(分隔符) 用于拆分。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed69ae40",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ceaa8696",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.118058Z",
     "iopub.status.busy": "2026-07-30T06:58:25.117959Z",
     "iopub.status.idle": "2026-07-30T06:58:25.121044Z",
     "shell.execute_reply": "2026-07-30T06:58:25.120759Z"
    }
   },
   "outputs": [],
   "source": [
    "file_name = data_dir + \"7f_demo_cities.txt\"\n",
    "cities = [\"成都\", \"杭州\", \"重庆\", \"武汉\"]\n",
    "city_text = \",\".join(cities)\n",
    "\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    file.write(city_text)\n",
    "\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    saved_text = file.read()\n",
    "\n",
    "restored_cities = saved_text.split(\",\")\n",
    "print(\"文件文字：\", saved_text)\n",
    "print(\"恢复列表：\", restored_cities)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cebcbf34",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "join 使用英文逗号把城市列表拼成一个字符串，适合写入文本文件。\n",
    "读取后，split 使用相同分隔符恢复列表。写入和读取必须约定同一种分隔符。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0c7c115",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a72e3e69",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.122552Z",
     "iopub.status.busy": "2026-07-30T06:58:25.122431Z",
     "iopub.status.idle": "2026-07-30T06:58:25.124228Z",
     "shell.execute_reply": "2026-07-30T06:58:25.123909Z"
    }
   },
   "outputs": [],
   "source": [
    "# products = [\"双肩包\", \"旅行箱\", \"钱包\"]\n",
    "# product_text =                     # 使用 & 拼接\n",
    "# with open(data_dir + \"7f_demo_products.txt\", \"w\", encoding=\"utf-8\") as file:\n",
    "#     file.write(product_text)\n",
    "# restored = product_text.            # 使用 & 拆分\n",
    "# print(restored)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5fbba43",
   "metadata": {},
   "source": [
    "## 11. 二维数据存储：简单 CSV 读写\n",
    "\n",
    "**学习目标：**理解 CSV 的“每行一条记录、字段用逗号分隔”结构。\n",
    "\n",
    "本节使用 split 和 join 展示 CSV 基本原理，数据字段中不包含逗号。\n",
    "若真实字段可能包含逗号、引号或换行，应使用 Python 的 csv 模块或 pandas。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "061810f2",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d1064fa",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.125635Z",
     "iopub.status.busy": "2026-07-30T06:58:25.125525Z",
     "iopub.status.idle": "2026-07-30T06:58:25.128611Z",
     "shell.execute_reply": "2026-07-30T06:58:25.128370Z"
    }
   },
   "outputs": [],
   "source": [
    "file_name = data_dir + \"7f_demo_scores.csv\"\n",
    "rows = [\n",
    "    [\"姓名\", \"语文\", \"数学\"],\n",
    "    [\"小红\", \"124\", \"137\"],\n",
    "    [\"小明\", \"116\", \"143\"],\n",
    "]\n",
    "\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    for row in rows:\n",
    "        file.write(\",\".join(row) + \"\\n\")\n",
    "\n",
    "table = []\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    for line in file:\n",
    "        row = line.strip().split(\",\")\n",
    "        table.append(row)\n",
    "\n",
    "print(table)\n",
    "print(\"小红的数学成绩：\", table[1][2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3759017",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "写入时，每个内层列表先用 join 变成一行，再补换行符。\n",
    "读取时，每行先 strip，再用 split 恢复为内层列表，最后 append 到 table。\n",
    "table[1][2] 先选第二行，再选第三列。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9700b59b",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e1d8c2b5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.129945Z",
     "iopub.status.busy": "2026-07-30T06:58:25.129840Z",
     "iopub.status.idle": "2026-07-30T06:58:25.131579Z",
     "shell.execute_reply": "2026-07-30T06:58:25.131319Z"
    }
   },
   "outputs": [],
   "source": [
    "# rows = [\n",
    "#     [\"商品\", \"销量\"],\n",
    "#     [\"双肩包\", \"125\"],\n",
    "#     [\"旅行箱\", \"80\"],\n",
    "# ]\n",
    "# with open(data_dir + \"7f_demo_sales.csv\", \"w\", encoding=\"utf-8\") as file:\n",
    "#     for row in rows:\n",
    "#         file.write(                            )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "496ae191",
   "metadata": {},
   "source": [
    "## 12. JSON：保存和读取嵌套数据\n",
    "\n",
    "**学习目标：**使用 json 模块在 Python 数据和 JSON 文件之间转换。\n",
    "\n",
    "json.dump(Python 数据, 文件对象) 把字典或列表写入 JSON 文件；\n",
    "json.load(文件对象) 从 JSON 文件读取并还原为 Python 数据。\n",
    "ensure_ascii=False 让中文直接显示，indent=2 让文件层级更易阅读。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f7bd098",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "057178c5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.132900Z",
     "iopub.status.busy": "2026-07-30T06:58:25.132798Z",
     "iopub.status.idle": "2026-07-30T06:58:25.135890Z",
     "shell.execute_reply": "2026-07-30T06:58:25.135615Z"
    }
   },
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "file_name = data_dir + \"7f_demo_data.json\"\n",
    "products = [\n",
    "    {\"商品\": \"双肩包\", \"销量\": 125},\n",
    "    {\"商品\": \"旅行箱\", \"销量\": 80},\n",
    "]\n",
    "\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    json.dump(products, file, ensure_ascii=False, indent=2)\n",
    "\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    loaded_products = json.load(file)\n",
    "\n",
    "print(loaded_products)\n",
    "print(\"第一件商品：\", loaded_products[0][\"商品\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e59e453",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "dump 把 Python 的“列表中放字典”写成 JSON 数组和对象；\n",
    "load 再把 JSON 还原为列表和字典。读取后仍使用列表索引和字典键访问数据。\n",
    "JSON 特别适合保存接口响应一类多层结构。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8cfa424",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25813aa0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.137345Z",
     "iopub.status.busy": "2026-07-30T06:58:25.137232Z",
     "iopub.status.idle": "2026-07-30T06:58:25.138976Z",
     "shell.execute_reply": "2026-07-30T06:58:25.138724Z"
    }
   },
   "outputs": [],
   "source": [
    "# import json\n",
    "# student = {\"姓名\": \"小林\", \"成绩\": {\"语文\": 88, \"数学\": 92}}\n",
    "# with open(data_dir + \"7f_demo_student.json\", \"w\", encoding=\"utf-8\") as file:\n",
    "#     json.                                  # 使用 dump\n",
    "# with open(data_dir + \"7f_demo_student.json\", \"r\", encoding=\"utf-8\") as file:\n",
    "#     loaded = json.                         # 使用 load\n",
    "# print(loaded[\"成绩\"][\"数学\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5d91e38",
   "metadata": {},
   "source": [
    "## 13. 综合实训一：逐行备份文本文件\n",
    "\n",
    "**任务目标：**把源文件逐行复制到备份文件。\n",
    "\n",
    "示例先创建确定的源文件，再分别以 r 和 w 模式打开源文件与备份文件。\n",
    "整个过程不删除、不重命名原文件，可以安全重复运行。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad368146",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27db47cb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.140455Z",
     "iopub.status.busy": "2026-07-30T06:58:25.140338Z",
     "iopub.status.idle": "2026-07-30T06:58:25.143670Z",
     "shell.execute_reply": "2026-07-30T06:58:25.143414Z"
    }
   },
   "outputs": [],
   "source": [
    "source_name = data_dir + \"7f_demo_source.txt\"\n",
    "backup_name = data_dir + \"7f_demo_backup.txt\"\n",
    "\n",
    "with open(source_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    file.writelines([\n",
    "        \"订单 A001\\n\",\n",
    "        \"订单 A002\\n\",\n",
    "        \"订单 A003\\n\",\n",
    "    ])\n",
    "\n",
    "with open(source_name, \"r\", encoding=\"utf-8\") as source_file:\n",
    "    with open(backup_name, \"w\", encoding=\"utf-8\") as backup_file:\n",
    "        for line in source_file:\n",
    "            backup_file.write(line)\n",
    "\n",
    "with open(backup_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    print(file.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1acc8b8",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "外层 with 只读源文件，内层 with 覆盖写入备份文件。\n",
    "for 循环每次取得源文件一行，write 把原行完整写入备份。\n",
    "两个 with 结束后，两个文件都会自动关闭。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f4938f2",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4d16686",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.145236Z",
     "iopub.status.busy": "2026-07-30T06:58:25.145134Z",
     "iopub.status.idle": "2026-07-30T06:58:25.146910Z",
     "shell.execute_reply": "2026-07-30T06:58:25.146653Z"
    }
   },
   "outputs": [],
   "source": [
    "# source_name = data_dir + \"7f_demo_source.txt\"\n",
    "# backup_name = data_dir + \"7f_demo_backup_practice.txt\"\n",
    "# with open(source_name,       , encoding=\"utf-8\") as source_file:\n",
    "#     with open(backup_name,       , encoding=\"utf-8\") as backup_file:\n",
    "#         for line in source_file:\n",
    "#             backup_file."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "326c75ae",
   "metadata": {},
   "source": [
    "## 14. 综合实训二：读取、转换并排序数字\n",
    "\n",
    "**任务目标：**从多行文本中取得所有数字，转换为整数并排序。\n",
    "\n",
    "本例对应教材习题：每行可能包含一个或多个由空格分隔的数字。\n",
    "处理顺序是“逐行读取 → 按空格拆分 → 转换为整数 → 收集 → 排序”。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6bf99d43",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "930a28c0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.148421Z",
     "iopub.status.busy": "2026-07-30T06:58:25.148299Z",
     "iopub.status.idle": "2026-07-30T06:58:25.151394Z",
     "shell.execute_reply": "2026-07-30T06:58:25.151116Z"
    }
   },
   "outputs": [],
   "source": [
    "file_name = data_dir + \"7f_demo_numbers.txt\"\n",
    "with open(file_name, \"w\", encoding=\"utf-8\") as file:\n",
    "    file.writelines([\"10 6 55\\n\", \"20\\n\", \"80 30\\n\", \"5 8\\n\"])\n",
    "\n",
    "numbers = []\n",
    "with open(file_name, \"r\", encoding=\"utf-8\") as file:\n",
    "    for line in file:\n",
    "        parts = line.split()\n",
    "        for part in parts:\n",
    "            numbers.append(int(part))\n",
    "\n",
    "numbers.sort()\n",
    "print(\"排序结果：\", numbers)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6bff89e",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "不向 split 传分隔符时，它会按连续空白拆分文字，适合处理空格和换行。\n",
    "每个 part 仍是字符串，int 转换后才能按数值大小排序。\n",
    "numbers.sort() 直接把最终数字列表改为升序。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef53b797",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0b91b300",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T06:58:25.152898Z",
     "iopub.status.busy": "2026-07-30T06:58:25.152788Z",
     "iopub.status.idle": "2026-07-30T06:58:25.154479Z",
     "shell.execute_reply": "2026-07-30T06:58:25.154233Z"
    }
   },
   "outputs": [],
   "source": [
    "# numbers = []\n",
    "# with open(data_dir + \"7f_demo_numbers.txt\", \"r\", encoding=\"utf-8\") as file:\n",
    "#     for line in file:\n",
    "#         parts = line.\n",
    "#         for part in parts:\n",
    "#             numbers.                       # 添加转换后的整数\n",
    "# numbers.                                   # 升序排序\n",
    "# print(numbers)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17f180ce",
   "metadata": {},
   "source": [
    "## 教材中的阅读拓展\n",
    "\n",
    "- **二进制模式 rb、wb、ab**：用于图片、音频等字节数据；\n",
    "- **r+、w+、a+**：同时允许读取和写入，指针规则更复杂；\n",
    "- **seek 的相对偏移**：文本文件与二进制文件规则不同，当前只要求 seek(0)；\n",
    "- **remove、rename、rmdir、chdir**：会删除、移动文件或改变工作目录，操作前必须确认目标；\n",
    "- **XML 与 HTML**：使用标签组织数据，后续网页采集专题再学习解析方法；\n",
    "- **标准 csv 模块**：真实 CSV 字段可能包含逗号和引号，届时应使用专用工具处理。\n",
    "\n",
    "对于会覆盖、删除或移动文件的操作，先用只读方式确认路径和目标，再执行修改。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e00afab6",
   "metadata": {},
   "source": [
    "## 离场检验\n",
    "\n",
    "1. 文件标识由哪三个部分组成？\n",
    "2. 相对路径以哪里作为起点？\n",
    "3. 文本文件和二进制文件有什么区别？\n",
    "4. 为什么推荐使用 with open？\n",
    "5. r、w、a 三种模式分别会怎样处理文件？\n",
    "6. write 与 writelines 有什么区别？\n",
    "7. read、readline、readlines 分别返回什么？\n",
    "8. 为什么大文件更适合逐行遍历？\n",
    "9. tell 与 seek(0) 分别有什么作用？\n",
    "10. 一维、二维和多维数据可以分别使用哪些 Python 结构？\n",
    "11. join 与 split 在一维数据读写中分别负责什么？\n",
    "12. 简单 CSV 的一行和一个字段分别怎样表示？\n",
    "13. json.dump 与 json.load 的转换方向是什么？\n",
    "14. 文件操作中为什么要特别谨慎使用 w、remove 和 rename？"
   ]
  }
 ],
 "metadata": {
  "course_source": "7f.pdf",
  "course_style": "培训课件：示例代码、对应解读、动手练习",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
