背景

前段时间在做一个算法测试,需要对源于日志的数据进行分析才能获取到结果;日志文件较大,所以想要获取数据的变化曲线,增量读取是最好的方式。

网上有很多人的技术博客都是写的用for循环readline以及一个计数器去增量读取,假如文件很大,遍历一次太久。而且对于很多大文件的增量读取,如果遍历每一行比对历史记录的输出或者全都加载到内存通过历史记录的索引查找,是非常浪费资源的,

获取文件句柄的基本理论中就包含指针操作。linux的文件描述符的struct里有一个f_pos的这么个属性,里面存着文件当前读取位置,通过这个东东经过vfs的一系列映射就会得到硬盘存储的位置了,所以很直接,很快。

在Python中的读取文件的方法也有类似的属性。

具体实现

Python中相关方法的核心函数如下:

函数 作用
tell() 返回文件当前位置
seek() 从指定位置开始读取信息

其中seek()有三种模式:

  • f.seek(p,0) 移动当文件第p个字节处,绝对位置
  • f.seek(p,1) 移动到相对于当前位置之后的p个字节
  • f.seek(p,2) 移动到相对文章尾之后的p个字节

参考代码:

  1. #!/usr/bin/python
  2. fd=open("test.txt",'r') #获得一个句柄
  3. for i in xrange(1,3): #读取三行数据
  4. fd.readline()
  5. label=fd.tell() #记录读取到的位置
  6. fd.close() #关闭文件
  7. #再次阅读文件
  8. fd=open("test.txt",'r') #获得一个句柄
  9. fd.seek(label,0)# 把文件读取指针移动到之前记录的位置
  10. fd.readline() #接着上次的位置继续向下读取

拓展

如何得知这个大文件行数,以及变化

我的想法:

方式1: 遍历'\n'字符。

方式2: 开始时就在for循环中对fd.readline()计数,变化的部分(用上文说的seek、tell函数做)再用for循环fd.readline()进行统计。

如何避免文件读取时,内存溢出

  • 可以通过 read 函数的chunk关键字来指定每次读区数据的大小
  • 使用生成器确保只有在数据被调用时才会生成

    具体方法封装如下:
  1. def read_in_chunks(file_path, chunk=100 * 100): # 通过chunk指定每次读取文件的大小防止内存占用过大
  2. file_object = open(file_path, "r")
  3. while True:
  4. data = file_object.read(chunk)
  5. if not data:
  6. file_object.close()
  7. break
  8. # 使用generator(生成器)使数据只有在被使用时才会迭代时占用内存
  9. yield data

应用

20191129添加

根据博客园一个朋友的实际问题写的一段应用代码,解决程序运行异常、断点再读问题:

  1. #! /usr/bin/python
  2. # coding:utf-8
  3. """
  4. @author:Bingo.he
  5. @file: 20191129-file.py
  6. @time: 2019/11/29
  7. """
  8. import os
  9. import glob
  10. class opened(object):
  11. def __init__(self, filename):
  12. self.filename = filename
  13. self.handle = open(filename)
  14. if filename in get_read_info().keys():
  15. self.handle.seek(get_read_info()[filename], 0)
  16. def __enter__(self):
  17. return self.handle
  18. def __exit__(self, exc_type, exc_value, exc_trackback):
  19. seek_num = self.handle.tell()
  20. set_read_info(self.filename, seek_num)
  21. self.handle.close()
  22. if exc_trackback is None:
  23. print(f"文件【{self.filename}】读取正常退出。")
  24. else:
  25. print(f"文件【{self.filename}】读取退出异常!")
  26. def get_read_info():
  27. """
  28. 读取已读取的文件的句柄位置
  29. :return:
  30. """
  31. file_info = {}
  32. # 如果文件不存在则创建一个空文件
  33. if not os.path.exists("temp"):
  34. with open("temp", 'w', encoding="utf-8") as f:
  35. pass
  36. return file_info
  37. with open("temp", 'r', encoding="utf-8") as f:
  38. datas = f.readlines()
  39. for data in datas:
  40. name, line = data.split("===")
  41. file_info[name] = int(line)
  42. return file_info
  43. def set_read_info(filename, seek_num):
  44. """
  45. 设置为已经读取的文件的句柄位置
  46. :param filename: 文件名称
  47. :param seek_num: 句柄位置
  48. :return:
  49. """
  50. flag = True
  51. with open("temp", 'r', encoding="utf-8") as f:
  52. datas = f.readlines()
  53. for num, data in enumerate(datas):
  54. if filename in data:
  55. flag = False
  56. datas[num] = f"{filename}==={seek_num}\n"
  57. if flag:
  58. datas.append(f"{filename}==={seek_num}\n")
  59. # print(datas)
  60. with open("temp", 'w', encoding="utf-8") as f:
  61. f.writelines(datas)
  62. # 测试代码
  63. # 注:文件读完之后,存储在temp文件中的,第二次读取时不会再读,可以以删除temp文件或者修改其中信息
  64. pys = glob.glob("*.py") # 获取当前目录以Py结尾的文件
  65. for py in pys:
  66. with opened(py) as fp: # 默认为读模式
  67. for line_data in fp:
  68. print(line_data)

