Domanda

In Python 2.5, sto leggendo un file di dati di testo strutturato (di dimensioni ~ 30 MB) usando un puntatore del file:

fp = open('myfile.txt', 'r')
line = fp.readline()
# ... many other fp.readline() processing steps, which
# are used in different contexts to read the structures

Ma poi, mentre analizzavo il file, ho raggiunto qualcosa di interessante di cui voglio segnalare il numero di riga, in modo da poter indagare sul file in un editor di testo. posso usare fp.tell() per dirmi dove si trova l'offset del byte (ad es. 16548974L), ma non esiste "fp.tell_line_number ()" per aiutarmi a tradurre questo in un numero di riga.

Esiste un Python integrato o un'estensione per tracciare e "dire" in quale numero di riga è acceso un puntatore del file di testo?

Nota: sono non chiedo Per usare un line_number += 1 contatore di stile, come chiamo io fp.readline() In contesti diversi e quell'approccio richiederebbe un maggiore debug di quanto valga la pena inserire il contatore negli angoli giusti del codice.

È stato utile?

Soluzione

Una soluzione tipica a questo problema è definire una nuova classe che avvolge un'istanza esistente di a file, che conta automaticamente i numeri. Qualcosa di simile a questo (appena fuori dalla mia testa, non l'ho testato):

class FileLineWrapper(object):
    def __init__(self, f):
        self.f = f
        self.line = 0
    def close(self):
        return self.f.close()
    def readline(self):
        self.line += 1
        return self.f.readline()
    # to allow using in 'with' statements 
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

Usalo in questo modo:

f = FileLineWrapper(open("myfile.txt", "r"))
f.readline()
print(f.line)

Sembra il modulo standard fileinput fa più o meno la stessa cosa (e anche altre cose); Potresti usarlo invece se vuoi.

Altri suggerimenti

Potresti trovare il fileinput Modulo utile. Fornisce un'interfaccia generalizzata per iterarsi su un numero arbitrario di file. Alcuni momenti salienti pertinenti dai documenti:

fileinput.lineno()

Restituisce il numero di linea cumulativo della linea che è appena stato letto. Prima che la prima riga sia stata letta, restituisce 0. Dopo che l'ultima riga dell'ultima file è stata letta, restituisce il numero di riga di quella riga.

fileinput.filelineno()

Restituisci il numero di riga nel file corrente. Prima che la prima riga sia stata letta, restituisce 0. Dopo che l'ultima riga dell'ultima file è stata letta, restituisce il numero di riga di quella riga all'interno del file.

Il seguente codice stamperà il numero di riga (in cui il puntatore è attualmente acceso) mentre attraversa il file ('Testfile')

file=open("testfile", "r")
for line_no, line in enumerate(file):
    print line_no     # The content of the line is in variable 'line'
file.close()

produzione:

1
2
3
...

Non credo, non nel modo in cui desideri (come in una funzione standard integrata delle maniglie dei file Python restituite da open).

Se non sei suscettibile di monitorare manualmente il numero di linea mentre leggi le righe o l'uso di una classe wrapper (eccellenti suggerimenti di Gregh e Senderle, a proposito), allora penso che dovrai semplicemente usare il fp.tell() Figura e torna all'inizio del file, leggendo fino a quando non ci arrivi.

Questo non è anche Un'opzione negativa poiché presumo che le condizioni di errore saranno meno probabili di tutto ciò che funziona a nuoto. Se tutto funziona bene, non c'è impatto.

Se c'è un errore, allora hai lo sforzo extra di salvare il file. Se il file è grande, Ciò può influire sulle prestazioni percepite: dovresti tenerne conto se è un problema.

Un modo potrebbe essere quello di iterare oltre la linea e mantenere un conteggio esplicito del numero di righe già visto:

>>> f=open('text.txt','r')
>>> from itertools import izip
>>> from itertools import count
>>> f=open('test.java','r')
>>> for line_no,line in izip(count(),f):
...     print line_no,line

Il seguente codice crea una funzione Wha_line_for_position (POS) che dà il Numero della linea per la posizione pos, questo è per dire il Numero di linea in cui si trova il personaggio situato in posizione pos nel file.

Questa funzione può essere utilizzata con qualsiasi posizione come argomento, indipendentemente dal valore della posizione attuale del puntatore del file e dallo storico dei movimenti di questo puntatore prima che venga chiamata la funzione.

Quindi, con questa funzione, non si limita a determinare il numero della linea corrente solo durante un'iterazione ininterrotta sulle linee, in quanto è il caso della soluzione di Greg Hewgill.

with open(filepath,'rb') as f:
    GIVE_NO_FOR_END = {}
    end = 0
    for i,line in enumerate(f):
        end += len(line)
        GIVE_NO_FOR_END[end] = i
    if line[-1]=='\n':
        GIVE_NO_FOR_END[end+1] = i+1
    end_positions = GIVE_NO_FOR_END.keys()
    end_positions.sort()

def Which_Line_for_Position(pos,
                            dic = GIVE_NO_FOR_END,
                            keys = end_positions,
                            kmax = end_positions[-1]):
    return dic[(k for k in keys if pos < k).next()] if pos<kmax else None

.

La stessa soluzione può essere scritta con l'aiuto del modulo fileinput:

import fileinput

GIVE_NO_FOR_END = {}
end = 0
for line in fileinput.input(filepath,'rb'):
    end += len(line)
    GIVE_NO_FOR_END[end] = fileinput.filelineno()
if line[-1]=='\n':
    GIVE_NO_FOR_END[end+1] = fileinput.filelineno()+1
