Python之mmap内存映射模块(大文本处理)说明

背景:

      一般在UNIX下面处理文本文件的方法是sed、awk等shell命令,对于处理大文件受CPU,IO等因素影响,对服务器也有必定的压力。关于sed的说明能够看了解sed的工做原理,本文将介绍经过python的mmap模块来实现对大文件的处理,来对比看他们的差别。html

说明:

     mmap是一种虚拟内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。关于系统中mmap的理论说明能够看百度百科维基百科说明以及mmap函数介绍,这里的说明是针对在Python下mmap模块的使用说明。python

使用:
1,建立:建立并返回一个 mmap 对象msql

m=mmap.mmap(fileno, length[, flags[, prot[, access[, offset]]]])

fileno: 文件描述符,能够是file对象的fileno()方法,或者来自os.open(),在调用mmap()以前打开文件,再也不须要文件时要关闭。shell

os.O_RDONLY 以只读的方式打开 Read only os.O_WRONLY 以只写的方式打开 Write only os.O_RDWR 以读写的方式打开 Read and write os.O_APPEND 以追加的方式打开 os.O_CREAT 建立并打开一个新文件 os.O_EXCL os.O_CREAT| os.O_EXCL 若是指定的文件存在,返回错误 os.O_TRUNC 打开一个文件并截断它的长度为零(必须有写权限) os.O_BINARY 以二进制模式打开文件(不转换) os.O_NOINHERIT 阻止建立一个共享的文件描述符 os.O_SHORT_LIVED os.O_TEMPORARY 与O_CREAT一块儿建立临时文件 os.O_RANDOM 缓存优化,但不限制从磁盘中随机存取 os.O_SEQUENTIAL 缓存优化,但不限制从磁盘中序列存取 os.O_TEXT 以文本的模式打开文件(转换)
View Code

length要映射文件部分的大小(以字节为单位),这个值为0,则映射整个文件,若是大小大于文件当前大小,则扩展这个文件。缓存

flags:MAP_PRIVATE:这段内存映射只有本进程可用;mmap.MAP_SHARED:将内存映射和其余进程共享,全部映射了同一文件的进程,都可以看到其中一个所作的更改;
protmmap.PROT_READ, mmap.PROT_WRITE 和 mmap.PROT_WRITE | mmap.PROT_READ。最后一者的含义是同时可读可写。
服务器

access在mmap中有可选参数access的值有app

ACCESS_READ:读访问。ide

ACCESS_WRITE:写访问,默认。函数

ACCESS_COPY:拷贝访问,不会把更改写入到文件,使用flush把更改写到文件。post

2,方法:mmap 对象的方法,对象m

m.close()
关闭 m 对应的文件;

m.find(str, start=0)
从 start 下标开始,在 m 中从左往右寻找子串 str 最先出现的下标;

m.flush([offset, n]) 把 m 中从offset开始的n个字节刷到对应的文件中; m.move(dstoff, srcoff, n) 等于 m[dstoff:dstoff
+n] = m[srcoff:srcoff+n],把从 srcoff 开始的 n 个字节复制到从 dstoff 开始的n个字节,可能会覆盖重叠的部分。 m.read(n) 返回一个字符串,从 m 对应的文件中最多读取 n 个字节,将会把 m 对应文件的位置指针向后移动; m.read_byte() 返回一个1字节长的字符串,从 m 对应的文件中读1个字节,要是已经到了EOF还调用 read_byte(),则抛出异常 ValueError; m.readline() 返回一个字符串,从 m 对应文件的当前位置到下一个'\n',当调用 readline() 时文件位于 EOF,则返回空字符串; m.resize(n) ***有问题,执行不了*** 把 m 的长度改成 n,m 的长度和 m 对应文件的长度是独立的; m.seek(pos, how=0) 同 file 对象的 seek 操做,改变 m 对应的文件的当前位置; m.size() 返回 m 对应文件的长度(不是 m 对象的长度len(m)); m.tell() 返回 m 对应文件的当前位置; m.write(str) 把 str 写到 m 对应文件的当前位置,若是从 m 对应文件的当前位置到 m 结尾剩余的空间不足len(str),则抛出 ValueError; m.write_byte(byte) 把1个字节(对应一个字符)写到 m 对应文件的当前位置,实际上 m.write_byte(ch) 等于 m.write(ch)。若是 m 对应文件的当前位置在 m 的结尾,也就是 m 对应文件的当前位置到 m 结尾剩余的空间不足1个字节,write() 抛出异常ValueError,而 write_byte() 什么都不作。