【Python】实现对大文件的增量读取的更多相关文章

  1. C# 读取大文件 (可以读取3GB大小的txt文件)

    原文:C# 读取大文件 (可以读取3GB大小的txt文件) 在处理大数据时,有可能 会碰到 超过3GB大小的文件,如果通过 记事本 或 NotePad++去打开它,会报错,读不到任何文件. 如果你只是 ...

  2. python 小程序大文件的拆分合并

    1. 将大文件拆分为小文件 I 通过二进制的方式将大文件读取出来,将其拆分存,以不同的文件方式存放在一个目录下面 II 提供两种操作方式交互式和命令行模式 #! usr/bin/python # -* ...

  3. python处理分隔大文件

    4个.sql格式的文件,2G大小,直接插入mysql数据中,文件太大了,导入不进去. 太大的文件用python处理也很麻烦,处理不了,只能先分隔成小文件处理. 文件中数据格式:其中values里面的数 ...

  4. python里如何计算大文件的md5

    在python3中,有了一个hashlib,可以用来计算md5,这里先给出一个简单的例子: import hashlib sstr="i love hanyu" print(has ...

  5. python实现将大文件夹分割成多个子文件夹

    楼主用的linux,一旦数据达到几万,文件夹打开就会变卡,同时也方便同时分工协作,便于git管理,写了个将大文件夹分割成多个小文件夹的脚本 如操作文件夹:img,脚本不破坏img的数据,创建img_1 ...

  6. python读写txt大文件

    直接上代码: import easygui import os path = easygui.fileopenbox()#path是打开的文件的全路径 if path:#如果选择打开文件,没有选择取消 ...

  7. rsync增量传输大文件优化技巧

    问题 rsync用来同步数据非常的好用,特别是增量同步.但是有一种情况如果不增加特定的参数就不是很好用了.比如你要同步多个几十个G的文件,然后网络突然断开了一下,这时候你重新启动增量同步.但是发现等了 ...

  8. python学习笔记之文件操作(三)

    这篇博客小波主要介绍一下python对文件的操作 对文件的操作主要分为三步: 1.打开文件获取文件的句柄,句柄也是文件描述符 2.通过文件句柄操作文件 3.关闭文件. 现有以下文件,是小波随写的周杰伦 ...

  9. ASP.NET Core下载大文件的实现

    当我们的ASP.NET Core网站需要支持下载大文件时,如果不做控制可能会导致用户在访问下载页面时发生无响应,使得浏览器崩溃.可以参考如下代码来避免这个问题. 关于此代码的几点说明: 将数据分成较小 ...

随机推荐

  1. linux注意的一些地方

    assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行 #include <assert.h>void assert( int expr ...

  2. 容斥原理解决某个区间[1,n]闭区间与m互质数数量问题

    首先贴出代码(闭区间[1,n]范围内和m互质的数) 代码: int solve(II n,II m){ vector<II>p; ;i*i<=m;i++){ ){ p.push_ba ...

  3. Integer.valueof 和 Integer.parseInt

    System.out.println(Integer.valueOf("127")==Integer.valueOf("127")); System.out.p ...

  4. 转!!mysql 字段 is not null 和 字段 !=null

      今天在查询数据时,查到包含一条某个时间startTime(该字段默认为null ) 为null的记录,想把它过滤,加了 startTime != null 的条件,结果记录都没了,应该用条件 is ...

  5. git-【十】忽略文件

    1.在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件. 不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置 ...

  6. 国内比特币bitcoin交易平台

    火币网:   www.huobi.com 比特币中国:   www.btcchina.com okcoin:    www.okcoin.cn 中国比特币:   www.chbtc.com 比特币交易 ...

  7. (转) SpringBoot非官方教程 | 第十一篇:springboot集成swagger2,构建优雅的Restful API

    swagger,中文“拽”的意思.它是一个功能强大的api框架,它的集成非常简单,不仅提供了在线文档的查阅,而且还提供了在线文档的测试.另外swagger很容易构建restful风格的api,简单优雅 ...

  8. weiwo.wxmmd.com将您重定向的次数过多。尝试清除 Cookie.

    折腾了很久,最后更换PHP版本解决了,我的项目用的tp3.1.2,出现上图问题时的php版本是7.1,换回5.6就没有这个问题.希望能为大家提供一个思路.

  9. 2.4 The Object Model -- Computed Properties and Aggregate Data with @each(计算的属性和使用@each聚合数据)

    1. 通常,你可能有一个计算的属性依赖于数组中的所有元素来确定它的值.例如,你可能想要计算controller中所有todo items的数量,以此来确定完成了多少任务. export default ...

  10. Linux_Chrome出现Adobe Flash Player is out of date解决方法

    在安装Google的Chrome后都有出现Adobe Flash Player is out of date的问题. Chrome浏览器用的播放器插件是PepperFlashPlayer.而且是内置的 ...