前言:数据持久化的重要性

在前七篇文章中,我们学习了Python的基本语法、数据类型、控制结构以及常用的数据结构。现在,我们将探讨Python与外部世界交互的重要方式——输入输出(I/O文件读写。数据持久化是程序设计中不可或缺的一部分,它允许我们将数据保存到文件中,以便后续使用或与其他程序共享。

本文将深入探讨Python中的输入输出操作,包括标准输入输出、文件读写、异常处理以及常见文件格式的处理。通过丰富的实战案例,你将学会如何高效地处理各种I/O任务。


一、 标准输入输出:与用户交互的基础

Python提供了内置函数用于标准输入输出,这是最简单的与用户交互的方式。

1.1 标准输出:print()函数详解

print()函数是Python中最常用的输出函数,它可以将数据输出到标准输出设备(通常是屏幕)。

实战案例8-1:print()函数的高级用法

# 基本输出
print("Hello, World!")  # 输出字符串

# 输出多个对象,默认用空格分隔
print("Python", "is", "awesome")  # Python is awesome

# 修改分隔符
print("2023", "10", "15", sep="-")  # 2023-10-15

# 修改结束符(默认是换行)
print("Hello", end=" ")
print("World!")  # Hello World!

# 输出到文件
with open("output.txt", "w") as f:
    print("This goes to a file", file=f)

# 格式化输出
name = "Alice"
age = 25
# 旧式字符串格式化
print("Name: %s, Age: %d" % (name, age))
# str.format()方法
print("Name: {}, Age: {}".format(name, age))
# f-string (Python 3.6+)
print(f"Name: {name}, Age: {age}")

# 控制浮点数精度
pi = 3.1415926535
print(f"Pi: {pi:.2f}")  # Pi: 3.14

# 对齐文本
print(f"{'Left':<10}")   # 左对齐
print(f"{'Center':^10}") # 居中对齐
print(f"{'Right':>10}")  # 右对齐

1.2 标准输入:input()函数详解

input()函数用于从标准输入(通常是键盘)获取用户输入。

实战案例8-2:input()函数的应用

# 基本输入
name = input("请输入你的名字: ")
print(f"你好, {name}!")

# 输入数值(需要类型转换)
try:
    age = int(input("请输入你的年龄: "))
    print(f"明年你就{age + 1}岁了")
except ValueError:
    print("请输入有效的数字!")

# 输入多个值
data = input("请输入三个数字,用空格分隔: ")
numbers = data.split()  # 分割字符串
if len(numbers) == 3:
    try:
        a, b, c = map(float, numbers)
        print(f"总和: {a + b + c}")
    except ValueError:
        print("请输入有效的数字!")
else:
    print("请输入三个数字")

# 密码输入(不显示输入内容)
import getpass

password = getpass.getpass("请输入密码: ")
print(f"密码长度: {len(password)}")

# 简单的交互式菜单
while True:
    print("\n=== 菜单 ===")
    print("1. 选项一")
    print("2. 选项二")
    print("3. 退出")
    
    choice = input("请选择(1-3): ")
    
    if choice == "1":
        print("执行选项一")
    elif choice == "2":
        print("执行选项二")
    elif choice == "3":
        print("再见!")
        break
    else:
        print("无效选择,请重新输入")

二、 文件读写基础:与文件系统交互

Python提供了内置的open()函数用于文件操作,支持多种模式和编码方式。

2.1 文件打开模式

Python支持多种文件打开模式:

模式 描述
'r' 读取模式(默认)
'w' 写入模式(会覆盖现有文件)
'x' 独占创建模式(文件已存在则失败)
'a' 追加模式
'b' 二进制模式
't' 文本模式(默认)
'+' 更新模式(可读写)

实战案例8-3:文件基本操作

# 写入文件
with open("example.txt", "w", encoding="utf-8") as f:
    f.write("Hello, World!\n")
    f.write("这是第二行\n")
    f.write("这是第三行\n")

# 读取整个文件
with open("example.txt", "r", encoding="utf-8") as f:
    content = f.read()
    print("文件内容:")
    print(content)

# 逐行读取
with open("example.txt", "r", encoding="utf-8") as f:
    print("逐行读取:")
    for line in f:
        print(line, end="")  # line已经包含换行符

# 读取所有行到列表
with open("example.txt", "r", encoding="utf-8") as f:
    lines = f.readlines()
    print(f"总行数: {len(lines)}")
    for i, line in enumerate(lines, 1):
        print(f"第{i}行: {line.strip()}")

# 追加内容
with open("example.txt", "a", encoding="utf-8") as f:
    f.write("这是追加的内容\n")

# 读取追加后的内容
with open("example.txt", "r", encoding="utf-8") as f:
    print("追加后的内容:")
    print(f.read())

2.2 文件操作的最佳实践

使用with语句可以自动管理文件资源,确保文件正确关闭。

实战案例8-4:文件操作的最佳实践

# 不使用with语句(不推荐)
f = open("example.txt", "r")
try:
    content = f.read()
    print(content)
finally:
    f.close()  # 必须手动关闭

# 使用with语句(推荐)
with open("example.txt", "r") as f:
    content = f.read()
    print(content)
# 文件会自动关闭,即使发生异常

# 同时处理多个文件
with open("source.txt", "r") as source, open("destination.txt", "w") as dest:
    content = source.read()
    dest.write(content.upper())

# 处理二进制文件
with open("image.jpg", "rb") as f:
    data = f.read()
    print(f"文件大小: {len(data)} 字节")

# 复制二进制文件
with open("image.jpg", "rb") as source, open("copy.jpg", "wb") as dest:
    while True:
        chunk = source.read(1024)  # 每次读取1KB
        if not chunk:
            break
        dest.write(chunk)

# 处理大文件的高效方式
def process_large_file(filename):
    """处理大文件的示例"""
    with open(filename, "r", encoding="utf-8") as f:
        for line in f:
            # 逐行处理,避免内存不足
            process_line(line)  # 假设的处理函数

def process_line(line):
    """处理单行数据的示例函数"""
    # 这里可以进行各种处理,如数据清洗、分析等
    if line.strip():  # 跳过空行
        print(f"处理: {line.strip()}")

三、 文件路径处理:os和pathlib模块

Python提供了多种处理文件路径的方式,推荐使用现代的pathlib模块。

3.1 使用os.path模块

实战案例8-5:os.path模块的应用

import os

# 路径拼接
path = os.path.join("folder", "subfolder", "file.txt")
print(f"路径: {path}")

# 获取绝对路径
abs_path = os.path.abspath("example.txt")
print(f"绝对路径: {abs_path}")

# 检查路径是否存在
exists = os.path.exists("example.txt")
print(f"文件存在: {exists}")

# 检查是否是文件/目录
is_file = os.path.isfile("example.txt")
is_dir = os.path.isdir("example.txt")
print(f"是文件: {is_file}, 是目录: {is_dir}")

# 获取文件大小
size = os.path.getsize("example.txt")
print(f"文件大小: {size} 字节")

# 获取文件修改时间
mtime = os.path.getmtime("example.txt")
from datetime import datetime
print(f"修改时间: {datetime.fromtimestamp(mtime)}")

# 遍历目录
for item in os.listdir("."):
    item_path = os.path.join(".", item)
    if os.path.isfile(item_path):
        print(f"文件: {item}")
    elif os.path.isdir(item_path):
        print(f"目录: {item}")

# 创建和删除目录
if not os.path.exists("test_dir"):
    os.makedirs("test_dir")
    print("创建目录")

if os.path.exists("test_dir"):
    os.rmdir("test_dir")
    print("删除目录")

3.2 使用pathlib模块(Python 3.4+)

pathlib提供了面向对象的路径操作方式,更加直观和易用。

实战案例8-6:pathlib模块的应用

from pathlib import Path

# 创建Path对象
p = Path("example.txt")
print(f"路径对象: {p}")

# 获取文件信息
if p.exists():
    print(f"文件存在: {p.exists()}")
    print(f"是文件: {p.is_file()}")
    print(f"是目录: {p.is_dir()}")
    print(f"文件大小: {p.stat().st_size} 字节")
    print(f"修改时间: {datetime.fromtimestamp(p.stat().st_mtime)}")

# 路径操作
p = Path("folder") / "subfolder" / "file.txt"  # 路径拼接
print(f"拼接后的路径: {p}")

print(f"父目录: {p.parent}")
print(f"文件名: {p.name}")
print(f"stem(无后缀): {p.stem}")
print(f"后缀: {p.suffix}")
print(f"所有后缀: {p.suffixes}")

# 文件操作
p = Path("test.txt")
p.write_text("Hello, Pathlib!", encoding="utf-8")  # 写入文件
content = p.read_text(encoding="utf-8")  # 读取文件
print(f"文件内容: {content}")

# 目录操作
dir_path = Path("test_directory")
dir_path.mkdir(exist_ok=True)  # 创建目录,exist_ok=True表示如果已存在也不报错

# 遍历目录
for item in Path(".").iterdir():
    if item.is_file():
        print(f"文件: {item.name}")
    elif item.is_dir():
        print(f"目录: {item.name}")

# 使用通配符查找文件
for py_file in Path(".").glob("*.py"):
    print(f"Python文件: {py_file}")

# 递归查找
for py_file in Path(".").rglob("*.py"):
    print(f"Python文件(递归): {py_file}")

# 删除文件
if p.exists():
    p.unlink()  # 删除文件

if dir_path.exists():
    dir_path.rmdir()  # 删除空目录

四、 文件读写异常处理

文件操作可能会遇到各种异常,如文件不存在、权限不足等,需要进行适当的异常处理。

实战案例8-7:文件操作异常处理

# 基本的异常处理
try:
    with open("nonexistent.txt", "r") as f:
        content = f.read()
except FileNotFoundError:
    print("文件不存在!")
except IOError as e:
    print(f"IO错误: {e}")
except Exception as e:
    print(f"其他错误: {e}")

# 更完整的异常处理
def safe_file_operation(filename, mode="r", content=None):
    """安全的文件操作函数"""
    try:
        if mode.startswith("r"):
            with open(filename, mode, encoding="utf-8") as f:
                return f.read()
        elif mode.startswith("w") or mode.startswith("a"):
            with open(filename, mode, encoding="utf-8") as f:
                if content:
                    f.write(content)
                return True
    except FileNotFoundError:
        print(f"错误: 文件 '{filename}' 不存在")
    except PermissionError:
        print(f"错误: 没有权限访问文件 '{filename}'")
    except IsADirectoryError:
        print(f"错误: '{filename}' 是一个目录")
    except UnicodeDecodeError:
        print(f"错误: 文件编码问题 '{filename}'")
    except Exception as e:
        print(f"未知错误: {e}")
    return False

# 测试异常处理
result = safe_file_operation("nonexistent.txt")
result = safe_file_operation("/root/protected.txt")  # 可能需要root权限
result = safe_file_operation("example.txt", "w", "测试内容")

# 检查文件是否可读写
def check_file_access(filename):
    """检查文件访问权限"""
    checks = {
        "存在": os.access(filename, os.F_OK),
        "可读": os.access(filename, os.R_OK),
        "可写": os.access(filename, os.W_OK),
        "可执行": os.access(filename, os.X_OK)
    }
    
    print(f"文件 '{filename}' 的权限:")
    for permission, result in checks.items():
        print(f"  {permission}: {'是' if result else '否'}")
    
    return checks

check_file_access("example.txt")

五、 常见文件格式处理

在实际应用中,我们经常需要处理各种格式的文件,如JSON、CSV等。Python提供了丰富的库来处理这些格式。

5.1 JSON文件处理

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,Python提供了json模块来处理JSON数据。

实战案例8-8:JSON文件操作

import json

# Python对象转换为JSON字符串
data = {
    "name": "Alice",
    "age": 25,
    "is_student": False,
    "hobbies": ["reading", "swimming", "coding"],
    "address": {
        "street": "123 Main St",
        "city": "New York"
    }
}

# 将Python对象转换为JSON字符串
json_string = json.dumps(data, indent=2, ensure_ascii=False)
print("JSON字符串:")
print(json_string)

# 将JSON字符串保存到文件
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=2, ensure_ascii=False)