方法的使用说明:介绍上面经常使用的方法

测试文本:test.txt,mmap对象m

-- MySQL dump 10.13  Distrib 5.6.19, for osx10.7 (x86_64)
--
-- Host: localhost    Database: test
-- ------------------------------------------------------
-- Server version       5.6.19

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
View Code

①: m.close(),关闭对象

>>> import os,mmap >>> m=mmap.mmap(os.open('test.txt',os.O_RDWR),0)  #创业内存映射对象>>> m.read(10)                                    #可使用方法 '-- MySQL d'
>>> m.close() #关闭对象 >>> m.read(10)                                    #方法不可用
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: mmap closed or invalid

②:m.find(str, start=0),从start的位置开始寻找第一次出现的str。

>>> m.find('SET',0)      #从头开始查找第一次出现SET的字符串
197

③:m.read(n),返回一个从 m对象文件中读取的n个字节的字符串,将会把 m 对象的位置指针向后移动,后续读取会继续往下读。

>>> m.read(10)         #读取10字节的字符串
'-- MySQL d'
>>> m.read(10)         #读取上面10字节后,再日后的10字节数据
'ump 10.13 '

④:m.read_byte(),返回一个1字节长的字符串,从 m 对应的文件中读1个字节

>>> m.read_byte() #读取第一个字节 '-'
>>> m.read_byte() #读取第二个字节 '-'
>>> m.read_byte()  #读取第三个字节 ' '

⑤:m.readline():返回一个字符串,从 m 对应文件的当前位置到下一个'\n',当调用 readline() 时文件位于 EOF,则返回空字符串

>>> m.readline()             #读取一正行
'-- MySQL dump 10.13  Distrib 5.6.19, for osx10.7 (x86_64)\n'
>>> m.readline()             #读取下一正行
'--\n'

⑥:m.size():返回 m 对应文件的长度(不是 m 对象的长度len(m))

>>> m.size()            #整个文件的大小
782

⑦:m.tell():返回 m 对应文件的当前光标位置

>>> m.tell()        #当前光标的位置0
0
>>> m.read(10)      #读取10个字节
'-- MySQL d'
>>> m.tell()        #当前光标位置10
10

⑧:m.seek(pos, how=0),改变 m 对应的文件的当前位置

>>> m.seek(10)        #当前光标定位到10
>>> m.tell()          #读取当前光标的位置
10
>>> m.read(10)        #读取当前光标以后的10字节内容
'ump 10.13 '

⑨:m.move(dstoff, srcoff, n):等于 m[dstoff:dstoff+n] = m[srcoff:srcoff+n],把从 srcoff 开始的 n 个字节复制到从 dstoff 开始的n个字节

>>> m[101:108]            #切片101到108的值
'-------'
>>> m[1:8]                #切片1到8的值
'- MySQL'
>>> m.move(1,101,8)       #从101开始到后面的8字节(108),替换从1开始到后面的8字节(8)效果:m[1:8]=m[101:108] 
>>> m[1:8]                #被替换后
'-------'

⑩:m.write(str):把 str 写到 m 对应文件的当前光标位置(覆盖对应长度),若是从 m 对应文件的当前光标位置到 m 结尾剩余的空间不足len(str),则抛出 ValueError

>>> m.tell()                #当前光标位置
0 
>>> m.write('zhoujy')       #写入str,要是写入的大小大于本来的文件,会报错。m.write_byte(byte)不会报错。
>>> m.tell()                #写入后光标位置 
6
>>> m.seek(0)               #重置,光标从头开始 
>>> m.read(10)              #查看10个字节,肯定是否被修改为功 
'zhoujy---d'

⑪:m.flush():把 m 中从offset开始的n个字节刷到对应的文件中

注意:对于m的修改操做,能够当成一个列表进行切片操做,可是对于切片操做的修改须要改为一样长度的字符串,不然都会报错。如m中的10个字符串进行修改,必须改为10个字符的长度。

3,应用说明: 

1):读文件,ACCESS_READ

①:读取整个文件

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import mmap
import contextlib

f = open('test.txt', 'r')
with contextlib.closing(mmap.mmap(f.fileno(), 0,access=mmap.ACCESS_READ)) as m:
#readline须要循环才能读取整个文件
while True: line = m.readline().strip() print line
#光标到最后位置(读完),就退出
if m.tell()==m.size(): break

效果:

~$ python untitled.py                                                                                                                                1-- ZHOUJY  ---dump 10.13  Distrib 5.6.19, for osx10.7 (x86_64)
--
-- Host: localhost    Database: test
-- ------------------------------------------------------
-- Server version       5.6.19

