2012-03-09 13 views
14

Tengo un script muy simple ahora que cuenta con líneas en un archivo de texto usando enumerate():(Python) Contar líneas en un enorme (> 10 GB) presentar lo más rápido posible

i = 0 
f = open("C:/Users/guest/Desktop/file.log", "r") 
for i, line in enumerate(f): 
     pass 
print i + 1 
f.close() 

Esto toma alrededor de 3 y medio minuto para pasar por un archivo de registro de 15 GB con ~ 30 millones de líneas. Sería genial si pudiera obtener esto menos de dos minutos o menos, porque estos son registros diarios y queremos hacer un análisis mensual, por lo que el código tendrá que procesar 30 registros de ~ 15GB, más de una hora y media posiblemente , y nos gustaría minimizar el tiempo de carga de la memoria & en el servidor.

También me conformarse con un método de buena aproximación/estimación, pero tiene que ser de 4 sig fig precisa ...

Gracias!

+3

En general, probablemente sería más rápido tratar el archivo como datos binarios, leerlo en fragmentos de tamaño razonable (digamos, 4 KB a la vez) y contar los caracteres '\ n' en cada fragmento sobre la marcha. – aroth

+4

Esto no es mejor que su solución ingenua, pero fyi la forma pitónica de escribir lo que tiene aquí sería simplemente 'con abrir (fname) como f: imprimir suma (1 para la línea en f)' – wim

+1

aroth: Gracias por el consejo, debería ver eso. wim: genial, gracias, eso es mucho más corto ... – Adrienne

Respuesta

17

Ignacio's answer es correcto, pero puede fallar si tiene un proceso de 32 bits.

Pero tal vez podría ser útil leer el archivo en bloque y luego contar los caracteres \n en cada bloque.

def blocks(files, size=65536): 
    while True: 
     b = files.read(size) 
     if not b: break 
     yield b 

with open("file", "r") as f: 
    print sum(bl.count("\n") for bl in blocks(f)) 

hará su trabajo.

Tenga en cuenta que no abro el archivo como binario, por lo que el \r\n se convertirá en \n, haciendo que el recuento sea más confiable.

+1

Al igual que un punto de datos, una lectura de un archivo grande de aproximadamente 51 MB pasó de aproximadamente un minuto utilizando el enfoque ingenuo a menos de un segundo con este enfoque. –

+3

@MKatz ¿Qué pasa ahora, "un archivo grande" o "un archivo de aproximadamente 51 MB"?;-) – glglgl

+0

esta solución podría perderse la última línea, pero eso podría no importar para un archivo enorme. –

5

mmap el archivo y cuente las nuevas líneas.

6

Sé que es un poco injusto, pero se podía hacer esto

int(subprocess.check_output("wc -l C:\\alarm.bat").split()[0]) 

Si su en las ventanas Coreutils

+0

Mi solución solo requiere 1m37 en tiempo real. –

+1

esto es mucho más rápido –

+0

Parece que tiene que hacer 'int (subprocess.check_output ("/usr/bin/wc -l cred ", shell = True) .split() [0])' para python3 – ZN13

0

una solución, 1 línea rápida es:

sum((1 for i in open(file_path, 'rb'))) 

Se debe trabajar en archivos de tamaño arbitrario.

1

me gustaría extender la respuesta de GL y ejecutar su/su código usando el módulo de multiprocesamiento Python para el conteo rápido:

def blocks(f, cut, size=64*1024): # 65536 
    start, chunk =cut 
    iter=0 
    read_size=int(size) 
    _break =False 
    while not _break: 
     if _break: break 
     if f.tell()+size>start+chunk: 
      read_size=int(start+chunk- f.tell()) 
      _break=True 
     b = f.read(read_size) 
     iter +=1 
     if not b: break 
     yield b 


def get_chunk_line_count(data): 
    fn, chunk_id, cut = data 
    start, chunk =cut 
    cnt =0 
    last_bl=None 

    with open(fn, "r") as f: 
     if 0: 
      f.seek(start) 
      bl = f.read(chunk) 
      cnt= bl.count('\n') 
     else: 
      f.seek(start) 
      for i, bl in enumerate(blocks(f,cut)): 
       cnt += bl.count('\n') 
       last_bl=bl 

     if not last_bl.endswith('\n'): 
      cnt -=1 

     return cnt 
.... 
pool = multiprocessing.Pool(processes=pool_size, 
          initializer=start_process, 
          ) 
pool_outputs = pool.map(get_chunk_line_count, inputs) 
pool.close() # no more tasks 
pool.join() 

Esto mejorará el rendimiento contando 20 pliegues. Lo envolví en un script y lo puse en Github.

Cuestiones relacionadas