> ## Documentation Index
> Fetch the complete documentation index at: https://docs.akria.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Day 10 | 文件处理与正则

> 读写 JSON/YAML/CSV 配置文件与日志正则解析

<Info>
  **今日目标**：DevOps 日常工作本质上就是"处理文本"。今天我们将学习如何用 Python 优雅地读写 **JSON/YAML** 配置文件，并用 **正则表达式 (Regex)** 提取复杂的日志信息。今天不只是写代码，而是要彻底理解 **"为什么 JSON/YAML 是配置的标准格式"**、**"正则表达式如何高效匹配文本"** 以及 **"如何避免常见的文件处理陷阱"**。
</Info>

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="book" size={26} color="#059669" /> 学习内容 (30 mins)</span>

在开始写代码前，先搞懂这些核心概念，否则后面的代码你会看得云里雾里。

<AccordionGroup>
  <Accordion title="结构化数据处理 (15 mins)" icon="file">
    **什么是结构化数据？**

    结构化数据是指有固定格式的数据，如 JSON、YAML、CSV。这些格式都有明确的语法规则，便于程序解析。

    **为什么需要不同的数据格式？**

    * **JSON**：
      * 优点：轻量级、易读、广泛支持（API 响应、配置文件）
      * 缺点：不支持注释、格式较严格
      * 应用：API 响应、配置文件、数据交换
    * **YAML**：
      * 优点：支持注释、可读性强、支持多行字符串
      * 缺点：缩进敏感、解析较慢
      * 应用：Kubernetes 配置、Docker Compose、CI/CD 配置
    * **CSV**：
      * 优点：简单、Excel 兼容、适合大数据
      * 缺点：不支持嵌套结构、类型推断困难
      * 应用：数据导出、报表生成、数据分析

    **Python 处理方式**：

    * **JSON**：内置库 `json`，无需安装
    * **YAML**：第三方库 `PyYAML`，需要 `pip install pyyaml`
    * **CSV**：内置库 `csv`，无需安装
  </Accordion>

  <Accordion title="正则表达式 (15 mins)" icon="code">
    **什么是正则表达式？**

    正则表达式（Regular Expression，简称 Regex）是一种描述字符串模式的语法，用于匹配、查找、替换文本。

    **为什么需要正则表达式？**

    * **精确匹配**：可以匹配复杂的文本模式（如 IP 地址、邮箱、日期）
    * **批量处理**：一次匹配多个模式，比字符串方法更强大
    * **日志分析**：从日志中提取关键信息（IP、时间、错误信息）
    * **数据清洗**：清理和格式化数据

    **常用正则模式**：

    * `\d+`：匹配一个或多个数字（如：`123`、`4567`）
    * `\w+`：匹配一个或多个单词字符（字母、数字、下划线）
    * `.*?`：非贪婪匹配任意字符（尽可能少匹配）
    * `\s+`：匹配一个或多个空白字符（空格、制表符、换行）
    * `^`：匹配字符串开头
    * `$`：匹配字符串结尾
    * `()`：捕获组，用于提取匹配的内容

    **Python 正则方法**：

    * `re.search()`：查找第一个匹配（返回 Match 对象或 None）
    * `re.findall()`：查找所有匹配（返回列表）
    * `re.sub()`：替换匹配的文本
    * `re.match()`：从字符串开头匹配（类似 `^pattern`）
  </Accordion>
</AccordionGroup>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="code" size={26} color="#059669" /> 代码任务 (90 mins)</span>