fileinput.close()

end_positions = GIVE_NO_FOR_END.keys()
end_positions.sort()

def Which_Line_for_Position(pos,
                            dic = GIVE_NO_FOR_END,
                            keys = end_positions,
                            kmax = end_positions[-1]):
    return dic[(k for k in keys if pos < k).next()] if pos<kmax else None

Ma questa soluzione ha alcuni inconvenienti:

  • Deve importare il modulo fileinput
  • Elimina tutto il contenuto del file !! Ci deve essere qualcosa di sbagliato nel mio codice ma non lo so fileinput abbastanza per trovarlo. O è un comportamento normale di fileInput.input () funzione ?
  • Sembra che il file venga letta per la prima volta prima che possa essere lanciata qualsiasi iterazione. In tal caso, per un file molto grande, le dimensioni del file possono superare la capacità della RAM. Non sono sicuro di questo punto: ho provato a testare con un file di 1,5 GB ma è piuttosto lungo e ho lasciato cadere questo punto per il momento. Se questo punto è giusto, costituisce un argomento con cui utilizzare l'altra soluzione con enumerare()

.

Esemple:

text = '''Harold Acton (1904–1994)
Gilbert Adair (born 1944)
Helen Adam (1909–1993)
Arthur Henry Adams (1872–1936)
Robert Adamson (1852–1902)
Fleur Adcock (born 1934)
Joseph Addison (1672–1719)
Mark Akenside (1721–1770)
James Alexander Allan (1889–1956)
Leslie Holdsworthy Allen (1879–1964)
William Allingham (1824/28-1889)
Kingsley Amis (1922–1995)
Ethel Anderson (1883–1958)
Bruce Andrews (born 1948)
Maya Angelou (born 1928)
Rae Armantrout (born 1947)
Simon Armitage (born 1963)
Matthew Arnold (1822–1888)
John Ashbery (born 1927)
Thomas Ashe (1836–1889)
Thea Astley (1925–2004)
Edwin Atherstone (1788–1872)'''


#with open('alao.txt','rb') as f:

f = text.splitlines(True)
# argument True in splitlines() makes the newlines kept

GIVE_NO_FOR_END = {}
end = 0
for i,line in enumerate(f):
    end += len(line)
    GIVE_NO_FOR_END[end] = i
if line[-1]=='\n':
    GIVE_NO_FOR_END[end+1] = i+1
end_positions = GIVE_NO_FOR_END.keys()
end_positions.sort()


print '\n'.join('line %-3s  ending at position %s' % (str(GIVE_NO_FOR_END[end]),str(end))
                for end in end_positions)

def Which_Line_for_Position(pos,
                            dic = GIVE_NO_FOR_END,
                            keys = end_positions,
                            kmax = end_positions[-1]):
    return dic[(k for k in keys if pos < k).next()] if pos<kmax else None

print
for x in (2,450,320,104,105,599,600):
    print 'pos=%-6s   line %s' % (x,Which_Line_for_Position(x))

risultato

line 0    ending at position 25
line 1    ending at position 51
line 2    ending at position 74
line 3    ending at position 105
line 4    ending at position 132
line 5    ending at position 157
line 6    ending at position 184
line 7    ending at position 210
line 8    ending at position 244
line 9    ending at position 281
line 10   ending at position 314
line 11   ending at position 340
line 12   ending at position 367
line 13   ending at position 393
line 14   ending at position 418
line 15   ending at position 445
line 16   ending at position 472
line 17   ending at position 499
line 18   ending at position 524
line 19   ending at position 548
line 20   ending at position 572
line 21   ending at position 600

pos=2        line 0
pos=450      line 16
pos=320      line 11
pos=104      line 3
pos=105      line 4
pos=599      line 21
pos=600      line None

.

Quindi, avere una funzione Wha_line_for_position () , è facile ottenere il numero di una linea corrente: solo il passaggio f.tell () Come argomento alla funzione

Ma AVVERTIMENTO: quando si usa f.tell () E facendo movimenti del puntatore del file nel file, è assolutamente necessario che il file sia aperto in modalità binaria: 'RB' o 'rb+' o 'AB' o ....

Di recente, scherzare con un problema simile e ha trovato questa soluzione basata sulla classe.

class TextFileProcessor(object):

    def __init__(self, path_to_file):
        self.print_line_mod_number = 0
        self.__path_to_file = path_to_file
        self.__line_number = 0

    def __printLineNumberMod(self):
        if self.print_line_mod_number != 0:
            if self.__line_number % self.print_line_mod_number == 0:
                print(self.__line_number)

    def processFile(self):
        with open(self.__path_to_file, 'r', encoding='utf-8') as text_file:
            for self.__line_number, line in enumerate(text_file, start=1):
                self.__printLineNumberMod()

                # do some stuff with line here.

Impostare il print_line_mod_number Proprietà alla cadenza che desideri registrarsi e quindi chiamare processFile.

Ad esempio ... se vuoi feedback ogni 100 righe sembrerebbe così.

tfp = TextFileProcessor('C:\\myfile.txt')
tfp.print_line_mod_number = 100
tfp.processFile()

L'output della console sarebbe

100
200
300
400
etc...

Per quanto riguarda la Soluzione di @eyquem, Suggerisco di usare mode='r' con il modulo FileInput e fileinput.lineno() opzione e ha funzionato per me.

Ecco come sto implementando queste opzioni nel mio codice.

    table=fileinput.input('largefile.txt',mode="r")
    if fileinput.lineno() >= stop : # you can disregard the IF condition but I am posting to illustrate the approach from my code.
           temp_out.close()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top