/*!40101 ZHOUJY SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */ZHOUJY;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */ ZHOUJY;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
View Code

②:逐步读取指定字节数文件

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import mmap
import contextlib

with open('test.txt', 'r') as f:
    with contextlib.closing(mmap.mmap(f.fileno(), 0,access=mmap.ACCESS_READ)) as m:
        print '读取10个字节的字符串 :', m.read(10)
        print '支持切片,对读取到的字符串进行切片操做:', m[2:10]
        print '读取以前光标后的10个字符串', m.read(10)

效果:

 ~$ python untitled.py
读取10个字节的字符串 : -- ZHOUJY 
支持切片,对读取到的字符串进行切片操做:  ZHOUJY 
读取以前光标后的10个字符串  ---dump 1
View Code

2):查找文件,ACCESS_READ

①:从整个文件查找全部匹配的

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import mmap
import contextlib

word = 'ZHOUJY'
print '查找:', word

f = open('test.txt', 'r')
with contextlib.closing(mmap.mmap(f.fileno(), 0,access=mmap.ACCESS_READ)) as m:
#也能够经过find(str,pos)来处理
while True: line = m.readline().strip() if line.find(word)>=0: print "结果:" print line elif m.tell()==m.size(): break else: pass

效果:

~$ python untitled.py
查找: ZHOUJY
结果:
-- ZHOUJY  ---dump 10.13  Distrib 5.6.19, for osx10.7 (x86_64)
结果:
/*!40101 ZHOUJY SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */ZHOUJY;
结果:
/*!40103 SET TIME_ZONE='+00:00' */ ZHOUJY;
View Code

②:从整个文件里查找,找到就退出(确认究竟是否存在

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import mmap
import contextlib

word = 'ZHOUJY'
print '查找:', word

f = open('test.txt', 'r')
with contextlib.closing(mmap.mmap(f.fileno(), 0,access=mmap.ACCESS_READ)) as m:
#不须要循环,只要找到一个就能够了 loc
= m.find(word) if loc >= 0: print loc print m[loc:loc+len(word)]

效果:

~$ python untitled.py
查找: ZHOUJY
194
ZHOUJY
View Code

③:经过正则查找,(找出40开头的数字)

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import mmap
import re
import contextlib
  
pattern = re.compile(r'(40\d*)')
  
with open('test.txt', 'r') as f:
    with contextlib.closing(mmap.mmap(f.fileno(), 0,access=mmap.ACCESS_READ)) as m:
        print pattern.findall(m)

效果:

 ~$ python untitled.py
['40101', '40101', '40101', '40101', '40103', '40103', '40014', '40014', '40101', '40111']
View Code 

3):处理文本,只能等长处理(经过上面的查找方法,来替换查找出的内容),模式:ACCESS_WRITE、ACCESS_COPY

通过上面对mmap方法的介绍和使用说明,大体了解了mmap的特色。这里经过对比sed的方法,来看看到底处理大文件使用哪一种方法更高效。 

①:替换文本中出现一次的内容。好比想把A库的备份文件(9G)还原到B库,须要把里面的USE `A`改为USE `B`。

1> sed处理:时间消耗近105s;磁盘IO几乎跑满;内存几乎没消耗、CPU消耗10~20%之间。

1:替换文本中第一次出现的内容 ~$ date && sed -i '0,/USE `edcba`;/s//USE `ABCDE`;/' test.sql && date
2016年 11月 16日 星期三 12:04:17 CST
2016年 11月 16日 星期三 12:06:02 CST

2:替换文本中指定行的内容 ~$ date && sed -i '24s/USE `ABCDE`;/USE `edcba`;/' test.sql && date
2016年 11月 16日 星期三 12:09:05 CST
2016年 11月 16日 星期三 12:10:50 CST

IO消耗:

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               1.00     7.00  772.00  105.00    87.22    92.06   418.65    27.90   31.35    2.21  245.56   1.14 100.00

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               1.00     4.00  778.00  102.00    87.59    90.03   413.36    25.08   30.30    2.59  241.65   1.13  99.60

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               2.00     5.00  771.00  101.00    87.48    88.04   412.22    29.80   30.24    2.34  243.21   1.14  99.60

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               1.00    18.00  431.00  137.00    49.08   122.04   616.99    66.20   70.25    3.02  281.75   1.75  99.60

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     1.00    1.00  248.00     0.00   177.04  1456.16   105.24  416.53   24.00  418.11   4.02 100.00
View Code