# 从JSON字符串加载Python对象
loaded_data = json.loads(json_string)
print("从JSON字符串加载的数据:")
print(loaded_data)

# 从JSON文件加载Python对象
with open("data.json", "r", encoding="utf-8") as f:
    loaded_from_file = json.load(f)
    print("从文件加载的数据:")
    print(loaded_from_file)

# 处理日期对象
from datetime import datetime

class Person:
    def __init__(self, name, birthdate):
        self.name = name
        self.birthdate = birthdate

# 自定义JSON编码器
class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        elif isinstance(obj, Person):
            return {"name": obj.name, "birthdate": obj.birthdate.isoformat()}
        return super().default(obj)

# 使用自定义编码器
person = Person("Bob", datetime(1990, 5, 15))
person_json = json.dumps(person, cls=CustomEncoder, ensure_ascii=False)
print("自定义编码的JSON:")
print(person_json)

# JSON解码器
def custom_decoder(dct):
    if "birthdate" in dct and "name" in dct:
        try:
            birthdate = datetime.fromisoformat(dct["birthdate"])
            return Person(dct["name"], birthdate)
        except ValueError:
            pass
    return dct

# 使用自定义解码器
decoded_person = json.loads(person_json, object_hook=custom_decoder)
print(f"解码后: {decoded_person.name}, 生日: {decoded_person.birthdate}")

