El siguiente código crea una función Which_Line_for_Position (pos) que da la número de la línea para la posición pos, es decir el número de la línea en la que se encuentra el carácter situado en la posición pos en el archivo.
Esta función se puede utilizar con cualquier posición como argumento, independientemente del valor de la posición actual del puntero del archivo y del histórico de los movimientos de este puntero antes de llamar a la función.
Por lo tanto, con esta función, uno no se limita a determinar el número de la línea actual solo durante una iteración ininterrumpida en las líneas, como es el caso de la solución de 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 misma solución se puede escribir con la ayuda del módulo 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
Pero esta solución tiene algunos inconvenientes:
- necesita importar el módulo fileinput
- ¡borra todo el contenido del archivo! Debe haber algo mal en mi código pero no sé fileinput suficiente para encontrarlo. ¿O es un comportamiento normal de fileinput.input() función?
- Parece que el archivo se lee por completo antes de que se pueda iniciar cualquier iteración. Si es así, para un archivo muy grande, el tamaño del archivo puede exceder la capacidad de la RAM. No estoy seguro de este punto: traté de probar con un archivo de 1,5 GB, pero es bastante largo y dejé este punto por el momento. Si este punto es correcto, constituye un argumento para utilizar la otra solución con enumerate()
.
Ejemplo:
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))
resultado
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
.
Entonces, que tiene una función Which_Line_for_Position(), es fácil obtener el número de una línea de corriente: de paso f.tell() como argumento a la función
Pero ADVERTENCIA: cuando utilizando f.tell() y haciendo movimientos de puntero del archivo en el archivo, es absolutamente necesario que el archivo se abre en modo binario: 'rb' o 'rb +' o 'ab' o ...
+1, una buena solución simple ya que sólo requiere la 'open' llamada a ser cambiado. Es probable que desee proporcionar envoltorios para cualquier otra función que se utilice también (como 'cerrar'), pero deben ser funciones de transferencia bastante menores. – paxdiablo
Oh bien, 'cerrar' es útil, lo agregaré. –
Ambas soluciones funcionan genial, ¡fantástico! –