2> python处理:时间消耗是毫秒级别的,几乎是秒级别完成,该状况比较特别:搜索的关键词在大文本里比较靠前的位置,这样处理上T的大文件也是很是快的,要是搜索的关键词靠后怎会怎么样呢?后面会说明。

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import mmap
import contextlib
import re


word = 'USE `EDCBA`;'
replace = 'USE `ABCDE`;'
print '查找:', word
print'替换:', replace

f = open('test.sql', 'r+')
with contextlib.closing(mmap.mmap(f.fileno(), 0,access=mmap.ACCESS_WRITE)) as m:
    loc = m.find(word)
    if loc >=0:
        print loc
        m[loc:loc + len(word)] = replace

执行:

~$ date && python mmap_python.py && date
2016年 11月 16日 星期三 12:14:19 CST
查找: USE `EDCBA`;
替换: USE `ABCDE`;
929
2016年 11月 16日 星期三 12:14:19 CST

②:替换文本中全部匹配的关键词。好比想把备份文件里的ENGINE=MYISAM改为ENGINE=InnoDB,看看性能如何。

1> sed处理:时间消耗110s;磁盘IO几乎跑满(读写IO高);内存几乎没消耗、CPU消耗10~30%之间。

~$ date && sed -i 's/ENGINE=InnoDB/ENGINE=MyISAM/g' test.sql && date
2016年 11月 16日 星期三 12:19:30 CST
2016年 11月 16日 星期三 12:21:20 CST 

和①中sed的执行效果差很少,其实对于处理一条仍是多条记录,sed都是作一样工做量的事情,至于缘由能够看了解sed的工做原理说明,我的理解大体意思就是:sed是1行1行读取(因此内存消耗很小),放入到本身设置的缓冲区里,替换完以后再写入(因此IO很高),处理速度受限于CPU和IO。

2> python处理:时间消耗20多秒,比sed少。由于不用重写全部内容,只须要替换指定的内容便可,而且是在内存中处理的,因此写IO的压力几乎没有。当关键词比较靠后,其读入的数据就比较大,文件须要从磁盘读入到内存,这时磁盘的读IO也很高,写IO仍是没有。由于是虚拟内存映射文件,因此占用的物理内存很少,虽然经过TOP看到的内存使用率%mem很高,这里能够不用管,由于大部分都是在SHR列里的消耗,真正使用掉的内存能够经过RES-SHR来计算。关于top中SHR的意思,能够去看相关文章说明。

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import mmap
import contextlib

word    = 'ENGINE=MyISAM'
replace = 'ENGINE=InnoDB'
print '查找:', word
print'替换:', replace

loc = 0 f
= open('test.sql', 'r+') with contextlib.closing(mmap.mmap(f.fileno(), 0,access=mmap.ACCESS_WRITE)) as m: while True: loc = m.find(word,loc) if loc >=0: print loc m[loc:loc + len(word)] = replace #要是access=mmap.ACCESS_COPY须要执行flush #m.flush() elif loc == -1: break else: pass

效果:

~$ date && python mmap_python.py && date
2016年 11月 16日 星期三 13:19:30 CST
查找: ENGINE=MyISAM
替换: ENGINE=InnoDB
1663
5884938
11941259
12630481
12904261
64852169
64859312
65018692
65179617
65181544
65709930
149571849
3592900115
5874952354
7998151839
2016年 11月 16日 星期三 13:19:55 CST

③:正则匹配修改,这个能够经过上面介绍的查找方法,作下修改便可,就再也不作说明。

小结:

      对比sed和python处理文件的方法,这里来小结下:对于sed无论修改的关键字在文本中的任意位置、次数,修改的工做量都同样(全文的读写IO),差距不大;对于python mmap的修改,要是关键字出如今比较靠前的地方,修改起来速度很是快,不然修改也会有大量的读IO,写IO没有。经过上面的对比分析来看,mmap的修改要比sed修改性能高。

      Python还有另外一个读取操做的方法:open中的read、readline、readlines,这个方法是把文件所有载入内存,再进行操做。若内存不足直接用swap或则报错退出,内存消耗和文本大小成正比,而经过mmap模块的方法能够很好的避免了这个问题。

总结:

经过上面的介绍,大体知道如何使用mmap模块了,其大体特色以下:

  • 普通文件被映射到虚拟地址空间后,程序能够向访问普通内存同样对文件进行访问,在有些状况下能够提升IO效率。
  • 它占用物理内存空间少,能够解决内存空间不足的问题,适合处理超大文件。
  • 不一样于一般的字符串对象,它是可变的,能够经过切片的方式更改,也能够定位当前文件位置m.tell()m.seek()定位到文件的指定位置,再进行m.write(str)固定长度的修改操做

