2013-10-03 10 views
7

Per citare perlvar:

... il valore di $/ è una stringa, non è un'espressione regolare. awk deve essere migliore per qualcosa. :-)

Non è difficile pensare a situazioni in cui una tale funzione sarebbe utile - l'analisi di file con record a lunghezza variabile è un caso d'uso classico che ho riscontrato molte volte.

Finora non ho mai avuto problemi a caricare l'intero file in memoria e fare un:

my @records = split /my_regex/, <> ; 

ma per ovvie ragioni di questa tecnica non può essere utilizzato in situazioni in cui la memoria disponibile è insufficiente. In effetti, molte volte non è necessario memorizzare tutti i record contemporaneamente.

Che mi riporta a $/.

Trovo strano che la lingua non abbia predisposto il supporto regex per $/. Questo è stato fatto dal design? È semplicemente impossibile da implementare? Quali altre soluzioni esistono che possono essere considerate come le migliori pratiche in assenza di quella che sarebbe una caratteristica elegante?

+1

[ 'Acme :: InputRecordSeparatorIsRegexp'] (https://metacpan.org/pod/Acme::InputRecordSeparatorIsRegexp) – mob

+1

@mob: Non si poteva lasciare che awk farla franca, ora, vero? :) – Zaid

risposta

8

Non ha molto senso persino provare. Troppo spesso, non saresti in grado di dire se hai raggiunto la fine della linea senza aver letto oltre la sua fine. Potrebbe essere molto brutto nelle situazioni interattive.

Per esempio, supponiamo di avere il seguente programma:

local $/ = qr/\n|\r\n?/; # Handle Windows, Unix and old MacOS line endings. 
while (1) { 
    print "Please enter a command: "; 
    my $cmd = <>; 
    $cmd =~ s{$/\z}{}; 
    process($cmd); 
} 

sembra piuttosto semplice, giusto? In effetti, supportare qr/\n|\r\n?/ è probabilmente il motivo numero uno per questa richiesta. Bene, anche quel semplice codice è gravemente imperfetto. Diciamo che io uso i fine riga MacOS (CR,^M, \ r)

$ processor 
Please enter a command: foo^M 
[hangs] 

Il programma si blocca perché non può dire se ho dato una linea di MacOS fine (CR,^M, \ r) o un finale di riga di Windows (CRLF,^M^J, \ r \ n) fino a quando non viene digitato un altro carattere.

Dovrei inserire un secondo comando per elaborare il primo, un terzo comando per elaborare il secondo, ecc. Non ha senso.

0

Perl6::Slurp si presenta come una possibile workaraound:

È possibile impostare il separatore di record in ingresso ({IRS => $ your_irs_here}) per l'operazione di ingresso. Il separatore può essere specificato come stringa o come espressione regolare .

+1

Dalla documentazione: "Si noti che un separatore di record di input esplicito non ha alcun effetto di terminazione dell'input in un contesto scalare, lo slurp legge sempre nell'intero flusso di input, qualunque sia il valore" irs " – Zaid

+0

... quindi questo è semplicemente caricamento dell'intero file in memoria, quindi separazione di – Zaid

4

Uno dei maggiori problemi che posso vedere è che il sostegno di un separatore di record regex in generale richiede l'intero contenuto del file da sottoporre a scansione.

Supponiamo, per esempio, che, per qualsiasi motivo, si era specificato un separatore di /\n[^X]+\z/. L'intero file dovrebbe essere letto per verificare se ci fossero X caratteri dopo ogni fine riga.

così ci sono tre opzioni che mi viene in mente:

  • il buffering l'intero file solo per eseguire la scansione per i separatori di record

  • Attuare le espressioni regolari su una stringa "paging" in modo che il file si può leggere in parti

  • L'implementazione di un sottoinsieme delle espressioni regolari standard, da utilizzare come separatori di record

Nessuna di queste è una prospettiva particolarmente interessante dal punto di vista dell'implementazione, e posso vedere che eviterei di farlo se possibile, soprattutto perché la prima opzione è disponibile per il coder Perl attraverso l'uso di split.

+1

Il fatto che le persone possano fornire regex che non hanno senso non è un motivo per non supportare le espressioni regolari. – ikegami

+0

Ri "Buffering dell'intero file solo per cercare i separatori di record", Questo è già il caso (con 'local $ /;' e con un file che non contiene '$ /') – ikegami

3

L'(backtracking) implementazione del motore regex Perl è sostanzialmente incompatibile con l'uso come una linea finale.Parte di questo problema è che non si desidera rieseguire l'intera espressione regolare quando viene letto il carattere successivo. Per esempio, prendiamo l'espressione regolare

$/ = qr/ A \w*? B | XY/; 

E il flusso di dati

f o o A 1 2 X Y B b a r 

Così, quando dovrebbe il ritorno readline? Se facciamo corrispondenza incrementale, potremmo ottenere qualcosa di simile

f o o A 1 2 X Y B b a r 
     A\w\w\w\w B 

#=> fooA12XYB 

Se rieseguire l'intera espressione regolare in ogni posizione, otteniamo

f o o A 1 2 X Y B b a r 

     A *FAIL 
     *FAIL 

     A\w *FAIL 
     *FAIL 

     A\w\w *FAIL 
     *FAIL 

     A\w\w\w *FAIL 
      X *FAIL 

     A\w\w\w\w *FAIL 
      X Y 

#=> fooA12XY 

In altre parole, alternanze (con precedenza) fanno di questo corrispondenza complicata. Se il motore regex non fosse di tipo backtracking (ma preferibilmente eseguito come parser di tabella o macchina di stato), non vi sarebbe alcuna differenza tra rieseguire l'intera espressione regolare o eseguire la corrispondenza incrementale. Tuttavia, i motori regex dove questo è possibile sono meno espressivi rispetto alle regex di Perl.

Un altro problema sarebbe la linea che termina

$/ = qr/ .+ /xs; 

Dovrebbe leggere un tale “linea” tornare solo il carattere successivo (perché l'espressione regolare è già soddisfatto dopo un carattere), o l'intero file (perché .* vuole abbinare il più possibile)? O dovrebbe essere restituito il resto del buffer interno, qualunque cosa contenga al momento?

Per utilizzare le espressioni regolari per le terminazioni di riga, è necessario definire queste ambiguità e imporre ulteriori limitazioni (ad esempio, sono consentite solo le lingue regolari).

+0

vedo. Questo risponde anche alla domanda di follow-up sul perché awk può farlo e Perl no. – Zaid

+0

Il fatto che è possibile utilizzare espressioni regex inefficienti non è un motivo molto convincente per non supportarli. – ikegami

+0

Non ci sono problemi con '$/= qr /. +/Xs;' (eccetto che sarebbe stupido da fare). Devi continuare a leggere, anche senza espressioni regolari. per esempio. Considera '$/=" \ r \ n ";' oppure '$/= $ mime_sep;'. – ikegami

Problemi correlati