New FAMILUG

The PyMiers

Thursday 4 September 2014

[python] đếm số dòng của 1 file.

Để đếm số dòng của một file, trên UNIX-like OSes, ta có thể dùng lệnh ``wc``. Lấy file /etc/passwd làm chuột bạch:
$ wc -l /etc/passwd
      86 /etc/passwd
Trong python, các nhanh nhất để làm việc này là:
(hãy tự trả lời trước khi xem đáp án)


In [1]: sum(1 for line in open('/etc/passwd'))
Out[1]: 86
Giải thích

sum - tính tổng của một chuỗi các số.
open('/etc/passwd') - mở file /etc/passwd và trả về một file object. File object cũng là iterator object, nó implement 2 method theo iterator protocol:

In [4]: f = open('/etc/passwd')

In [8]: hasattr(f, '__iter__')
Out[8]: True

In [9]: hasattr(f, 'next')
Out[9]: True

Nói đơn giản, bất cứ object nào có CẢ 2 method __iter__ và next thì nó đã implement iterator protocol và object đấy có thể dùng như 1 iterator. Khi một object (f) là 1 iterator thì có thể viết:
for x in f: 
để duyệt qua các phần tử mà f sẽ trả về.

Với file object, nó trả về lần lượt từng dòng trong file.
1 for line in open('/etc/passwd') - sẽ trả về một list toàn số 1, số các số 1 bằng số dòng trong file /etc/passwd.
Nên tổng của list này chính là số dòng trong file.

Bonus
 đếm số dòng của tất cả các file có extension là ".py" trong một thư mục:

def count(dest='.', ftype='.py'):
    no_files = 0
    locs = 0

    def filenames():
        for d, _, fns in os.walk(dest):
            for fn in fns:
                yield os.path.join(d, fn)

    for fn in filenames():
        no_files += 1
        fd = open(fn)
        loc = sum(1 for line in fd)
        print '%5d %s' % (loc, fn)
        locs += loc
        fd.close() # đóng file sau khi dùng xong để tránh leak file descriptor, có thể dùng with open(fn) để tự động làm việc này 

    print 'Total of files:', no_files
    print 'Total of lines:', locs
 
 
Credit:
http://stackoverflow.com/questions/845058/how-to-get-line-count-cheaply-in-python 

1 comment:

  1. In [1]: sum(1 for line in open('/etc/passwd'))
    => đơn giản nhưng không phải thằng nào cũng nghĩ ra (y)
    Thanks

    ReplyDelete