C'è un modo semplice per dire in quale numero di riga si trova un puntatore di file?
-
28-10-2019 - |
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.
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()