使用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語言優美又混亂。
補充知識:Python.json.常見兩個錯誤處理(Expecting,delimiter)(Invalid control character at)
ValueError: Invalid control character at: line 1 column 122(char 123)
出現錯誤的原因是字串中包含了回車符(\r)或者換行符(\n)
解決方案:
轉義
json_data = json_data.replace('\r','\\r').replace('\n','\\n')
使用關鍵字strict
json.loads(json_data,strict=False)
ValueError: Expecting,delimiter: line 13 column 650 (char 4186)
原因:json資料不合法,類似“group_buy_create_description_text”: “1. Select the blue “Buy” button to let other shoppers buy with you.這樣的內容出現在json資料中。
解決方案:
將類似的情形通過正則篩選出來通過下面的方式處理。
正則表示式如下:
json_data = json_data.replace('""','"########"')
js_str = '"[\s\S]+?":\s?"([\s\S]+?)"\}?\}?\]?,'
後續使用中發現無法匹配value為空的情況,故先做一下預處理
這個正則可以匹配到大部分的key,value中的value值,但是也有例外,暫時的處理方法是如果匹配結果中包含”{“,“}”,“[“,“]”這樣的字元,說明是匹配失敗結果,跳過處理。其他的使用下邊的方法替換掉可能出問題的字元。
如果大家有更好的正則匹配方式,歡迎隨時批評指正。
def htmlEscape(input) { if not input return input; input = input.replace("&","&"); input = input.replace("<","<"); input = input.replace(">",">"); input = input.replace(" "," "); input = input.replace("'","'"); //IE暫不支援單引號的實體名稱,而支援單引號的實體編號,故單引號轉義成實體編號,其它字元轉義成實體名稱 input = input.replace("\"","""); //雙引號也需要轉義,所以加一個斜線對其進行轉義 input = input.replace("\n","<br/>"); //不能把\n的過濾放在前面,因為還要對<和>過濾,這樣就會導致<br/>失效了 return input; }
以上這篇使用Python檔案讀寫,自定義分隔符(custom delimiter)就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。