<Steps>
  <Step title="环境准备">
    确保虚拟环境已激活，并安装必要的包：

    ```bash theme={null}
    # 确保虚拟环境已激活（提示符前有 (.venv)）
    source .venv/bin/activate

    # 安装 PyYAML（用于处理 YAML 文件）
    pip install pyyaml

    # 验证安装
    python -c "import yaml; print('PyYAML installed successfully')"
    ```
  </Step>

  <Step title="任务 A：JSON 文件处理">
    编写 `10_json_demo.py`，演示 JSON 的读写操作。

    <CodeGroup>
      ```python 10_json_demo.py theme={null}
      #!/usr/bin/env python3
      """
      Day 10 - JSON 文件处理示例
      演示 JSON 的读写和解析
      """

      import json

      # ========== 1. 写入 JSON 文件 ==========
      print("=== 写入 JSON 文件 ===")

      # 准备数据（字典格式）
      config_data = {
          "app": {
              "name": "insightful-ops",
              "version": "1.0.0",
              "debug": False
          },
          "database": {
              "host": "localhost",
              "port": 3306,
              "name": "infra_db"
          },
          "servers": ["web-01", "web-02", "db-01"]
      }

      # 写入 JSON 文件
      # indent=4: 格式化输出，每级缩进 4 个空格（便于阅读）
      # ensure_ascii=False: 允许非 ASCII 字符（如中文）
      with open("config.json", "w", encoding="utf-8") as f:
          json.dump(config_data, f, indent=4, ensure_ascii=False)

      print("✅ 配置文件已写入 config.json")

      # ========== 2. 读取 JSON 文件 ==========
      print("\n=== 读取 JSON 文件 ===")

      with open("config.json", "r", encoding="utf-8") as f:
          loaded_data = json.load(f)

      print(f"应用名称: {loaded_data['app']['name']}")
      print(f"数据库端口: {loaded_data['database']['port']}")
      print(f"服务器列表: {loaded_data['servers']}")

      # ========== 3. JSON 字符串处理 ==========
      print("\n=== JSON 字符串处理 ===")

      # 模拟 API 返回的 JSON 字符串
      api_response = '{"status": "success", "data": {"users": 100, "servers": 5}}'

      # 解析 JSON 字符串
      response_dict = json.loads(api_response)
      print(f"状态: {response_dict['status']}")
      print(f"用户数: {response_dict['data']['users']}")

      # 将字典转换为 JSON 字符串
      json_string = json.dumps(response_dict, indent=2)
      print(f"\nJSON 字符串:\n{json_string}")
      ```
    </CodeGroup>

    **代码解释**：

    * **`json.dump()`**：将字典写入文件
    * **`json.load()`**：从文件读取 JSON 并解析为字典
    * **`json.dumps()`**：将字典转换为 JSON 字符串
    * **`json.loads()`**：将 JSON 字符串解析为字典

    **运行脚本**：

    ```bash theme={null}
    python 10_json_demo.py
    ```

    **验证步骤**：

    1. 检查 `config.json` 文件是否创建
    2. 查看文件内容：`cat config.json`
    3. 确认 JSON 格式正确（缩进、引号等）
  </Step>

  <Step title="任务 B：YAML 文件处理">
    编写 `10_yaml_demo.py`，演示 YAML 的读写操作。

    <CodeGroup>
      ```python 10_yaml_demo.py theme={null}
      #!/usr/bin/env python3
      """
      Day 10 - YAML 文件处理示例
      演示 YAML 的读写和转换
      """

      import yaml
      import json

      # ========== 1. 创建 YAML 内容 ==========
      print("=== 创建 YAML 内容 ===")

      # YAML 支持注释和多行字符串
      yaml_content = """
      # 应用配置
      app:
          name: insightful-ops
          version: 1.0.0
          debug: false

      # 数据库配置
      database:
          host: localhost
          port: 3306
          name: infra_db

      # 服务器列表
      servers:
          - web-01
          - web-02
          - db-01
      """

      # ========== 2. 解析 YAML ==========
      print("=== 解析 YAML ===")

      # safe_load: 安全加载（只加载标准 YAML 标签，防止代码执行）
      data = yaml.safe_load(yaml_content)
      print(f"应用名称: {data['app']['name']}")
      print(f"数据库端口: {data['database']['port']}")

      # ========== 3. 写入 YAML 文件 ==========
      print("\n=== 写入 YAML 文件 ===")

      config_dict = {
          "app": {
              "name": "insightful-ops",
              "version": "1.0.0"
          },
          "database": {
              "host": "localhost",
              "port": 3306
          }
      }

      with open("config.yaml", "w", encoding="utf-8") as f:
          yaml.dump(config_dict, f, default_flow_style=False, allow_unicode=True)
          # default_flow_style=False: 使用块样式（更易读）
          # allow_unicode=True: 允许 Unicode 字符

      print("✅ 配置文件已写入 config.yaml")

      # ========== 4. YAML 转 JSON ==========
      print("\n=== YAML 转 JSON ===")

      # 读取 YAML
      with open("config.yaml", "r", encoding="utf-8") as f:
          yaml_data = yaml.safe_load(f)

      # 转换为 JSON
      with open("config_from_yaml.json", "w", encoding="utf-8") as f:
          json.dump(yaml_data, f, indent=4, ensure_ascii=False)

      print("✅ YAML 已转换为 JSON")
      ```
    </CodeGroup>

    **代码解释**：

    * **`yaml.safe_load()`**：安全加载 YAML（推荐，防止代码执行）
    * **`yaml.dump()`**：将字典写入 YAML 文件
    * **`default_flow_style=False`**：使用块样式，更易读

    **运行脚本**：

    ```bash theme={null}
    python 10_yaml_demo.py
    ```

    **验证步骤**：

    1. 检查 `config.yaml` 文件是否创建
    2. 查看文件内容：`cat config.yaml`
    3. 确认 YAML 格式正确（缩进、注释等）
  </Step>

  <Step title="任务 C：正则表达式实战">
    编写 `10_regex_demo.py`，演示正则表达式的使用。

    <CodeGroup>
      ```python 10_regex_demo.py theme={null}
      #!/usr/bin/env python3
      """
      Day 10 - 正则表达式实战
      演示从日志中提取关键信息
      """

      import re

      # ========== 1. 基础匹配 ==========
      print("=== 基础匹配 ===")

      # 匹配 IP 地址
      text = "Server IP: 192.168.1.100"
      pattern = r'\d+\.\d+\.\d+\.\d+'  # \d+ 匹配数字，\. 匹配点号

      match = re.search(pattern, text)
      if match:
          print(f"找到 IP: {match.group()}")  # 192.168.1.100

      # ========== 2. 捕获组 ==========
      print("\n=== 捕获组 ===")

      # 从日志中提取 IP、方法和状态码
      log_line = '192.168.1.10 - - [31/Jan/2026:14:00:00 +0800] "GET /index.html HTTP/1.1" 200 4096'

      # 正则模式解释：
      # (\d+\.\d+\.\d+\.\d+)  - 捕获组1：IP 地址
      # .*?                   - 非贪婪匹配任意字符
      # "(\w+)                - 捕获组2：请求方法（GET/POST等）
      # .*?"                  - 匹配到引号结束
      # \s(\d{3})\s           - 捕获组3：状态码（3位数字）
      pattern = r'(\d+\.\d+\.\d+\.\d+).*?"(\w+) .*?"\s(\d{3})\s'

      match = re.search(pattern, log_line)
      if match:
          ip = match.group(1)      # 第一个捕获组
          method = match.group(2)   # 第二个捕获组
          status = match.group(3)  # 第三个捕获组
          print(f"IP: {ip}, 方法: {method}, 状态码: {status}")

      # ========== 3. 查找所有匹配 ==========
      print("\n=== 查找所有匹配 ===")

      log_text = """
      192.168.1.10 - - [31/Jan/2026:14:00:00] "GET /index.html" 200
      192.168.1.11 - - [31/Jan/2026:14:01:00] "POST /api/data" 201
      192.168.1.12 - - [31/Jan/2026:14:02:00] "GET /about.html" 404
      """

      # 提取所有 IP 地址
      ips = re.findall(r'\d+\.\d+\.\d+\.\d+', log_text)
      print(f"所有 IP: {ips}")

      # 提取所有状态码
      status_codes = re.findall(r'\s(\d{3})\s', log_text)
      print(f"所有状态码: {status_codes}")

      # ========== 4. 文本替换 ==========
      print("\n=== 文本替换 ===")

      text = "Contact: email@example.com or phone: 138-0013-8000"

      # 替换邮箱为 [EMAIL]
      masked = re.sub(r'\w+@\w+\.\w+', '[EMAIL]', text)
      print(f"脱敏后: {masked}")

      # 替换手机号为 [PHONE]
      masked = re.sub(r'\d{3}-\d{4}-\d{4}', '[PHONE]', masked)
      print(f"进一步脱敏: {masked}")

      # ========== 5. 复杂日志解析 ==========
      print("\n=== 复杂日志解析 ===")

      error_log = """
      [2026-01-31 10:00:01] INFO: Service started
      [2026-01-31 10:05:23] ERROR: Database connection timeout (IP: 192.168.1.5)
      [2026-01-31 10:06:12] ERROR: Out of memory in worker process
      [2026-01-31 10:07:00] INFO: Health check passed
      """

      # 提取所有错误日志
      error_pattern = r'\[([^\]]+)\]\s+ERROR:\s+(.+)'
      errors = re.findall(error_pattern, error_log)

      print("发现的错误:")
      for timestamp, message in errors:
          print(f"  [{timestamp}] {message}")

      # 从错误信息中提取 IP 地址
      for timestamp, message in errors:
          ip_match = re.search(r'\d+\.\d+\.\d+\.\d+', message)
          if ip_match:
              print(f"  错误 IP: {ip_match.group()}")
      ```
    </CodeGroup>

    **正则表达式详解**：

    * `\d+`：一个或多个数字
    * `\.`：转义的点号（匹配字面量 `.`）
    * `\w+`：一个或多个单词字符
    * `.*?`：非贪婪匹配任意字符
    * `()`：捕获组，用于提取匹配内容
    * `\s`：空白字符

    **运行脚本**：

    ```bash theme={null}
    python 10_regex_demo.py
    ```

    **验证步骤**：

    1. 脚本能正常运行，无语法错误
    2. 观察匹配结果是否正确
    3. 确认捕获组提取的内容正确
    4. 确认文本替换功能正常

    **常见错误**：

    <Warning>
      * ❌ `re.error: bad escape` - 正则表达式转义错误，检查反斜杠
      * ❌ `AttributeError: 'NoneType' object has no attribute 'group'` - 匹配失败返回 None，应先检查
      * ❌ 匹配结果不符合预期 - 检查正则模式是否正确，可以使用在线工具测试
    </Warning>
  </Step>
