Skip to main content
 首页 » 编程设计

python之使用 Python 从文本文件读取的行中的特殊行尾字符/字符串

2025年05月04日32lidabo

我需要从文本文件中读取行,但是,“行尾”字符并不总是\n 或\x 或组合,可能是字符的任意组合,如“xyz”或“|”,但是对于每种类型的文件,“行尾”始终相同且已知。

由于文本文件可能很大,我必须牢记性能和内存使用情况,最好的解决方案是什么? 今天我使用 string.read(1000) 和 split(myendofline) 或 partition(myendofline) 的组合,但我想知道是否存在更优雅和标准的解决方案。

请您参考如下方法:

这是一个生成器函数,它充当文件的迭代器,根据奇异的换行符切割行在所有文件中都相同。

它按 lenchunk 字符 block 读取文件,并逐 block 显示每个当前 block 中的行。

因为在我的示例中换行符是 3 个字符 (':;:'),所以一个 block 可能会以截断的换行符结束:这个生成器函数会处理这种可能性并设法显示正确的行.

如果换行符只有一个字符,函数可以简化。我只为最微妙的情况编写了函数。

使用这个函数允许一次读取一个文件一行,而不用将整个文件读入内存。

from random import randrange, choice 
 
 
# this part is to create an exemple file with newline being :;: 
alphabet = 'abcdefghijklmnopqrstuvwxyz ' 
ch = ':;:'.join(''.join(choice(alphabet) for nc in xrange(randrange(0,40))) 
                for i in xrange(50)) 
with open('fofo.txt','wb') as g: 
    g.write(ch) 
 
 
# this generator function is an iterator for a file 
# if nl receives an argument whose bool is True, 
# the newlines :;: are returned in the lines 
 
def liner(filename,eol,lenchunk,nl=0): 
    # nl = 0 or 1 acts as 0 or 1 in splitlines() 
    L = len(eol) 
    NL = len(eol) if nl else 0 
    with open(filename,'rb') as f: 
        chunk = f.read(lenchunk) 
        tail = '' 
        while chunk: 
            last = chunk.rfind(eol) 
            if last==-1: 
                kept = chunk 
                newtail = '' 
            else: 
                kept = chunk[0:last+L]   # here: L 
                newtail = chunk[last+L:] # here: L 
            chunk = tail + kept 
            tail = newtail 
            x = y = 0 
            while y+1: 
                y = chunk.find(eol,x) 
                if y+1: yield chunk[x:y+NL] # here: NL 
                else: break 
                x = y+L # here: L 
            chunk = f.read(lenchunk) 
        yield tail 
     
 
 
for line in liner('fofo.txt',':;:'): 
    print line 

这里也是一样,在这里和那里打印以允许遵循算法。

from random import randrange, choice 
 
 
# this part is to create an exemple file with newline being :;: 
alphabet = 'abcdefghijklmnopqrstuvwxyz ' 
ch = ':;:'.join(''.join(choice(alphabet) for nc in xrange(randrange(0,40))) 
                for i in xrange(50)) 
with open('fofo.txt','wb') as g: 
    g.write(ch) 
 
 
# this generator function is an iterator for a file 
# if nl receives an argument whose bool is True, 
# the newlines :;: are returned in the lines 
 
def liner(filename,eol,lenchunk,nl=0): 
    L = len(eol) 
    NL = len(eol) if nl else 0 
    with open(filename,'rb') as f: 
        ch = f.read() 
        the_end = '\n\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'+\ 
                  '\nend of the file=='+ch[-50:]+\ 
                  '\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n' 
        f.seek(0,0) 
        chunk = f.read(lenchunk) 
        tail = '' 
        while chunk: 
            if (chunk[-1]==':' and chunk[-3:]!=':;:') or chunk[-2:]==':;': 
                wr = [' ##########---------- cut newline cut ----------##########'+\ 
                     '\nchunk== '+chunk+\ 
                     '\n---------------------------------------------------'] 
            else: 
                wr = ['chunk== '+chunk+\ 
                     '\n---------------------------------------------------'] 
            last = chunk.rfind(eol) 
            if last==-1: 
                kept = chunk 
                newtail = '' 
            else: 
                kept = chunk[0:last+L]   # here: L 
                newtail = chunk[last+L:] # here: L 
            wr.append('\nkept== '+kept+\ 
                      '\n---------------------------------------------------'+\ 
                      '\nnewtail== '+newtail) 
            chunk = tail + kept 
            tail = newtail 
            wr.append('\n---------------------------------------------------'+\ 
                      '\ntail + kept== '+chunk+\ 
                      '\n---------------------------------------------------') 
            print ''.join(wr) 
            x = y = 0 
            while y+1: 
                y = chunk.find(eol,x) 
                if y+1: yield chunk[x:y+NL] # here: NL 
                else: break 
                x = y+L # here: L 
            print '\n\n===================================================' 
            chunk = f.read(lenchunk) 
        yield tail 
        print the_end 
     
 
 
for line in liner('fofo.txt',':;:',1): 
    print 'line== '+line 

.

编辑

我比较了我的代码和 chmullig 的代码的执行时间。

带有一个大约 10 MB 的“fofo.txt”文件,创建于

alphabet = 'abcdefghijklmnopqrstuvwxyz ' 
ch = ':;:'.join(''.join(choice(alphabet) for nc in xrange(randrange(0,60))) 
                for i in xrange(324000)) 
with open('fofo.txt','wb') as g: 
    g.write(ch) 

并像这样测量时间:

te = clock() 
for line in liner('fofo.txt',':;:', 65536): 
    pass 
print clock()-te 
 
 
fh = open('fofo.txt', 'rb') 
zenBreaker = SpecialDelimiters(fh, ':;:', 65536) 
 
te = clock() 
for line in zenBreaker: 
    pass 
print clock()-te 

我在几篇论文中获得了以下观察到的最小时间:

............my code 0,7067 seconds

chmullig's code 0.8373 seconds

.

编辑 2

我更改了生成器函数:liner2() 采用文件处理程序而不是文件名。所以文件的打开可以不计时间,因为是chmullig代码的计费

def liner2(fh,eol,lenchunk,nl=0): 
    L = len(eol) 
    NL = len(eol) if nl else 0 
    chunk = fh.read(lenchunk) 
    tail = '' 
    while chunk: 
        last = chunk.rfind(eol) 
        if last==-1: 
            kept = chunk 
            newtail = '' 
        else: 
            kept = chunk[0:last+L]   # here: L 
            newtail = chunk[last+L:] # here: L 
        chunk = tail + kept 
        tail = newtail 
        x = y = 0 
        while y+1: 
            y = chunk.find(eol,x) 
            if y+1: yield chunk[x:y+NL] # here: NL 
            else: break 
            x = y+L # here: L 
        chunk = fh.read(lenchunk) 
    yield tail 
 
fh = open('fofo.txt', 'rb') 
te = clock() 
for line in liner2(fh,':;:', 65536): 
    pass 
print clock()-te 

经过无数篇文章,看到最少次数的结果,是

.........with liner() 0.7067seconds

.......with liner2() 0.7064 seconds

chmullig's code 0.8373 seconds

事实上,打开文件只占总时间的极小部分。