# Python 文件读写

## 学习目标
- 掌握文件的打开、读取和写入操作
- 理解不同的文件模式
- 学会处理文本文件和二进制文件
- 掌握文件编码和常见文件格式处理

---

## 1. 打开文件

### 1.1 open() 函数

```python
# 基本语法
# open(file, mode='r', encoding=None, ...)

# 读取文本文件
f = open('data.txt', 'r', encoding='utf-8')
content = f.read()
f.close()

# 推荐：使用 with 语句（自动关闭）
with open('data.txt', 'r', encoding='utf-8') as f:
    content = f.read()
# 文件已自动关闭
```

### 1.2 文件模式

| 模式 | 说明 |
|------|------|
| `'r'` | 读取（默认） |
| `'w'` | 写入，会覆盖原有内容 |
| `'x'` | 独占创建，文件已存在则报错 |
| `'a'` | 追加写入 |
| `'b'` | 二进制模式 |
| `'t'` | 文本模式（默认） |
| `'+'` | 读写模式 |

```python
# 常用模式组合
'r'   # 只读文本
'w'   # 只写文本（覆盖）
'a'   # 追加文本
'rb'  # 只读二进制
'wb'  # 只写二进制
'r+'  # 读写（文件必须存在）
'w+'  # 读写（覆盖或创建）
```

---

## 2. 读取文件

### 2.1 读取整个文件

```python
with open('example.txt', 'r', encoding='utf-8') as f:
    content = f.read()  # 读取全部内容
    print(content)
```

### 2.2 逐行读取

```python
# 方法1：readlines()
with open('example.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()  # 返回列表
    for line in lines:
        print(line.strip())

# 方法2：直接迭代（推荐，内存友好）
with open('example.txt', 'r', encoding='utf-8') as f:
    for line in f:
        print(line.strip())

# 方法3：readline() 逐行
with open('example.txt', 'r', encoding='utf-8') as f:
    while True:
        line = f.readline()
        if not line:
            break
        print(line.strip())
```

### 2.3 指定读取大小

```python
with open('example.txt', 'r', encoding='utf-8') as f:
    # 读取前100个字符
    chunk = f.read(100)
    print(f"前100个字符: {chunk}")
    
    # 继续读取
    chunk2 = f.read(100)
    print(f"接下来100个字符: {chunk2}")
```

---

## 3. 写入文件

### 3.1 写入文本

```python
# 覆盖写入
with open('output.txt', 'w', encoding='utf-8') as f:
    f.write('第一行\n')
    f.write('第二行\n')

# 追加写入
with open('output.txt', 'a', encoding='utf-8') as f:
    f.write('第三行（追加）\n')

# 写入多行
lines = ['行1\n', '行2\n', '行3\n']
with open('output.txt', 'w', encoding='utf-8') as f:
    f.writelines(lines)
```

### 3.2 print 重定向到文件

```python
with open('log.txt', 'w', encoding='utf-8') as f:
    print('日志信息1', file=f)
    print('日志信息2', file=f)
    print('带变量:', 42, file=f)
```

---

## 4. 二进制文件操作

### 4.1 读写二进制

```python
# 写入二进制数据
data = bytes([0, 1, 2, 255])
with open('binary.bin', 'wb') as f:
    f.write(data)

# 读取二进制数据
with open('binary.bin', 'rb') as f:
    data = f.read()
    print(data)  # b'\x00\x01\x02\xff'
    print(list(data))  # [0, 1, 2, 255]
```

### 4.2 复制文件

```python
def copy_file(src, dst):
    """复制文件"""
    with open(src, 'rb') as f_src:
        with open(dst, 'wb') as f_dst:
            while True:
                chunk = f_src.read(4096)  # 分块读取
                if not chunk:
                    break
                f_dst.write(chunk)

# 使用
copy_file('source.jpg', 'backup.jpg')
```

---

## 5. 文件指针操作

```python
with open('data.txt', 'r+', encoding='utf-8') as f:
    # 读取前10个字符
    print(f.read(10))
    
    # 获取当前位置
    print(f"当前位置: {f.tell()}")
    
    # 移动到文件开头
    f.seek(0)
    print(f"回到开头后: {f.read(5)}")
    
    # seek(offset, whence)
    # whence: 0=开头(默认), 1=当前位置, 2=文件末尾
    f.seek(0, 2)  # 移动到末尾
    print(f"文件大小: {f.tell()}")
```

