查看全课大纲(第 8 / 52 节)
- 1.量化分析基本概念:从咖啡馆到华尔街
- 2.量化交易员学习路线:从零基础到策略开发
- 3.量化策略生命周期:数据分析的五大流程
- 4.量化数据类型:数值型与类别型数据
- 5.量化沙盒搭建(1):Python与Anaconda安装
- 6.量化沙盒搭建(2):Jupyter Notebook与库配置
- 7.本地多源数据:量化历史回测的基石
- 8.TXT数据流操作:处理金融交易日志
- 9.JSON数据流操作:解析股票行情API响应
- 10.CSV行情文件操作:读取与存储历史K线
- 11.Excel报表操作:清洗财报与财务指标
- 12.量化数据库:SQL常用查询语法
- 13.量化数据库:Python连接与写入
- 14.量化多表连接:关联股票信息与行情
- 15.实战:小象咖啡店营业数据与个股财务指标的多源数据分析
- 16.量化数据采集:爬虫工作原理与合规
- 17.量化爬虫核心:URL管理与去重策略
- 18.量化网页下载:防反爬与请求头伪装
- 19.网页解析(1):使用XPath定位财经新闻
- 20.网页解析(2):使用CSS选择器与ItemLoader
- 21.Scrapy框架:构建工业级量化爬虫
- 22.Scrapy实战(1):新建项目与数据模型定义
- 23.Scrapy实战(2):编写财经爬虫逻辑
- 24.Scrapy实战(3):启用数据管道清洗行情
- 25.Scrapy实战(4):防反爬中间件配置
- 26.实战:构建每日热门个股行情自动采集系统
- 27.NumPy与SciPy:加速量化矩阵计算
- 28.NumPy数组:构建股票价格矩阵
- 29.数组切片:提取特定股票与交易日数据
- 30.NumPy常用方法:计算均值与波动率
- 31.向量化计算:摆脱缓慢的For循环
- 32.通用函数ufunc:计算对数收益率与累计收益
- 33.实战:股票组合资产收益率与协方差矩阵计算
- 34.Pandas Series:管理单只股票收盘价
- 35.Pandas DataFrame:构建多只股票行情面板
- 36.Pandas Index:时间序列索引与对齐
- 37.DataFrame索引操作:loc与iloc定位行情
- 38.索引总结:量化多字段筛选与定位
- 39.运算与对齐:合并不同交易日的数据
- 40.Pandas map:对股票代码与状态做映射
- 41.Pandas apply:滚动计算技术指标
- 42.排序操作:按市盈率与收益率对股票排名
- 43.数据清洗:填充与剔除停牌股票缺失值
- 44.数据清洗:剔除重复报价与异常交易数据
- 45.常用统计:量化最大回撤与分位数风险评估
- 46.极值定位:寻找历史最高价与最低价的发生日
- 47.风险与累计收益:计算年化波动率与累计回报
- 48.实战:小象咖啡店营业数据与热门个股时间序列清洗分析
- 49.Series索引操作:按位置与标签访问行情数据
- 50.文件读写操作:CSV行情数据导入导出
- 51.数据清洗:替换异常报价数据
- 52.常用统计:sum/mean/median/count聚合指标
TXT数据流操作:处理金融交易日志
约 12 分钟
小象实战讲义 · 量化交易Python入门:从咖啡馆到华尔街
在量化交易中,历史回测数据往往以多种异构本地文件(TXT、CSV、JSON、Excel)和关系数据库形式存在。本章你将编写数据接口,读取小象咖啡的门店营收数据并解析股票行情API响应,打通量化回测的数据通道!
💡 核心导读
TXT文件是最基础的文本存储格式,掌握其操作是数据采集与处理的必备技能。本章将系统讲解Python处理TXT文件的5大核心操作:文件读取、写入、追加、行级处理以及编码转换。通过实战演练,您将学会使用open()函数的不同模式、with语句管理资源、处理多编码文本等实用技巧,并掌握大文件的分块读取策略。
基础读写操作
文件读取三模式
import os
# 创建示例数据文件
sample_data = """2024-01-02 09:30:00 [INFO] 系统启动
2024-01-02 09:31:15 [INFO] 用户登录成功
2024-01-02 09:32:45 [WARN] 内存使用率超过80%
2024-01-02 09:35:20 [ERROR] 数据库连接失败
2024-01-02 09:36:10 [INFO] 重连数据库成功
"""
with open('data.txt', 'w', encoding='utf-8') as f:
f.write(sample_data)
# 块1:三种读取方式
print("=== 读取整个文件 ===")
with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)
print("\n=== 逐行迭代读取 ===")
with open('data.txt', 'r', encoding='utf-8') as f:
for line in f:
print(line.strip())
print("\n=== 读取为列表 ===")
with open('data.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()}")
# 清理临时文件
os.remove('data.txt')=== 读取整个文件 ===
2024-01-02 09:30:00 [INFO] 系统启动
2024-01-02 09:31:15 [INFO] 用户登录成功
2024-01-02 09:32:45 [WARN] 内存使用率超过80%
2024-01-02 09:35:20 [ERROR] 数据库连接失败
2024-01-02 09:36:10 [INFO] 重连数据库成功
=== 逐行迭代读取 ===
2024-01-02 09:30:00 [INFO] 系统启动
2024-01-02 09:31:15 [INFO] 用户登录成功
2024-01-02 09:32:45 [WARN] 内存使用率超过80%
2024-01-02 09:35:20 [ERROR] 数据库连接失败
2024-01-02 09:36:10 [INFO] 重连数据库成功
=== 读取为列表 ===
共读取 5 行
第1行: 2024-01-02 09:30:00 [INFO] 系统启动
第2行: 2024-01-02 09:31:15 [INFO] 用户登录成功
第3行: 2024-01-02 09:32:45 [WARN] 内存使用率超过80%
第4行: 2024-01-02 09:35:20 [ERROR] 数据库连接失败
第5行: 2024-01-02 09:36:10 [INFO] 重连数据库成功文件写入与追加
# 块2:写入和追加文件
print("=== 写入模式(覆盖) ===")
with open('output.txt', 'w', encoding='utf-8') as f:
f.write("第一行内容\n")
f.writelines(["第二行\n", "第三行\n"])
with open('output.txt', 'r', encoding='utf-8') as f:
print("写入后内容:")
print(f.read())
print("\n=== 追加模式 ===")
with open('output.txt', 'a', encoding='utf-8') as f:
f.write("新增内容\n")
with open('output.txt', 'r', encoding='utf-8') as f:
print("追加后内容:")
print(f.read())
# 清理临时文件
import os
os.remove('output.txt')=== 写入模式(覆盖) ===
写入后内容:
第一行内容
第二行
第三行
=== 追加模式 ===
追加后内容:
第一行内容
第二行
第三行
新增内容高级处理技巧
编码检测与转换
# 块3:演示编码检测与转换(使用标准库)
import codecs
# 创建GBK编码的示例数据(在内存中)
gbk_content = "股票代码,名称,价格\n000001,平安银行,15.23\n600519,贵州茅台,1750.50"
gbk_bytes = gbk_content.encode('gbk')
# 模拟检测编码(实际环境中可能使用chardet,这里演示标准库方法)
# 假设我们已知或通过其他方式确定编码为GBK
src_encoding = 'gbk'
print(f"假设检测到的编码: {src_encoding}")
# 使用codecs解码
content = codecs.decode(gbk_bytes, src_encoding)
print(f"读取的内容:\n{content}")
# 转换为UTF-8(在内存中)
utf8_bytes = content.encode('utf-8')
print("\n转换后验证(解码UTF-8):")
print(codecs.decode(utf8_bytes, 'utf-8'))假设检测到的编码: gbk
读取的内容:
股票代码,名称,价格
000001,平安银行,15.23
600519,贵州茅台,1750.50
转换后验证(解码UTF-8):
股票代码,名称,价格
000001,平安银行,15.23
600519,贵州茅台,1750.50大文件分块处理
# 创建大文件示例数据
large_content = "交易日志数据\n" * 1000 + "结束标记"
with open('big_data.txt', 'w', encoding='utf-8') as f:
f.write(large_content)
def process_large_file(file_path, chunk_size=1024):
"""处理大文件的生成器函数"""
with open(file_path, 'r', encoding='utf-8') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
print("分块处理大文件:")
total_size = 0
for i, chunk in enumerate(process_large_file('big_data.txt', chunk_size=500), 1):
chunk_size = len(chunk)
total_size += chunk_size
print(f"第{i}块: {chunk_size}字节, 内容预览: {chunk[:50].replace(chr(10), ' ')}...")
print(f"总处理大小: {total_size}字节")
# 清理临时文件
import os
os.remove('big_data.txt')数据结构化处理
日志文件解析示例
原始日志格式:
分块处理大文件:
第1块: 500字节, 内容预览: 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交...
第2块: 500字节, 内容预览: 志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志...
第3块: 500字节, 内容预览: 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 ...
第4块: 500字节, 内容预览: 日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日...
第5块: 500字节, 内容预览: 据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据...
第6块: 500字节, 内容预览: 易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易...
第7块: 500字节, 内容预览: 数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数...
第8块: 500字节, 内容预览: 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交...
第9块: 500字节, 内容预览: 志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志...
第10块: 500字节, 内容预览: 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 ...
第11块: 500字节, 内容预览: 日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日...
第12块: 500字节, 内容预览: 据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据...
第13块: 500字节, 内容预览: 易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易...
第14块: 500字节, 内容预览: 数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数据 交易日志数...
第15块: 4字节, 内容预览: 结束标记...
总处理大小: 7004字节解析代码:
import re
from collections import defaultdict
# 创建示例日志文件
log_content = """2024-01-02 09:30:00 [INFO] 系统启动完成
2024-01-02 09:31:15 [INFO] 用户admin登录成功
2024-01-02 09:32:45 [WARN] 内存使用率85%
2024-01-02 09:35:20 [ERROR] 数据库连接超时
2024-01-02 09:36:10 [INFO] 数据库重连成功
2024-01-02 09:40:00 [DEBUG] 缓存刷新开始
2024-01-02 09:41:30 [ERROR] 交易处理失败
"""
with open('system.log', 'w', encoding='utf-8') as f:
f.write(log_content)
log_pattern = re.compile(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)')
def parse_logs(file_path):
stats = defaultdict(int)
with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
match = log_pattern.match(line.strip())
if match:
timestamp, level, message = match.groups()
stats[level] += 1
print(f"{timestamp} | {level:5s} | {message}")
return stats
print("日志解析结果:")
log_stats = parse_logs('system.log')
print("\n日志级别统计:")
for level, count in sorted(log_stats.items()):
print(f"{level}: {count}次")
# 清理临时文件
import os
os.remove('system.log')执行前后对比:
日志解析结果:
2024-01-02 09:30:00 | INFO | 系统启动完成
2024-01-02 09:31:15 | INFO | 用户admin登录成功
2024-01-02 09:32:45 | WARN | 内存使用率85%
2024-01-02 09:35:20 | ERROR | 数据库连接超时
2024-01-02 09:36:10 | INFO | 数据库重连成功
2024-01-02 09:40:00 | DEBUG | 缓存刷新开始
2024-01-02 09:41:30 | ERROR | 交易处理失败
日志级别统计:
DEBUG: 1次
ERROR: 2次
INFO: 3次
WARN: 1次📝 动手练一练
第 1 题 · 概念应用
你的量化策略程序需要持续监控一个由外部数据源(如行情网关)生成的实时日志文件 market_feed.log,该文件会不断追加新的行情快照。程序需要实时读取并处理最新到达的日志行,同时避免因一次性读取整个大文件而占用过多内存。以下哪种文件读取方式最适合此场景?
- A. 使用
open(‘market_feed.log’, ‘r’).read()一次性读取全部内容,处理完后再关闭文件。 - B. 使用
open(‘market_feed.log’, ‘r’).readlines()将文件所有行读入列表,然后遍历列表。 - C. 使用
with open(‘market_feed.log’, ‘r’) as f:打开文件后,在循环中反复调用f.read(4096)分块读取。 - D. 使用
with open(‘market_feed.log’, ‘r’) as f:打开文件后,直接对文件对象f进行for line in f:迭代。
👉 点击查看参考答案
答案:D
推理过程:
- 选项A (
read()):会一次性将整个文件内容加载到内存。对于持续增长的日志文件,这会导致内存使用量随时间不断增加,直至可能耗尽内存,不适合实时持续处理。 - 选项B (
readlines()):与read()类似,会将所有行读入内存列表,存在相同的内存问题。 - 选项C (
read(chunk_size)):分块读取确实可以控制内存占用,但它按固定字节数切割,可能破坏行的完整性(例如一行数据被截断在两块之间),增加行解析的复杂度。 - 选项D (
for line in f:):这是逐行迭代读取的标准做法。文件对象本身是一个迭代器,每次迭代自动读取下一行到内存,处理完即释放,内存占用恒定且极小,且天然以行为单位,方便解析,非常适合处理大日志文件。需要注意:普通的for line in f读到文件末尾(EOF)就结束,不会自动等待新内容;若要实时跟踪持续追加的日志(tail -f效果),还需循环中配合time.sleep轮询并用f.seek()记住已读位置(属于进阶内容,本题先掌握逐行迭代本身)。
因此,选项D是兼顾内存效率与代码简洁性的最佳选择。
第 2 题 · 代码填空
你正在编写一个数据清洗脚本,需要读取一个来源不明的中文文本数据文件 raw_data.txt,但该文件可能使用 GBK 或 UTF-8 编码。脚本的目标是:尝试用 UTF-8 解码,如果失败则自动回退到 GBK 解码,最终把读到的内容统一按 UTF-8 重新编码并验证。请补全下面代码中的空缺部分。
import codecs
# 模拟两种不同编码的原始字节数据
utf8_bytes = "沪深300, 上证指数\n".encode('utf-8')
gbk_bytes = "净利润, 同比增长\n".encode('gbk')
def read_file_with_fallback(file_path):
"""尝试用UTF-8读取文件,失败则用GBK"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
print(f"成功以 UTF-8 读取文件")
except UnicodeDecodeError:
print("UTF-8 解码失败,尝试 GBK...")
with open(file_path, 'r', encoding=______) as f:
content = f.read()
print(f"成功以 GBK 读取文件")
return content
# 测试1:写入UTF-8编码的文件
with open('test_utf8.txt', 'wb') as f:
f.write(utf8_bytes)
content1 = read_file_with_fallback('test_utf8.txt')
print(f"读取内容: {content1}")
# 测试2:写入GBK编码的文件
with open('test_gbk.txt', 'wb') as f:
f.write(gbk_bytes)
content2 = read_file_with_fallback('test_gbk.txt')
print(f"读取内容: {content2}")
# 统一转换为UTF-8字节并验证
utf8_output = content2.encode('utf-8')
print(f"统一转换为UTF-8后的字节: {utf8_output}")
print(f"重新解码: {utf8_output.decode('utf-8')}")
# 清理临时文件
import os
os.remove('test_utf8.txt')
os.remove('test_gbk.txt')👉 点击查看参考答案
空位应填:'gbk'
补全后的完整可运行代码块:
import codecs
import os
# 模拟两种不同编码的原始字节数据
utf8_bytes = "沪深300, 上证指数\n".encode('utf-8')
gbk_bytes = "净利润, 同比增长\n".encode('gbk')
def read_file_with_fallback(file_path):
"""尝试用UTF-8读取文件,失败则用GBK"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
print(f"成功以 UTF-8 读取文件")
except UnicodeDecodeError:
print("UTF-8 解码失败,尝试 GBK...")
with open(file_path, 'r', encoding='gbk') as f:
content = f.read()
print(f"成功以 GBK 读取文件")
return content
# 测试1:写入UTF-8编码的文件
with open('test_utf8.txt', 'wb') as f:
f.write(utf8_bytes)
content1 = read_file_with_fallback('test_utf8.txt')
print(f"读取内容: {content1}")
# 测试2:写入GBK编码的文件
with open('test_gbk.txt', 'wb') as f:
f.write(gbk_bytes)
content2 = read_file_with_fallback('test_gbk.txt')
print(f"读取内容: {content2}")
# 统一转换为UTF-8字节并验证
utf8_output = content2.encode('utf-8')
print(f"统一转换为UTF-8后的字节: {utf8_output}")
print(f"重新解码: {utf8_output.decode('utf-8')}")
# 清理临时文件
os.remove('test_utf8.txt')
os.remove('test_gbk.txt')成功以 UTF-8 读取文件
读取内容: 沪深300, 上证指数
UTF-8 解码失败,尝试 GBK...
成功以 GBK 读取文件
读取内容: 净利润, 同比增长
统一转换为UTF-8后的字节: b'\xe5\x87\x80\xe5\x88\xa9\xe6\xb6\xa6, \xe5\x90\x8c\xe6\xaf\x94\xe5\xa2\x9e\xe9\x95\xbf\n'
重新解码: 净利润, 同比增长运行结果说明:
- 第一个测试文件
test_utf8.txt以 UTF-8 编码写入。read_file_with_fallback函数第一次尝试用encoding='utf-8'打开成功,直接返回内容。 - 第二个测试文件
test_gbk.txt以 GBK 编码写入。函数第一次尝试用encoding='utf-8'打开会引发UnicodeDecodeError,被 except 捕获。随后在 except 块内,使用encoding='gbk'再次打开文件,成功读取。 - 最后,将读取到的 GBK 内容(此时在 Python 中已是 Unicode 字符串)重新编码为 UTF-8 字节,演示了编码统一的过程。
本章小结
📋 行动清单
- 创建一个包含多行文本的TXT文件,分别用三种读取模式提取内容并比较差异
- 编写一个日志分析脚本,统计不同日志级别(INFO/WARN/ERROR)的出现频率
- 下载一个GBK编码的文本文件,使用编码检测自动转换为UTF-8格式
—— 小象教研组