说明
有 1000 个 20M ⼤⼩的⽂本⽂件,⽂件中每⾏数据的格式固定为: {“name”: “xx”,“timestamp”: xx, “content”: “xx”}
name: 字符串, 长度为 32 个字节以内,
timestamp: 毫秒级时间戳,
content: 字符串,⻓度为 1024 个字节以内
文件地址
https://mc-public-resource-cn.s3.cn-north- 1.amazonaws.com.cn/records.zip
编写程序完成如下要求
1、找出所有 name 为 zhangsan 的⾏,并按照 timestamp 从⼩到⼤排序
2、 将排序好的结果重新写⼊新的⽂件,规则如下: 按照 timestamp 以天为维度组织⽂件,新⽂件的命名规则为 年-月-日,例如(2022-1-12), 将所有位于同⼀天的数据存放于同⼀个⽂件中
要求
- 开发语⾔可以根据⾃⼰的偏好在 python、java、go 中选择,可以⾃由的选择框架、 库
- 在硬件性能恒定(内存 16 G)的情况下,尽可能的缩短程序耗时
目录结构如下,result是运行结果保存目录
多线程代码如下:主要是多线程模块,json模块,time模块
import os
import time
import json
from threading import Thread
class Main:
"""处理文件"""
def __init__(self, file_path, result_path):
self.file_path = file_path
self.result_path = result_path
self.filename_list = os.listdir(self.file_path)
self.filename_list = [self.file_path + i for i in self.filename_list if i.endswith('.txt')] # 文件名列表
self.date_dict = {} # 存放日期对应的值,为列表
# 结果保存目录
try:
os.mkdir(self.result_path)
except FileExistsError as e:
print('目录已存在')
def read_files(self, file):
"""按日期分类文件内容"""
with open(file, mode='r', encoding='utf8') as f:
for line in f:
if line[10:12] == '张三':
item = json.loads(line) # json转为dict类型
timestamp = item.get('timestamp') # 获取时间戳
date = time.strftime("%Y-%m-%d", time.localtime(timestamp / 1000)) # 时间戳转为str类型日期
if date not in self.date_dict.keys():
self.date_dict[date] = [] # 日期为key,value为列表
print('添加字典key', date)
# 筛选,添加到日期对应的字典里面
self.date_dict[date].append(item)
print('读取一个文件并分类完成')
def write_file(self, filename):
"""写入文件"""
with open(self.result_path + filename + '.txt', mode='w', encoding='utf8') as file:
for line in self.date_dict[filename]:
json.dump(line, file, ensure_ascii=False)
file.write('\n')
print('写入完成一个文件')
def run(self):
"""运行"""
# 读取文件,分类到列表套
task_list = []
for file in self.filename_list:
task = Thread(target=self.read_files, args=(file,))
task.start()
task_list.append(task)
[_.join() for _ in task_list]
print('开始排序字典value')
# 按列表内每个字典的时间戳排序
for item in self.date_dict.values():
item.sort(key=lambda d: d['timestamp'])
print('一个列表排序完成')
# 将排序好的写入文件
l = []
for filename in self.date_dict.keys():
t = Thread(target=self.write_file, args=(filename,))
t.start()
l.append(t)
[i.join() for i in l]
if __name__ == '__main__':
start = time.time()
# 目录路径(相对路径):源文件路径,结果保存路径
test = Main(file_path=r'records\\', result_path=r'result\\')
test.run()
end = time.time() - start
print('运行时间:', end)
最终运行时间:130秒
协程async,await代码如下
"""async await 协程方式"""
import os
import time
import json
import asyncio
# from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
async def read_files(file: str, date_dict: dict):
"""按日期分类文件内容"""
with open(file, mode='r', encoding='utf8') as f:
for line in f:
if line[10:12] == '张三':
item = json.loads(line) # json转为dict类型
timestamp = item.get('timestamp') # 获取时间戳
date = time.strftime("%Y-%m-%d", time.localtime(timestamp / 1000)) # 时间戳转为str类型日期
if date not in date_dict.keys():
date_dict[date] = [] # 日期为key,value为列表
print('添加字典key', date)
# 筛选,添加到日期对应的字典里面
date_dict[date].append(item)
print('读取一个文件并分类完成')
async def write_file(filename, result_path, date_dict):
"""写入文件"""
with open(result_path + filename + '.txt', mode='w', encoding='utf8') as file:
for line in date_dict[filename]:
json.dump(line, file, ensure_ascii=False)
file.write('\n')
print('写入完成一个文件')
async def main(file_path, result_path):
"""主函数"""
filename_list = os.listdir(file_path)
filename_list = [file_path + i for i in filename_list if i.endswith('.txt')] # 文件名列表
# 结果保存目录
try:
os.mkdir(result_path)
except FileExistsError as e:
print('目录已存在')
# 读取文件
read_files_tasks = []
date_dict = {} # 存放日期对应的值,为列表
for file in filename_list:
read_files_tasks.append(read_files(file, date_dict))
await asyncio.wait(read_files_tasks)
# 写入文件
write_file_tasks = []
for filename in date_dict.keys():
write_file_tasks.append(write_file(filename=filename, result_path=result_path, date_dict=date_dict))
await asyncio.wait(write_file_tasks)
if __name__ == '__main__':
import cProfile
start = time.time()
# 初始目录
file_path = r'records\\'
result_path = r'result\\'
# 启动
asyncio.run(main(file_path=file_path, result_path=result_path))
# 性能测试
# cProfile.run('asyncio.run(main(file_path=file_path, result_path=result_path))')
print('运行时间:', time.time() - start)
运行结束91秒
python版本使用3.7,电脑配置信息如下
总体思路
使用的都是python内置的库跟函数,每一个文件使用一个线程,读取一行先截取字符串判断name是否等于张三,后用json模块将每一行转换为dict类型获取时间戳转换为“年-月-日”格式放到一个字典里面,每一个独立的日期为key,值为列表套字典格式,筛选出来的完全足够放在内存中,而不用先写入文件。最终使用lambda对字典内列表按时间戳排序:item.sort(key=lambda d: d[‘timestamp’]),然后再使用多线程写入文件。
结论
硬盘的读写速度是主要限制因素。
刚开始读取文件时先判断,再转换为json能缩短大量时间。
使用字典的 in 判断元素是否存在也能缩短运行时间。
列表的sort方法排序也能缩短运行时间。
GIL锁的存在,使得只能一个线程工作,阻塞挂起,还不如协程。