最后,能够把mmap封装起来进行使用了,脚本信息:

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import mmap
import contextlib
import time
from optparse import OptionParser


def calc_time(func):
    def _deco(*args, **kwargs):
        begin_time = time.time()
        func(*args, **kwargs)
        cost_time = time.time() - begin_time
        print 'cost time: %s' % (cost_time)
    return _deco

@calc_time
def replace_keyword_all(filename,old_word,new_word):
    if len(old_word) == len(new_word):
        loc = 0
        print "%s 替换成 %s " %(new_word,old_word)
        with open(filename,'r+') as f:
            with contextlib.closing(mmap.mmap(f.fileno(), 0,access=mmap.ACCESS_WRITE)) as m:
                while True:
                    loc = m.find(old_word,loc)
                    if loc >= 0:
                        m[loc:loc+len(old_word)] = new_word
                    elif loc == -1:
                        break
                    else:
                        pass
        f.close()
    else:
        print "替换的词要和被替换的词长度一致!"
        exit()


@calc_time
def replace_keyword_once(filename,old_word,new_word):
    if len(old_word) == len(new_word):
        print "%s 替换成 %s " %(new_word,old_word)
        with open(filename,'r+') as f:
            with contextlib.closing(mmap.mmap(f.fileno(), 0,access=mmap.ACCESS_WRITE)) as m:
                loc = m.find(old_word)
                if loc >= 0:
                    m[loc:loc+len(old_word)] = new_word
        f.close()
    else:
        print "替换的词要和被替换的词长度一致!"
        exit()

if __name__ == "__main__":
    parser = OptionParser()
    parser.add_option("-f", "--filename", help="Filename for search", dest="filename")
    parser.add_option("-o", "--oldword", help="the ip to use", dest="old_word")
    parser.add_option("-n", "--newword", help="the ip to use", dest="new_word")

    (options, args) = parser.parse_args()

    if not options.filename:
        print 'params filename need to apply'
        exit()

    if not options.old_word:
        print 'params oldword need to apply'
        exit()

    if not options.new_word:
        print 'params newword need to apply'
        exit()
# 替换文本中第一次出现的内容(查到一个就处理退出,越靠前越快)
#    replace_keyword_once(options.filename,options.old_word,options.new_word)
# 替换文本中出现的内容(查找处理整个文本)
    replace_keyword_all(options.filename,options.old_word,options.new_word)
View Code

方法: 

~$ python mmap_search.py -h
Usage: mmap_search.py [options]

Options:
  -h, --help            show this help message and exit
  -f FILENAME, --filename=FILENAME
                        Filename for search
  -o OLD_WORD, --oldword=OLD_WORD
                        the ip to use
  -n NEW_WORD, --newword=NEW_WORD
                        the ip to use
View Code

脚本处理效果:(40G的文本)

1)sed:替换文本中第一次出现的内容
~$ date && sed -i '0,/USE `EDCBA`;/s//USE `ABCDE`;/' test.sql && date
2016年 11月 17日 星期四 11:15:33 CST
2016年 11月 17日 星期四 11:21:47 CST

2)mmap:替换文本中第一次出现的内容(使用replace_keyword_once方法,查到一个就处理退出,越靠前越快)
~$ python mmap_search.py --filename='test.sql' --oldword="USE \`EDCBA\`;" --newword="USE \`ABCDE\`;"
USE `ABCDE`; 替换成 USE `EDCBA`; 
cost time: 0.000128984451294

3)sed:替换文本中出现的内容(查找处理整个文本)
~$ date && sed -i 's/ENGINE=InnoDB/ENGINE=MyISAM/g' test.sql && date
2016年 11月 17日 星期四 10:04:49 CST
2016年 11月 17日 星期四 10:11:34 CST

4)mmap:替换文本中出现的内容(使用replace_keyword_all方法,查找处理整个文本)
~$ python mmap_search.py --filename="test.sql" --oldword="ENGINE=MyISAM" --newword="ENGINE=InnoDB"
ENGINE=InnoDB 替换成 ENGINE=MyISAM 
cost time: 198.471223116

结论:修改大文本文件,经过sed处理,无论被修改的词在哪一个位置都须要重写整个文件;而mmap修改文本,被修改的词越靠前性能越好,不须要重写整个文本,只要替换被修改词语的长度便可。

参考文档:

Memory-mapped file support

经过mmap库映射文件到内存用法

mmap模块与mmap对象

相关文章
相关标签/搜索