2009-05-31 7 views
8

Ho un file di testo enorme con 25k lines.Inside che file di testo ogni riga inizia con "1 \ t (LineNumber)"Esiste un'opzione "vai alla linea" in TextReader/StreamReader?

Esempio:

1 1 ITEM_ETC_GOLD_01 골드(소) xxx xxx xxx_TT_DESC 0 0 3 3 5 0 180000 3 0 1 0 0 255 1 1 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_money_small.bsr xxx xxx xxx 0 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1 표현할 골드의 양(param1이상) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 2 ITEM_ETC_GOLD_02 골드(중) xxx xxx xxx_TT_DESC 0 0 3 3 5 0 180000 3 0 1 0 0 255 1 1 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_money_normal.bsr xxx xxx xxx 0 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000 표현할 골드의 양(param1이상) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 3 ITEM_ETC_GOLD_03 골드(대) xxx xxx xxx_TT_DESC 0 0 3 3 5 0 180000 3 0 1 0 0 255 1 1 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_money_large.bsr xxx xxx xxx 0 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 10000 표현할 골드의 양(param1이상) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 4 ITEM_ETC_HP_POTION_01 HP 회복 약초 xxx SN_ITEM_ETC_HP_POTION_01 SN_ITEM_ETC_HP_POTION_01_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 60 0 0 0 1 21 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_01.ddj xxx xxx 50 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 120 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 5 ITEM_ETC_HP_POTION_02 HP 회복약 (소) xxx SN_ITEM_ETC_HP_POTION_02 SN_ITEM_ETC_HP_POTION_02_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 110 0 0 0 2 39 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_02.ddj xxx xxx 50 2 0 0 2 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 220 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 6 ITEM_ETC_HP_POTION_03 HP 회복약 (중) xxx SN_ITEM_ETC_HP_POTION_03 SN_ITEM_ETC_HP_POTION_03_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 200 0 0 0 4 70 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_03.ddj xxx xxx 50 2 0 0 3 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 370 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 
1 7 ITEM_ETC_HP_POTION_04 HP 회복약 (대) xxx SN_ITEM_ETC_HP_POTION_04 SN_ITEM_ETC_HP_POTION_04_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 400 0 0 0 7 140 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_04.ddj xxx xxx 50 2 0 0 4 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 570 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 

Domanda: Come faccio a leggere direttamente, per esempio, riga 5?

risposta

10

È possibile utilizzare il mio LineReader classe (sia quella in MiscUtil o un simple version here) per implementare IEnumerable<string> e quindi utilizzare LINQ:

string line5 = new LineReader(file).Skip(4).First(); 

Ciò presuppone .NET 3.5, è vero. Altrimenti, apri uno TextReader (ad esempio con File.OpenText) e chiama semplicemente ReadLine() quattro volte per saltare le righe che non vuoi, e poi ancora una volta per leggere la quinta riga.

Non c'è modo di "abbreviare" questo a meno che non si sappia esattamente quanti byte ci sono in ogni riga.

+0

Sotto il cofano che legge ancora riga per riga fino a raggiungere la linea che vuoi. C'è un modo per andare * direttamente * alla linea 5? – BFree

+2

In che modo il flusso dovrebbe conoscere l'offset di byte della riga 5 in anticipo, BFree? –

+0

C'è un motivo per cui LineReader non ha un overload del costruttore Stream, solo Func ? –

3

Se si ha a che fare con un formato di dati a larghezza fissa (cioè si conoscono tutte le linee della stessa lunghezza), è possibile moltiplicare la lunghezza con il numero di linea desiderato e utilizzare Stream.Seek per trovare il punto di partenza della linea n

Se le linee non sono di lunghezza fissa, è necessario trovare il numero corretto di interruzioni di riga finché non si è all'inizio della riga desiderata. Sarebbe più semplice farlo con StreamReader.ReadLine. (Puoi creare un metodo di estensione per rendere il file en IEnumerable <string> come suggerisce Jon Skeet - questo ti farebbe ottenere una sintassi migliore, ma sotto il cofano userai ReadLine).