</Steps>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="lightbulb" size={26} color="#059669" /> 拓展任务 (30 mins)</span>

<CardGroup cols={2}>
  <Card title="挑战 1：pathlib 实践" icon="folder">
    **任务**：使用 `pathlib` 库列出当前目录下所有的 `.py` 文件。

    **提示**：

    ```python theme={null}
    from pathlib import Path
    # Path('.') 表示当前目录
    # .glob('*.py') 查找所有 .py 文件
    ```
  </Card>

  <Card title="挑战 2：日志分析器" icon="code">
    **任务**：编写一个日志分析器，从 Nginx 日志中统计：

    * 每个 IP 的访问次数
    * 每个状态码的出现次数
    * 最常访问的 URL

    **提示**：使用字典存储统计结果，正则表达式提取信息。
  </Card>
</CardGroup>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="file" size={26} color="#059669" /> 今日产出物</span>

* `10_json_demo.py` - JSON 处理示例
* `10_yaml_demo.py` - YAML 处理示例
* `10_regex_demo.py` - 正则表达式示例
* `config.json` - 生成的 JSON 配置文件
* `config.yaml` - 生成的 YAML 配置文件

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="github" size={26} color="#059669" /> 参考代码</span>

<CardGroup cols={2}>
  <Card title="查看参考代码" icon="code" href="https://github.com/akriamail/insightful-ops/tree/main/docs/mintlify/scripts/training/30daystudy/week-2/day-10">
    在 GitHub 查看完整的示例代码

    <br />

    <small>包含 JSON、YAML 和正则表达式示例</small>
  </Card>

  <Card title="在线运行" icon="play" href="https://replit.com">
    使用在线编辑器测试代码

    <br />

    <small>无需本地环境配置</small>
  </Card>
