Python'daki büyük metin dosyalarını belleğe yüklemeden satır satır nasıl okuyabilirim?


239

Büyük bir dosyayı satır satır okumam gerekiyor. Diyelim ki dosya 5GB'den fazla ve her satırı okumalıyım, ama açıkçası kullanmak istemiyorum readlines()çünkü bellekte çok büyük bir liste oluşturacak.

Aşağıdaki kod bu durum için nasıl çalışacak? Is xreadlineskendisi belleğe birer birer okuma? Jeneratör ifadesi gerekli mi?

f = (line for line in open("log.txt").xreadlines())  # how much is loaded in memory?

f.next()  

Ayrıca, bunu Linux tailkomutu gibi ters sırada okumak için ne yapabilirim ?

Buldum:

http://code.google.com/p/pytailer/

ve

" python kafası, kuyruğu ve geriye doğru bir metin dosyasının satırları tarafından okunur "

Her ikisi de çok iyi çalıştı!


Ve bunu kuyruktan okumak için ne yapabilirim? son satırdan başlayarak satır satır.
Bruno Rocha - rochacbruno

Bu ayrı bir soru olmalı
cmcginty

Yanıtlar:


311

Bu yanıtı sağladım çünkü Keith's özlü olsa da dosyayı açıkça kapatmıyor

with open("log.txt") as infile:
    for line in infile:
        do_something_with(line)

31
soru hala, "infile in line" 5GB satırlarımı belleğe yükleyecek? ve, Kuyruktan nasıl okuyabilirim?
Bruno Rocha - rochacbruno

67
@rochacbruno, her seferinde yalnızca bir satır okur. Bir sonraki satır okunduğunda, başka bir yerde referans göndermediğiniz sürece önceki satır çöp toplanır
John La Rooy

1
@rochacbruno, Satırları ters sırada okumak maalesef verimli bir şekilde yapmak o kadar kolay değil. Genellikle dosyanın sonundan mantıklı boyutta parçalar (kilobayt ila megabayt diyelim) okumak ve satırsonu karakterleri (veya platformda satır sonu karakteri ne olursa olsun) bölmek istersiniz
John La Rooy

4
Teşekkürler! Kuyruk çözümünü buldum stackoverflow.com/questions/5896079/…
Bruno Rocha - rochacbruno

1
@bawejakunal, Bir hattın aynı anda belleğe yüklenemeyecek kadar uzun olduğunu mu kastediyorsunuz? Bu bir metin dosyası için alışılmadık bir durumdur . forSatırlar üzerinde yinelenen döngü kullanmak yerine, chunk = infile.read(chunksize)içeriklerinden bağımsız olarak sınırlı boyutlu parçaları okumak için kullanabilirsiniz . Yeni satırları kendi başınıza aramak zorundasınız.
John La Rooy

60

Tek yapmanız gereken dosya nesnesini yineleyici olarak kullanmaktır.

for line in open("log.txt"):
    do_something_with(line)

Daha da iyisi, son Python sürümlerinde bağlam yöneticisini kullanmaktır.

with open("log.txt") as fileobject:
    for line in fileobject:
        do_something_with(line)

Bu, dosyayı otomatik olarak da kapatacaktır.


2
Bu, tüm dosyayı belleğe yüklemiyor mu?
Bruno Rocha - rochacbruno

17

Eski bir okul yaklaşımı:

fh = open(file_name, 'rt')
line = fh.readline()
while line:
    # do stuff with line
    line = fh.readline()
fh.close()

2
küçük açıklama: istisna güvenliği için fh olarak "with" ifadesi, sizin durumunuzda "open (dosya adı, 'rt') ile kullanılması
önerilir

16
@prokher: Evet, ama ben buna "eski okul" dedim.
PTBNL

15

Bunun yerine bir yineleyici kullanmak daha iyidir. İlgili: http://docs.python.org/library/fileinput.html

Dokümanlardan:

import fileinput
for line in fileinput.input("filename"):
    process(line)

Bu, tüm dosyayı bir kerede belleğe kopyalamaktan kaçınacaktır.


Dokümanlar snippet'i "tipik kullanım" olarak göstermesine rağmen, döngü bittiğinde bunu kullanarak close()döndürülen FileInputsınıf nesnesinin yöntemini çağırmaz - bu yüzden bu şekilde kullanmaktan kaçınırım. Python 3.2'de nihayet fileinputbu sorunu ele alan bağlam yöneticisi protokolü ile uyumlu hale getirdiler (ancak kod hala gösterilen şekilde yazılmayacaktı).
martineau

7

Dosyada yeni satırlar yoksa yapmanız gerekenler:

with open('large_text.txt') as f:
  while True:
    c = f.read(1024)
    if not c:
      break
    print(c)