---

## 6. 编码处理

### 6.1 指定编码

```python
# UTF-8（推荐）
with open('utf8.txt', 'w', encoding='utf-8') as f:
    f.write('中文内容')

# GBK
with open('gbk.txt', 'w', encoding='gbk') as f:
    f.write('中文内容')

# 读取时指定编码
with open('gbk.txt', 'r', encoding='gbk') as f:
    print(f.read())
```

### 6.2 自动检测编码

```python
# 使用 chardet 库检测编码（需安装: pip install chardet）
# import chardet
# 
# with open('unknown.txt', 'rb') as f:
#     result = chardet.detect(f.read())
#     print(result)  # {'encoding': 'UTF-8', 'confidence': 0.99}
```

### 6.3 处理编码错误

```python
# 忽略错误字符
with open('file.txt', 'r', encoding='utf-8', errors='ignore') as f:
    content = f.read()

# 用替换字符
with open('file.txt', 'r', encoding='utf-8', errors='replace') as f:
    content = f.read()

# 严格模式（默认，遇到错误抛出异常）
with open('file.txt', 'r', encoding='utf-8', errors='strict') as f:
    content = f.read()
```

---

## 7. 常见文件格式处理

### 7.1 CSV 文件

```python
import csv

# 写入 CSV
with open('data.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['姓名', '年龄', '城市'])
    writer.writerow(['Alice', 25, '北京'])
    writer.writerow(['Bob', 30, '上海'])

# 读取 CSV
with open('data.csv', 'r', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

# 使用 DictReader
with open('data.csv', 'r', encoding='utf-8') as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(f"{row['姓名']}: {row['年龄']}岁")
```

### 7.2 JSON 文件

```python
import json

data = {
    "name": "Alice",
    "age": 25,
    "hobbies": ["读书", "游泳"]
}

# 写入 JSON
with open('data.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# 读取 JSON
with open('data.json', 'r', encoding='utf-8') as f:
    loaded = json.load(f)
    print(loaded)

# JSON 字符串
json_str = json.dumps(data, ensure_ascii=False)
print(json_str)

parsed = json.loads(json_str)
print(parsed)
```

### 7.3 临时文件

```python
import tempfile

# 创建临时文件
with tempfile.TemporaryFile(mode='w+', encoding='utf-8') as f:
    f.write('临时数据')
    f.seek(0)
    print(f.read())
# 文件自动删除

# 创建有名临时文件
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f:
    f.write('有名临时文件')
    print(f"临时文件路径: {f.name}")
# 需要手动删除

# 临时目录
with tempfile.TemporaryDirectory() as tmpdir:
    print(f"临时目录: {tmpdir}")
    # 在目录中创建文件...
# 目录自动删除
```

---

## 8. 文件和目录操作

```python
import os
import shutil

# 文件操作
os.rename('old.txt', 'new.txt')       # 重命名
os.remove('file.txt')                 # 删除文件
shutil.copy('src.txt', 'dst.txt')     # 复制文件
shutil.move('src.txt', 'dst/')        # 移动文件

# 检查文件
print(os.path.exists('file.txt'))     # 是否存在
print(os.path.isfile('file.txt'))     # 是否是文件
print(os.path.getsize('file.txt'))    # 文件大小

# 路径操作
print(os.path.join('folder', 'file.txt'))  # folder/file.txt
print(os.path.abspath('file.txt'))         # 绝对路径
print(os.path.dirname('/a/b/c.txt'))       # /a/b
print(os.path.basename('/a/b/c.txt'))      # c.txt
print(os.path.splitext('file.txt'))        # ('file', '.txt')
```

---

## 本节小结

- **打开文件**：使用 `with` 语句自动管理资源
- **文件模式**：`r/w/a/x` 配合 `b/t/+` 满足各种需求
- **读取方式**：`read()`、`readline()`、迭代文件对象
- **编码处理**：始终显式指定 `encoding='utf-8'`
- **常见格式**：`csv`、`json` 使用标准库处理
- **临时文件**：`tempfile` 模块安全创建临时资源

---

## 练习

1. 编写一个函数，统计文本文件的行数、单词数和字符数
2. 实现一个配置文件解析器，支持 JSON 和 CSV 格式
3. 编写程序合并多个文本文件，并去除重复行
4. 创建一个日志文件处理器，自动按日期分割日志文件