</CardGroup>

***

## 实际应用场景

<CardGroup cols={2}>
  <Card title="文件处理在运维中的应用" icon="server">
    * **配置管理**：读取和更新应用配置文件（JSON/YAML）
    * **数据导出**：将监控数据导出为 CSV 格式
    * **API 集成**：解析 API 返回的 JSON 响应
    * **日志分析**：从日志文件中提取关键信息
  </Card>

  <Card title="正则表达式在运维中的应用" icon="code">
    * **日志解析**：从日志中提取 IP、时间、错误信息
    * **数据清洗**：清理和格式化数据
    * **文本替换**：批量替换配置文件中的内容
    * **模式匹配**：验证输入格式（如邮箱、IP 地址）
  </Card>
</CardGroup>

<Tip>
  **与 Day 11 的关联**：今天学习的文件处理和正则表达式，明天会学习如何用 Python 调用 Shell 命令（subprocess），将文件处理和系统命令结合起来。
</Tip>

***

<CardGroup cols={2}>
  <Card title="上一天: 数据结构进阶" href="/training/30daystudy/day-09">
    Day 09 | 数据结构与装饰器
  </Card>

  <Card title="下一天: 系统调用" href="/training/30daystudy/day-11">
    Day 11 | Python 调用 Shell
  </Card>
</CardGroup>