Bu yöntemi sevdiğim halde, metninizde satırın parçalara ayrılma riski taşıyorsunuz. Bunu şahsen gördüm, yani dosyada benim gibi sstring arıyorsanız, bazılarını özleyeceğim, çünkü bulundukları çizgi parçalar halinde kesildi. Bunu aşmanın bir yolu var mı? Ariel Cabib
edo101

6

Lütfen şunu deneyin:

with open('filename','r',buffering=100000) as f:
    for line in f:
        print line

lütfen açıkla?
Nikhil VJ

3
Python'un resmi docmunet'lerinden: link İsteğe bağlı arabelleğe alma argümanı dosyanın istenen tampon boyutunu belirtir: 0 arabelleğe alınmamış, 1 satır arabelleğe alınmış, diğer herhangi bir pozitif değer anlamına gelir (yaklaşık olarak) bu boyutta (bayt cinsinden) bir tampon kullanın. Negatif bir arabelleğe alma, varsayılan olarak tty aygıtları için satır arabelleğe alınan ve diğer dosyalar için tamamen arabelleğe alınan sistem varsayılanını kullanmak anlamına gelir. Atlanırsa, sistem varsayılanı kullanılır
jyoti das

Benim durumumda, benim durumumda, iki dosya işleyicileri (bir okuma, diğer yazma)> 4gb dosyaları ile python asılı idi ve şimdi iyi! Teşekkürler.
Xelt

@jyotidas Bu yöntemi sevdiğim halde, metninizde satırın parçalara bölünme riski taşıyorsunuz. Bunu şahsen gördüm, yani dosyada benim gibi sstring arıyorsanız, bazılarını özleyeceğim, çünkü bulundukları çizgi parçalar halinde kesildi. Bunu aşmanın bir yolu var mı? Ben yanlış
sayılar

3

@ John-la-rooy'un cevabının göründüğü kadar kolay olabileceğine inanamadım. Bu yüzden, cpkomut satır satır okuma ve yazma kullanarak yeniden oluşturdum . ÇILGIN HIZLI.

#!/usr/bin/env python3.6

import sys

with open(sys.argv[2], 'w') as outfile:
    with open(sys.argv[1]) as infile:
        for line in infile:
            outfile.write(line)

Not: Python'ın readlinesatır sonlarını standartlaştırdığı için, bunun DOS satır sonları ile belgeleri \r\nUnix satır sonlarına dönüştürme yan etkisi vardır \n. Bu konuyu araştırmak için tüm nedenim (satır geliştirici körü körüne çeşitli .NET kütüphaneleri kullandığı için) satır sonları alır bir günlük dosyasını dönüştürmek için gerekli oldu. İlk hız testimden sonra, geri dönmem ve rstripçizgilere gitmem gerekmediğini görünce şok oldum . Zaten mükemmeldi!
Bruno Bronosky

2

Yangını proje son 6 yılda uzun bir yol kat etti. Panda özelliklerinin kullanışlı bir alt kümesini kapsayan basit bir API'ye sahiptir.

dask.dataframe , dahili olarak parçalanmaya özen gösterir, birçok paralelleştirilebilir işlemi destekler ve bellek içi işlemler için dilimleri kolayca pandalara geri vermenizi sağlar.

import dask.dataframe as dd

df = dd.read_csv('filename.csv')
df.head(10)  # return first 10 rows
df.tail(10)  # return last 10 rows

# iterate rows
for idx, row in df.iterrows():
    ...

# group by my_field and return mean
df.groupby(df.my_field).value.mean().compute()

# slice by column
df[df.my_field=='XYZ'].compute()

2

Bellek sorunlarına neden olmadan herhangi bir boyuttaki metin dosyalarını yüklemek için kod heres. Gigabyte boyutlu dosyaları destekler

https://gist.github.com/iyvinjose/e6c1cb2821abd5f01fd1b9065cbc759d

data_loading_utils.py dosyasını indirin ve kodunuza aktarın

kullanım

import data_loading_utils.py.py
file_name = 'file_name.ext'
CHUNK_SIZE = 1000000


def process_lines(data, eof, file_name):

    # check if end of file reached
    if not eof:
         # process data, data is one single line of the file

    else:
         # end of file reached

data_loading_utils.read_lines_from_file_as_data_chunks(file_name, chunk_size=CHUNK_SIZE, callback=self.process_lines)

process_lines yöntemi geri çağırma işlevidir. Tüm satırlar için çağrılır ve parametre verileri aynı anda tek bir satırı temsil eder.

Makine donanım yapılandırmanıza bağlı olarak CHUNK_SIZE değişkenini yapılandırabilirsiniz.