5.2 CSV文件处理

CSV(Comma-Separated Values)是一种常用的表格数据存储格式,Python提供了csv模块来处理CSV文件。

实战案例8-9:CSV文件操作

import csv

# 写入CSV文件
data = [
    ["Name", "Age", "City"],
    ["Alice", 25, "New York"],
    ["Bob", 30, "London"],
    ["Charlie", 35, "Tokyo"]
]

with open("people.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerows(data)

# 读取CSV文件
with open("people.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f)
    print("CSV文件内容:")
    for row in reader:
        print(row)

# 使用字典方式读写CSV(推荐)
people = [
    {"Name": "Alice", "Age": 25, "City": "New York"},
    {"Name": "Bob", "Age": 30, "City": "London"},
    {"Name": "Charlie", "Age": 35, "City": "Tokyo"}
]

# 写入CSV(字典方式)
with open("people_dict.csv", "w", newline="", encoding="utf-8") as f:
    fieldnames = ["Name", "Age", "City"]
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    
    writer.writeheader()  # 写入表头
    writer.writerows(people)

# 读取CSV(字典方式)
with open("people_dict.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    print("字典方式读取CSV:")
    for row in reader:
        print(f"{row['Name']} - {row['Age']} - {row['City']}")

# 处理包含特殊字符的CSV
data_with_special = [
    ["Name", "Description"],
    ["Alice", "Loves reading, swimming, and coding"],
    ["Bob", "Works in IT; likes hiking"]
]

with open("special.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f, quoting=csv.QUOTE_ALL)  # 引用所有字段
    writer.writerows(data_with_special)

# 读取带引号的CSV
with open("special.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

# CSV方言(Dialect)注册
csv.register_dialect('my_dialect', delimiter='|', quoting=csv.QUOTE_MINIMAL)

with open("custom_dialect.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f, dialect='my_dialect')
    writer.writerows(data)

# 读取自定义方言的CSV
with open("custom_dialect.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f, dialect='my_dialect')
    print("自定义方言CSV:")
    for row in reader:
        print(row)

5.3 其他格式文件处理

实战案例8-10:其他文件格式处理

# 配置文件处理(ini格式)
import configparser

config = configparser.ConfigParser()
config['DEFAULT'] = {
    'debug': 'True',
    'log_level': 'INFO',
    'max_connections': '100'
}
config['database'] = {
    'host': 'localhost',
    'port': '5432',
    'name': 'mydb'
}
config['server'] = {
    'host': '0.0.0.0',
    'port': '8000'
}

# 写入配置文件
with open('config.ini', 'w') as f:
    config.write(f)

# 读取配置文件
config_read = configparser.ConfigParser()
config_read.read('config.ini')

print("配置文件内容:")
print(f"Debug模式: {config_read['DEFAULT']['debug']}")
print(f"数据库主机: {config_read['database']['host']}")
print(f"服务器端口: {config_read['server']['port']}")

# XML文件处理
import xml.etree.ElementTree as ET

# 创建XML数据
root = ET.Element("catalog")
for i in range(3):
    book = ET.SubElement(root, "book", id=str(i+1))
    title = ET.SubElement(book, "title")
    title.text = f"Book {i+1}"
    author = ET.SubElement(book, "author")
    author.text = f"Author {i+1}"
    price = ET.SubElement(book, "price")
    price.text = str(19.99 + i)

# 写入XML文件
tree = ET.ElementTree(root)
tree.write("books.xml", encoding="utf-8", xml_declaration=True)

# 读取XML文件
tree_read = ET.parse("books.xml")
root_read = tree_read.getroot()

print("XML文件内容:")
for book in root_read.findall("book"):
    book_id = book.get("id")
    title = book.find("title").text
    author = book.find("author").text
    price = book.find("price").text
    print(f"ID: {book_id}, 标题: {title}, 作者: {author}, 价格: {price}")

六、 序列化与反序列化

序列化是将对象转换为可存储或传输的格式,反序列化则是将序列化后的数据恢复为对象。

实战案例8-11:序列化与反序列化

import pickle
import shelve

# 使用pickle进行序列化
data = {
    "name": "Alice",
    "age": 25,
    "hobbies": ["reading", "swimming"]
}

# 序列化到文件
with open("data.pkl", "wb") as f:
    pickle.dump(data, f)

# 从文件反序列化
with open("data.pkl", "rb") as f:
    loaded_data = pickle.load(f)
    print("Pickle加载的数据:")
    print(loaded_data)

# 序列化到字节串
data_bytes = pickle.dumps(data)
print(f"序列化后的字节串: {data_bytes[:50]}...")  # 显示前50个字节

# 从字节串反序列化
loaded_from_bytes = pickle.loads(data_bytes)
print("从字节串加载的数据:")
print(loaded_from_bytes)

# 使用shelve进行持久化(基于pickle的键值存储)
with shelve.open("mydata") as db:
    # 存储数据
    db["person1"] = {"name": "Alice", "age": 25}
    db["person2"] = {"name": "Bob", "age": 30}
    db["settings"] = {"theme": "dark", "language": "en"}
    
    # 读取数据
    print("Shelve存储的数据:")
    for key in db.keys():
        print(f"{key}: {db[key]}")
    
    # 修改数据
    db["person1"]["age"] = 26  # 注意:需要重新存储整个对象
    
    # 正确修改方式
    person1 = db["person1"]
    person1["age"] = 26
    db["person1"] = person1

# 自定义序列化类
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email
        self.created_at = datetime.now()
    
    def __repr__(self):
        return f"User(name={self.name}, email={self.email}, created_at={self.created_at})"

# 序列化自定义对象
user = User("Alice", "alice@example.com")
with open("user.pkl", "wb") as f:
    pickle.dump(user, f)

# 反序列化自定义对象
with open("user.pkl", "rb") as f:
    loaded_user = pickle.load(f)
    print("加载的用户对象:")
    print(loaded_user)

七、 综合实战:日志分析器

现在,让我们创建一个综合性的日志分析器,应用本章所学的文件读写和数据处理知识。

项目目标:

  • 读取日志文件
  • 分析日志数据(错误统计、访问统计等)
  • 生成分析报告
  • 支持多种日志格式
  • 提供交互式界面

代码实现:

def log_analyzer_tool():
    """日志分析工具"""
    log_data = []
    
    while True:
        print("\n=== 日志分析工具 ===")
        print("1. 加载日志文件")
        print("2. 查看日志统计")
        print("3. 分析错误日志")
        print("4. 分析访问模式")
        print("5. 生成报告")
        print("6. 退出")
        
        choice = input("请选择操作(1-6): ")
        
        if choice == "1":
            log_data = load_log_file()
        elif choice == "2":
            show_log_statistics(log_data)
        elif choice == "3":
            analyze_errors(log_data)
        elif choice == "4":
            analyze_access_patterns(log_data)
        elif choice == "5":
            generate_report(log_data)
        elif choice == "6":
            print("感谢使用日志分析工具,再见!")
            break
        else:
            print("无效选择,请重新输入")

def load_log_file():
    """加载日志文件"""
    filename = input("请输入日志文件路径: ")
    
    if not os.path.exists(filename):
        print("文件不存在!")
        return []
    
    log_data = []
    try:
        with open(filename, "r", encoding="utf-8") as f:
            for line in f:
                log_data.append(line.strip())
        
        print(f"成功加载 {len(log_data)} 行日志")
        return log_data
    except Exception as e:
        print(f"加载文件时出错: {e}")
        return []

def show_log_statistics(log_data):
    """显示日志统计信息"""
    if not log_data:
        print("请先加载日志文件")
        return
    
    print(f"总日志行数: {len(log_data)}")
    
    # 统计不同日志级别的数量
    level_stats = {}
    for line in log_data:
        # 简单的级别检测(实际应用中需要更复杂的解析)
        if "ERROR" in line:
            level = "ERROR"
        elif "WARN" in line:
            level = "WARN"
        elif "INFO" in line:
            level = "INFO"
        elif "DEBUG" in line:
            level = "DEBUG"
        else:
            level = "UNKNOWN"
        
        level_stats[level] = level_stats.get(level, 0) + 1
    
    print("\n日志级别统计:")
    for level, count in level_stats.items():
        print(f"{level}: {count} 行")
    
    # 时间范围分析
    timestamps = []
    for line in log_data:
        # 提取时间戳(简单示例)
        if len(line) > 20 and line[0].isdigit():
            timestamp = line[:19]  # 假设时间戳在前19个字符
            timestamps.append(timestamp)
    
    if timestamps:
        print(f"\n时间范围: {timestamps[0]} - {timestamps[-1]}")
    else:
        print("\n未找到时间戳信息")

def analyze_errors(log_data):
    """分析错误日志"""
    if not log_data:
        print("请先加载日志文件")
        return
    
    error_lines = [line for line in log_data if "ERROR" in line]
    print(f"找到 {len(error_lines)} 条错误日志")
    
    if error_lines:
        print("\n最近5条错误日志:")
        for line in error_lines[-5:]:
            print(f"- {line}")
        
        # 错误类型统计
        error_types = {}
        for line in error_lines:
            # 简单的错误类型提取
            if "Timeout" in line:
                error_type = "Timeout"
            elif "Connection" in line:
                error_type = "Connection"
            elif "Memory" in line:
                error_type = "Memory"
            elif "File" in line:
                error_type = "File"
            else:
                error_type = "Other"
            
            error_types[error_type] = error_types.get(error_type, 0) + 1
        
        print("\n错误类型统计:")
        for error_type, count in error_types.items():
            print(f"{error_type}: {count} 次")

def analyze_access_patterns(log_data):
    """分析访问模式"""
    if not log_data:
        print("请先加载日志文件")
        return
    
    # 提取IP地址(简单示例)
    ip_pattern = r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
    import re
    
    ip_addresses = []
    for line in log_data:
        match = re.search(ip_pattern, line)
        if match:
            ip_addresses.append(match.group())
    
    if ip_addresses:
        from collections import Counter
        ip_counter = Counter(ip_addresses)
        
        print(f"找到 {len(ip_addresses)} 个IP地址,来自 {len(ip_counter)} 个唯一IP")
        
        print("\n访问最频繁的5个IP:")
        for ip, count in ip_counter.most_common(5):
            print(f"{ip}: {count} 次访问")
    else:
        print("未找到IP地址信息")

def generate_report(log_data):
    """生成分析报告"""
    if not log_data:
        print("请先加载日志文件")
        return
    
    report_filename = "log_analysis_report.txt"
    
    with open(report_filename, "w", encoding="utf-8") as f:
        f.write("日志分析报告\n")
        f.write("=" * 50 + "\n\n")
        
        f.write(f"分析时间: {datetime.now()}\n")
        f.write(f"总日志行数: {len(log_data)}\n\n")
        
        # 错误统计
        error_lines = [line for line in log_data if "ERROR" in line]
        f.write(f"错误日志数量: {len(error_lines)}\n\n")
        
        # 写入部分错误日志
        if error_lines:
            f.write("最近错误日志:\n")
            for line in error_lines[-10:]:
                f.write(f"- {line}\n")
        
        f.write("\n报告生成完成!\n")
    
    print(f"报告已生成到 {report_filename}")

# 运行工具
if __name__ == "__main__":
    log_analyzer_tool()

运行示例:

=== 日志分析工具 ===
1. 加载日志文件
2. 查看日志统计
3. 分析错误日志
4. 分析访问模式
5. 生成报告
6. 退出
请选择操作(1-6): 1
请输入日志文件路径: server.log
成功加载 1520 行日志

=== 日志分析工具 ===
1. 加载日志文件
2. 查看日志统计
3. 分析错误日志
4. 分析访问模式
5. 生成报告
6. 退出
请选择操作(1-6): 2
总日志行数: 1520

日志级别统计:
INFO: 1200 行
ERROR: 25 行
WARN: 295 行
UNKNOWN: 0 行

时间范围: 2023-10-01 08:00:01 - 2023-10-01 12:30:45

=== 日志分析工具 ===
1. 加载日志文件
2. 查看日志统计
3. 分析错误日志
4. 分析访问模式
5. 生成报告
6. 退出
请选择操作(1-6): 5
报告已生成到 log_analysis_report.txt

=== 日志分析工具 ===
1. 加载日志文件
2. 查看日志统计
3. 分析错误日志
4. 分析访问模式
5. 生成报告
6. 退出
请选择操作(1-6): 6
感谢使用日志分析工具,再见!

总结与展望

通过本篇文章的深入学习,我们全面掌握了Python中的输入输出和文件读写:

  1. 标准输入输出

    • 掌握了print()函数的高级用法和格式化输出
    • 学会了使用input()函数获取用户输入并进行验证
  2. 文件操作

    • 掌握了文件的基本读写操作和各种打开模式
    • 学会了使用with语句进行资源管理
    • 理解了文件操作的异常处理机制
  3. 路径处理

    • 学会了使用os.path模块进行路径操作
    • 掌握了现代的pathlib模块的面向对象路径操作
  4. 文件格式处理

    • 学会了JSON文件的读写和自定义编码解码
    • 掌握了CSV文件的读写和高级处理技巧
    • 了解了其他格式文件(如INI、XML)的处理方法
  5. 序列化与反序列化

    • 掌握了使用pickle进行对象序列化
    • 学会了使用shelve进行持久化存储
  6. 综合应用

    • 通过日志分析器项目,将所学知识融会贯通
    • 学会了如何处理和分析真实世界的数据文件
    • 实现了完整的文件处理和分析功能

文件读写是Python编程中不可或缺的一部分,几乎所有的应用程序都需要与文件系统交互。掌握好这些技能,对于开发实际应用程序至关重要。

思考题:

  1. 在处理大文件时,为什么要使用逐行读取而不是一次性读取整个文件?
  2. JSON和CSV格式各有什么优缺点?在什么情况下应该选择哪种格式?
  3. 为什么推荐使用pathlib而不是os.path进行路径操作?
  4. 在异常处理中,为什么应该尽可能指定具体的异常类型而不是捕获所有异常?
  5. 在日志分析器的基础上,还可以添加哪些分析功能来增强其实用性?

更多推荐