Python文件读写,自定义分隔符(custom delimiter)

众所周知,python文件读取文件的时候所支持的newlines(即换行符),是指定的。这一点不管是从python的doucuments上还是在python的源码中(作者是参考了python的io版本,并没有阅读C版本),都可以看出来:

if newline is not None and not isinstance(newline, str):
    raise TypeError("illegal newline type: %r" % (type(newline),))
if newline not in (None, "", "\n", "\r", "\r\n"):
    raise ValueError("illegal newline value: %r" % (newline,))

好吧,问题来了,如果你恰好是个苦逼的生物狗,正在用python处理所谓的fastq格式的测序结果文件,每次只读一行往往不是你想要的。Ok, 我们也都知道其实这个问题在Perl里面十分好解决,无非就是重新定义下文件的分割符($/,The input record separator, newline by default. Set undef to read through the end of file.)

local $/;           # enable "slurp" mode
local $_ = <FH>;    # whole file now here
s/\n[ \t]+/ /g;

简单粗暴有效!《Programming Perl》开头的那些关于什么是happiness定义看来所言非虚,所以你只要需要将$/定义为fastq格式的分隔符就ok了。
但是,如果是Python呢?(容易钻牛角尖的孩纸,又或者是不喜欢花括号的孩子…..反正就是强行高端了)。终于要进入正题了,OK,在python中又有两种方式解决这个问题,看你个人喜好选择了(当然要是有大神知道四种、五种方法,也不妨指导一下我这个小菜鸟)。
方案一的代码:

import _pyio
import io
import functools
class MyTextWrapper(_pyio.TextIOWrapper):
    def readrecod(self, sep):
            readnl, self._readnl = self._readnl, sep
            self._readtranslate = False
            self._readuniversal = False
            try:
                   return self.readline()
            finally:
                   self._readnl = readnl
#class MyTextWrapper(_pyio.TextIOWrapper):
#    def __init__(self, *args, separator, **kwargs):
#        super().__init__(*args,**kwargs)
#        self._readnl = separator
#        self._readtranslate = False
#        self._readuniversal = False
#        print("{}:\t{}".format(self,self._readnl))

f = io.open('data',mode='rt')
#f = MyTextWrapper(f.detach(),separator = '>')
#print(f._readnl)
f = MyTextWrapper(f.detach())
records=iter(functools.partial(f.readrecod, '>'), '')
for r in records:
    print(r.strip('>'))
    print("###")

Ok,这是Python3.x中的方法(亲测),那么在Python2.x中需要改动的地方,目测好像是(没有亲测)

super(MyTextWrapper,self).__init__(*args,**kwargs)

这个方法看上去还是比较elegant,但是efficient 吗?答案恐怕并不,毕竟放弃了C模块的速度优势,但是OOP写起来还是比较舒服的。对了值得指出的Python的I/O是一个layer一个layer的累加起来的。从这里我们就能看出来。当然里面的继承关系还是值得研究一下的,从最开始的IOBase一直到最后的TextIOWrapper,这里面的故事,还是要看一看的。
方案二的代码:

#!/usr/bin/env python

def delimited(file, delimiter = '\n', bufsize = 4096):
    buf = ''
    while True:
        newbuf = file.read(bufsize)
        if not newbuf:
            yield buf
            return
        buf += newbuf
        lines = buf.split(delimiter)
        for line in lines[:-1]:
            yield line
        buf = lines[-1]

with open('data', 'rt') as f:
    lines = delimited(f, '>', bufsize = 1)
    for line in lines:
        print line,
        print '######'

Ok,这里用到了所谓的generator函数,优雅程度也还行,至于效率么,请自行比较和测试吧(毕竟好多生物程序猿是不关心效率的…..)。如此一来,比Perl多敲了好多代码,唉,怀念Perl的时代啊,简单粗暴有效,就是幸福的哲学么。
当然还有童鞋要问,那么能不能又elegant还efficient(我可是一个高端的生物程序猿,我要强行高端!)答案是有的,请用Cython! 问题又来了,都Cython了,为什么不直接用C呢?确实,C语言优美又混乱。

请自行参考:

1.Python的文档:[https://docs.python.org/2/library/io.html?highlight=textiowrapper#text-i-o]
2.StackOverflow: [http://stackoverflow.com/questions/16260061/reading-a-file-with-a-specified-delimiter-for-newline]

你可能感兴趣的:(Python)