Bu yöntemi sevdiğim halde, metninizde satırın parçalara ayrılma riski taşıyorsunuz. Bunu şahsen gördüm, yani dosyada benim gibi sstring arıyorsanız, bazılarını özleyeceğim, çünkü bulundukları çizgi parçalar halinde kesildi. Bunu aşmanın bir yolu var mı? Ben yanlış
sayılar

0

Buna ne dersin? Dosyanızı parçalara ayırın ve satır satır okuyun, çünkü bir dosyayı okuduğunuzda işletim sisteminiz bir sonraki satırı önbelleğe alır. Dosyayı satır satır okuyorsanız, önbelleğe alınan bilgileri verimli kullanmıyorsunuzdur.

Bunun yerine, dosyayı parçalar halinde bölün ve tüm yığını belleğe yükleyin ve ardından işleminizi yapın.

def chunks(file,size=1024):
    while 1:

        startat=fh.tell()
        print startat #file's object current position from the start
        fh.seek(size,1) #offset from current postion -->1
        data=fh.readline()
        yield startat,fh.tell()-startat #doesnt store whole list in memory
        if not data:
            break
if os.path.isfile(fname):
    try:
        fh=open(fname,'rb') 
    except IOError as e: #file --> permission denied
        print "I/O error({0}): {1}".format(e.errno, e.strerror)
    except Exception as e1: #handle other exceptions such as attribute errors
        print "Unexpected error: {0}".format(e1)
    for ele in chunks(fh):
        fh.seek(ele[0])#startat
        data=fh.read(ele[1])#endat
        print data

Bu umut verici görünüyor. Bu yükleme bayt veya satır mı? Baytlarsa satırların kırılmasından korkuyorum .. Bir seferde 1000 satır söyleyip nasıl işleyebiliriz?
Nikhil VJ

0

Teşekkür ederim! Son zamanlarda python 3'e dönüştürdüm ve büyük dosyaları okumak için readlines (0) kullanarak hayal kırıklığına uğradım. Bu sorunu çözdü. Ama her çizgiyi elde etmek için birkaç adım daha atmam gerekiyordu. Her satırın başında "b" harfi var. İkili biçimde olduğunu düşünüyorum. "Decode (utf-8)" kullanarak ascii değiştirdi.

Sonra her satırın ortasında bir "= \ n" kaldırmak zorunda kaldı.

Sonra çizgileri yeni çizgiye böldüm.

b_data=(fh.read(ele[1]))#endat This is one chunk of ascii data in binary format
        a_data=((binascii.b2a_qp(b_data)).decode('utf-8')) #Data chunk in 'split' ascii format
        data_chunk = (a_data.replace('=\n','').strip()) #Splitting characters removed
        data_list = data_chunk.split('\n')  #List containing lines in chunk
        #print(data_list,'\n')
        #time.sleep(1)
        for j in range(len(data_list)): #iterate through data_list to get each item 
            i += 1
            line_of_data = data_list[j]
            print(line_of_data)

İşte Arohi'nin kodundaki "yazdırma verisi" nin hemen üstünde başlayan kod.


0

Bu diğer soruda paralel bir bayt düzeyinde rasgele erişim yaklaşımı gösterdim:

Okuma satırları olmayan bir metin dosyasındaki satır sayısını alma

Daha önce verilen cevapların bazıları güzel ve özlü. Bazılarını seviyorum. Ancak bu gerçekten dosyadaki verilerle ne yapmak istediğinize bağlıdır. Benim durumumda, sadece büyük metin dosyalarında olabildiğince hızlı satır saymak istedim. Kodum, herhangi bir kod gibi, elbette başka şeyler yapmak için değiştirilebilir.


0

Bu konuda bulduğum en iyi çözüm ve 330 MB dosyada denedim.

lineno = 500
line_length = 8
with open('catfour.txt', 'r') as file:
    file.seek(lineno * (line_length + 2))
    print(file.readline(), end='')

Burada line_length, tek bir satırdaki karakter sayısıdır. Örneğin "abcd" satır uzunluğu 4'e sahiptir.

'\ N' karakterini atlamak ve bir sonraki karaktere geçmek için 2 satır uzunluğunda ekledim.


-1

Paralel olarak çalışmak ve yalnızca veri parçalarını okumak, ancak yeni satırlarla temiz tutmak istediğinizde bu yararlı olabilir.

def readInChunks(fileObj, chunkSize=1024):
    while True:
        data = fileObj.read(chunkSize)
        if not data:
            break
        while data[-1:] != '\n':
            data+=fileObj.read(1)
        yield data

-10
f=open('filename','r').read()
f1=f.split('\n')
for i in range (len(f1)):
    do_something_with(f1[i])

Bu yardımcı olur umarım.


5
Bu, bellekteki tüm dosyayı okumaz mı? Soru açıkça bundan nasıl kaçınılacağını soruyor, bu nedenle bu soruya cevap vermiyor.
Fermi paradoksu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.