2010-08-22 12 views
26

Esiste un metodo incorporato per farlo? In caso contrario, come posso farlo senza costi eccessivi?Come si legge una riga a caso da un file in python?

+0

@Greg Questo è Perl, non Python – quantumSoup

+2

@quantumSoup: la domanda usa Perl nei suoi esempi, ma la domanda è indipendente dal linguaggio. Le risposte più utili usano lo pseudocodice, facilmente traducibile nella tua lingua preferita. –

+0

Grazie, ho trovato molto aiuto anche questo: http://mail.python.org/pipermail/tutor/2007-July/055635.html Devi leggerli in memoria però. – Shane

risposta

51

Non built-in, ma l'algoritmo R(3.4.2) ("Algoritmo Reservoir" di Waterman) dal Knuth di "The Art of Computer Programming" è buono (in una versione molto semplificata):

import random 

def random_line(afile): 
    line = next(afile) 
    for num, aline in enumerate(afile): 
     if random.randrange(num + 2): continue 
     line = aline 
    return line 

Il num + 2 produce la sequenza 2, 3, 4 ... Il randrange sarà quindi 0 con una probabilità di 1.0/(num + 2) - e questa è la probabilità con cui dobbiamo sostituire la linea attualmente selezionata (il caso speciale della dimensione del campione 1 dell'algoritmo di riferimento - vedere Il libro di Knuth per la dimostrazione di correttezza == e naturalmente siamo anche nel caso di un "serbatoio" abbastanza piccolo da stare nella memoria; -) ... e precisamente la probabilità con che noi do così.

+1

+1 per la traduzione da MIX a Python – aaronasterling

+2

Questo è il campionamento del serbatoio, giusto? – HenryR

+6

Ho sempre pensato che la funzione 'random.choice()' dovrebbe funzionare su iteratori arbitrari e sequenze, implementando esattamente l'algoritmo di cui sopra. –

1

Cercare una posizione casuale, leggere una riga e scartarla, quindi leggere un'altra riga. La distribuzione delle linee non sarà normale, ma non sempre è importante.

+3

In particolare, ciò rende impossibile selezionare sempre la prima riga (oltre a selezionare altre linee con una probabilità proporzionale alla lunghezza di ciascuna riga precedente). Anche la mia A non produce una distribuzione normale (sarebbe strano - che cosa significa, quale varianza ?!), ma una uniforme, che sembra un po 'più probabile che soddisfi il significato dell'OP per "random". –

+0

Per superare il problema indicato da @AlexMartelli, scegliere la prima riga nel caso in cui la ricerca casuale porti all'ultima riga. Ma un altro problema qui è che una linea che ha relativamente più parole per altre linee avrà maggiori probabilità di essere selezionata. –

9

Dipende da cosa intendi per "troppo" sovraccarico. Se è possibile memorizzare un intero file in memoria, qualcosa come

import random 

random_lines = random.choice(open("file").readlines()) 

farebbe il trucco.

23
import random 
lines = open('file.txt').read().splitlines() 
myline =random.choice(lines) 
print(myline) 

Per file molto lungo: cercano di posto a caso nel file basato su di essa la lunghezza e trovare due caratteri di nuova riga dopo la posizione (o di nuova riga e la fine del file). Ripeti 100 caratteri prima o dall'inizio del file se la posizione di ricerca originale era < 100 se finivamo all'interno dell'ultima riga.

Tuttavia questa è finita complicato, come file è iterator.So renderlo lista e prendere random.choice (se avete bisogno di molti, l'uso random.sample):

import random 
print(random.choice(list(open('file.txt')))) 
+9

Se l'attività è di leggere solo una riga, non ha senso caricare il file completo in memoria. – iankit

6

Anche se sono quattro anni di ritardo, Penso di avere la soluzione più veloce. Recentemente ho scritto un pacchetto python chiamato linereader, che consente di manipolare i puntatori degli handle di file.

Qui è la soluzione semplice per ottenere una riga a caso con questo pacchetto:

from random import randint 
from linereader import dopen 

length = #lines in file 
filename = #directory of file 

file = dopen(filename) 
random_line = file.getline(randint(1, length)) 

La prima volta che questo viene fatto è il peggiore, come linereader deve compilare il file di output in un formato speciale. Al termine, il linereader può accedere rapidamente a qualsiasi riga del file, indipendentemente dalle dimensioni del file.

Se il file è molto piccolo (abbastanza piccolo da contenere un MB), è possibile sostituire dopen con copen e memorizza una voce memorizzata nella cache del file. Non solo è più veloce, ma ottieni il numero di linee all'interno del file mentre viene caricato in memoria; è fatto per te Tutto quello che devi fare è generare il numero di riga casuale. Ecco alcuni esempi di codice per questo.

from random import randint 
from linereader import copen 

file = copen(filename) 
lines = file.count('\n') 
random_line = file.getline(randint(1, lines)) 

mi ha regalato molto contento perché ho visto qualcuno che potrebbe trarre beneficio dal mio pacchetto! Ci scusiamo per la risposta senza risposta, ma il pacchetto potrebbe sicuramente essere applicato a molti altri problemi.

+0

Ho avuto la riga di errore ValueError. non trovato, ma linea no. era inferiore alle dimensioni del file. – kakarukeys

2

Se non si desidera leggere l'intero file, è possibile cercare nel mezzo del file, quindi cercare all'indietro la nuova riga e chiamare readline.

Ecco uno script python3 che fa solo questo,

Uno svantaggio di questo metodo è brevi linee hanno una minore probabilita di presentarsi.

def read_random_line(f, chunk_size=16): 
    import os 
    import random 
    with open(f, 'rb') as f_handle: 
     f_handle.seek(0, os.SEEK_END) 
     size = f_handle.tell() 
     i = random.randint(0, size) 
     while True: 
      i -= chunk_size 
      if i < 0: 
       chunk_size += i 
       i = 0 
      f_handle.seek(i, os.SEEK_SET) 
      chunk = f_handle.read(chunk_size) 
      i_newline = chunk.rfind(b'\n') 
      if i_newline != -1: 
       i += i_newline + 1 
       break 
      if i == 0: 
       break 
     f_handle.seek(i, os.SEEK_SET) 
     return f_handle.readline() 
0

È possibile aggiungere le righe in un set() che cambierà il loro ordine casuale.

filename=open("lines.txt",'r') 
f=set(filename.readlines()) 
filename.close() 

Per trovare la prima riga:

print(next(iter(f))) 

Per trovare la linea 3:

print(list(f)[2]) 

per elencare tutte le linee del set:

for line in f: 
    print(line) 
0

Questo potrebbe essere ingombrante, ma funziona credo? (almeno per i file txt)

import random 
choicefile=open("yourfile.txt","r") 
linelist=[] 
for line in choicefile: 
    linelist.append(line) 
choice=random.choice(linelist) 
print(choice) 

Legge ogni riga di un file e lo aggiunge a un elenco. Quindi sceglie una linea casuale dall'elenco. Se si desidera rimuovere la linea una volta che è scelto, solo che

linelist.remove(choice) 

Spero che questo può aiutare, ma almeno nessuno moduli aggiuntivi e le importazioni (a parte casuale) e relativamente leggeri.

Problemi correlati