2011-01-10 13 views
7

ho una regex complessa, e mi piacerebbe corrispondere con il contenuto di un intero file enorme. La preoccupazione principale è l'efficienza, dal momento che il file è davvero molto grande e l'esaurimento della memoria è una possibilità distinta.mi piacerebbe applicare un'espressione regolare in modo efficiente per un intero file

C'è un senso che posso in qualche modo "tamponare" i contenuti, mentre il pompaggio attraverso un matcher regex?

risposta

6

Sì, Pattern.match() avrà un CharSequence.

Se l'input è già in un set di caratteri che utilizza esattamente 2 byte per rappresentare un carattere senza alcuna 'prologue', è necessario solo:

ByteBuffer bb = ...; // acquire memory mapped byte buffer 
CharBuffer cb = bb.asCharBuffer(); // get a char[] 'view' of the bytes 

... e dato CharBuffer implementa CharSequence, sei fatto.

D'altra parte, se è necessario per decodificare i byte in qualche altro charset, avrete il vostro bel da fare, dal momento che è CharBuffer charset-agnostico, e CharsetDecorder.decode(ByteBuffer) assegna internamente un nuovo CharBuffer o meno le stesse dimensioni della input byte.

O se non sarete in grado di farla franca con un buffer più piccolo dipende un bel po 'sul vostro regex e che cosa si vuole fare con i risultati delle partite. Ma l'approccio di base sarebbe quella di implementare CharSequence e avvolgere il memory-mapped ByteBuffer, una più piccola per CharBuffer 'spazio di lavoro', e un CharsetDecoder. Utilizzerai Charset.decode(ByteBuffer,CharBuffer,boolean) per decodificare i byte "su richiesta" e sperare che la direzione generale del regex matcher sia "forward" e che l'input a cui sei interessato sia contenuto in blocchi piuttosto piccoli.

Come un inizio difficile:

class MyCharSequence implements CharSequence { 

    public MyCharSequence(File file, Charset cs, int bufferSize) throws IOException { 

     FileInputStream input = new FileInputStream(file); 
     FileChannel channel = input.getChannel(); 
     this.fileLength = (int) channel.size(); 
     this.bytes = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength); 
     this.charBuffer = CharBuffer.allocate(bufferSize); 
     this.decoder = cs.newDecoder(); 

    } 

    public int length() { 
     // ouch! have to decode the lot, even if you don't choose to keep it all handy 
    } 

    public char charAt(final int index) { 
     while (/* not yet decoded target char[] */) { 
      this.decoder.decode(this.bytes, this.charBuffer, true); 
     } 
     // don't assume 2-bytes == a char unless that's true for your charset! 
    } 

    public CharSequence subSequence(final int start, final int end) { 
     // this'll be fun, too 
    } 

    private long fileLength; 
    private MappedByteBuffer bytes; 
    private CharBuffer charBuffer; 
    private CharsetDecoder decoder; 

} 

Potrebbe essere istruttivo per avvolgere una completamente decodificati CharBuffer in una molto più semplice CharSequence involucro della propria, e log come i metodi sono effettivamente chiamati per la vostra data di ingresso, quando lo esegui con un grande heap nella tua casella di sviluppo. Questo ti darà un'idea se questo approccio funzionerà per il tuo particolare scenario.

+0

Ho appena scritto una risposta simile e ho visto che l'avevate già pubblicato! – AlexR

+2

Devi essere veloce :-) Inoltre, [http://java.sun.com/developer/technicalArticles/releases/nio/] è utile, specialmente la sezione su "File mappati". –

+0

Ok, quindi posso ottenere un MappedByteBuffer per il file, e quindi fare qualcosa come Charset.defaultCharset(). NewDecoder(). Decode (buffer) - ma questo non creerà una copia del buffer? – Jake

0

Non so Java, ma non si prevede che corrispondono l'intero contenuto del file come /^.+$/?
Oppure il file si rompe in blocchi in base alla regex ma non sai dove?
I motori Regex sono divertenti, se può fare file mappati in memoria, sarebbe un buon inizio.

Consente di vedere la propria espressione regolare. In genere, è possibile esaminare un'espressione regolare e determinare due punti di ancoraggio e utilizzarlo come cutoff per un buffer flottante, in cui viene trasferito l'overflow (sovrapposizione) e la finestra si sposta ulteriormente lungo il file.

Ho fatto questo più volte nei miei moduli Perl. E su qualsiasi cosa diversa dalle ancore all'inizio e alla fine di un file, è facile da fare.

Problemi correlati