Se le prestazioni sono un problema, potrebbe essere (un po ') più efficiente per la ricerca di <CR> <LF> sequenze di byte nel file manualmente utilizzando il metodo Stream.Read. Non l'ho provato; ma lo StreamReader ovviamente ha bisogno di fare del lavoro per costruire una stringa fuori dalla sequenza di byte - se non ti interessa delle prime righe, questo lavoro può essere salvato, quindi teoricamente dovresti essere in grado di fare un metodo di scansione che funzioni meglio . Questo sarebbe molto più lavoro per te, comunque.

+0

Le linee non sono lunghezze fisse, ma è possibile trovare la lunghezza di ogni riga. Tuttavia, ci vorrà del tempo come necessario leggendo ogni riga per file di grandi dimensioni con 25.000 linee. –

+0

Se si sta solo andando alla quinta riga, non è necessario leggere tutte le righe ... –

+0

Se non si conosce la lunghezza di ciascuna riga prima del tempo, non c'è altra opzione che attraversare ogni linea in un modo o nell'altro, per trovare una linea specifica. Non ci sono scorciatoie magiche. Se si tratta di un file che viene aggiunto a, e è necessario elaborare nuovi dati, è possibile memorizzare l'ultimo byte di offset tra letture, e iniziare da lì alla prossima lettura. – driis

3

Non è possibile passare direttamente a una riga in un file di testo a meno che ogni riga non sia a larghezza fissa e si stia utilizzando una codifica a larghezza fissa (ad esempio non UTF-8, che è una delle più comuni ora).

L'unico modo per farlo è leggere le righe e scartare quelle che non si desidera.

In alternativa, è possibile inserire un indice nella parte superiore del file (o in un file esterno) che indichi (ad esempio) che la riga 1000 inizia con l'offset di byte [x], la riga 2000 inizia con l'offset di byte [y ] ecc. Quindi utilizzare .Position o .Seek() sullo FileStream per spostarsi al punto indicizzato più vicino e camminare in avanti.

Supponendo l'approccio più semplice (nessun indice), il codice nell'esempio di Jon dovrebbe funzionare correttamente. Se non si desidera LINQ, si può battere fino qualcosa di simile in .NET 2.0 + C# 2.0:

// to read multiple lines in a block 
public static IEnumerable<string> ReadLines(
     string path, int lineIndex, int count) { 
    if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path"); 
    if (lineIndex < 0) throw new ArgumentOutOfRangeException("lineIndex"); 
    if (count < 0) throw new ArgumentOutOfRangeException("count"); 
    using (StreamReader reader = File.OpenText(path)) { 
     string line; 
     while (count > 0 && (line = reader.ReadLine()) != null) { 
      if (lineIndex > 0) { 
       lineIndex--; // skip 
       continue; 
      } 
      count--; 
      yield return line; 
     } 
    } 
} 
// to read a single line 
public static string ReadLine(string path, int lineIndex) { 
    foreach (string line in ReadLines(path, lineIndex, 1)) { 
     return line; 
    } 
    throw new IndexOutOfRangeException(); 
} 

Se hai bisogno di testare i valori della linea (e non solo indice di linea), che poi è facile abbastanza da fare anche basta modificare il blocco iteratore.

1

Se stai cercando molte linee diverse dal file (ma non tutte), allora potresti trarre qualche vantaggio dalla creazione di un indice mentre procedi. Usa uno qualsiasi dei suggerimenti che sono già qui, ma man mano che procedi, crea una serie di offset di byte per tutte le linee che hai già localizzato in modo da poterti salvare ogni volta dalla scansione del file dall'inizio.

APPENDICE:
C'è un altro modo è possibile farlo in fretta se avete solo bisogno della linea occasionale 'random', ma a costo di una ricerca più complessa (Se la risposta di Jon è abbastanza veloce, I' d sicuramente attenersi a quello per la semplicità).

Si può eseguire una 'ricerca binaria', iniziando a cercare a metà del file per la sequenza '1', la prima occorrenza trovata ti darà un'idea di quale numero di linea hai trovato; quindi in base a dove la linea che stai cercando è relativa al numero trovato che continui a dividere in modo ricorsivo.

Per prestazioni extra si può anche ipotizzare che le linee abbiano approssimativamente la stessa lunghezza e che l'algoritmo "indovina" la posizione approssimativa della linea che si sta cercando rispetto al numero totale di linee nel file e quindi eseguire questa ricerca da lì in poi. Se non vuoi fare supposizioni sulla lunghezza del file puoi addirittura renderlo self-prime dividendolo solo a metà, e usando il numero di riga trova prima un'approssimazione di quante linee ci sono nel file come un'intera.

Decisamente non banale da implementare, ma se si ha un sacco di accesso casuale in file con un numero elevato di linee, si può pagare in guadagni di prestazioni.

0

Se è necessario essere in grado di passare alla riga 24.000 utilizzando una funzione che ReadLine() in background sarà un po 'lento.

Se il numero di riga è alto, è possibile che si desideri fare una sorta di ipotesi su dove si trova il file e iniziare a leggere da lì. In questo modo per arrivare alla linea 24.567 non devi prima leggere 24.566 linee. Puoi saltare da qualche parte nel mezzo per scoprire in quale linea sei basato sul numero dopo/t e poi contare da lì.

Un po 'di tempo fa ho lavorato con un dev che doveva creare un DB prima di RDBMSs in comune. La sua soluzione al tuo problema era simile a quello che ho appena scritto, ma nel suo caso teneva una mappa in un file separato. La mappa può mappare ogni centesimo punto fino alla sua posizione nel documento. Una mappa come questa può essere caricata molto rapidamente e questo può aumentare i tempi di lettura. Al momento il suo sistema era molto veloce ed efficiente per dati di sola lettura ma non molto buono per i dati di lettura/scrittura. (ogni volta che cambi le linee devi cambiare l'intera mappa, non è molto efficiente)

Problemi correlati