Sostituzione di stringhe in un file molto grande


10

Ho una serie molto lunga di URL senza caratteri di separazione, nello stesso formato di seguito:

http://example.comhttp://example.nethttp://example.orghttp://etc...

Voglio che ogni URL sia su una nuova riga. Ho provato a farlo sostituendo tutte le istanze di "http: //" con "\ nhttp: //" usando sed

sed 's_http://_\nhttp://_g' urls.txt

ma si verifica un errore di segmentazione (violazione della memoria). Posso solo supporre che la mera dimensione del file (supera i 100 GB) stia facendo sì che sed superi un certo limite.

Potrei dividere il file in più file più piccoli per l'elaborazione, ma tutte le istanze di "http: //" dovrebbero essere mantenute intatte.

C'è un modo migliore per farlo?


Penso che a sed non piacciano i 100 GB senza terminazioni di riga in quanto tenta di leggere una singola riga nel suo buffer.
jippie,

la suddivisione (indipendentemente da "dove" si verifica il taglio), l'elaborazione e il riassemblaggio dovrebbero comunque fornire il risultato corretto.
enzotib,

3
Se hai davvero un file di testo da 100 GB che contiene una singola linea lunga, allora è meglio scrivere un programma C rapido per fare il lavoro.
fpmurphy,

Risposte:


11

Con awkte puoi evitare di leggere enormi quantità di testo contemporaneamente:

awk -vRS='http://' -vORS='\nhttp://' 1 urls.txt > urlsperline.txt

Il successo può dipendere awkdall'implementazione utilizzata . Ad esempio gawkfunziona bene, ma si mawkblocca.


6

Questo farà il lavoro:

perl -pe 'BEGIN { $/ = "//" } s!(?=http://\z)!\n!' urls.txt

Impostando $ / , ho modificato la definizione di una riga in modo che termini //invece di una nuova riga. Questo fa leggere a Perl un URL alla volta. È improbabile che un URL contenga, //tranne dopo lo schema, ma è OK se lo fa, la regex gli impedirà di aggiungere nuove righe spurie.

Se vuoi evitare di aggiungere una riga vuota prima del primo URL:

perl -pe 'BEGIN { $/ = "//"; print scalar <> } s!(?=http://\z)!\n!' urls.txt

Potresti provare il benchmarking per vedere se s!http://\z!\nhttp://!è più veloce. Sono equivalenti. Nota che il /gflag non è necessario sulla sostituzione, perché può esserci solo una corrispondenza per "linea".


Il motore regexp perl è a posto con le linee multi-gigabyte?
Alessio,

2
@Alexios, probabilmente no, ma non è necessario. Da quando ho cambiato $/, tratterà solo un URL alla volta.
cjm,

Ah, vedo cosa hai fatto lì. È passato un po 'di tempo dagli anni '90, e ho dovuto man perlvar, ma ha senso in questo modo.
Alessio,

Linux consente agli url di incorporare più barre nei percorsi, quindi questo codice potrebbe non riuscire se ne hai uno. Il test per l'intera stringa, http e all, non avrà questo problema.
Joe,

@Joe, sto testando la http:parte in regex. Esaminerà tutti //, ma non aggiungerà una nuova riga se non lo trova http://.
cjm

5
  1. Cambia tutte le occorrenze di a :con una nuova riga, per tagliare il file.
  2. Sostituire
    • http alla fine della linea con
    • una nuova riga seguita da http:e aggiungere la riga successiva ad essa
  3. Ripeti una volta, quindi le linee pari e dispari vengono aggiornate

Questi passaggi sembrano:

tr ':' '\n' | sed -e '/http$/{N;s/http\n/\nhttp:/}' | sed -e '/http$/{N;s/http\n/\nhttp:/}'
  1. Controlla se ci sono linee che non iniziano con http://, stampa i numeri delle linee. Ciò si verificherebbe solo se a: è da qualche parte nell'URL diverso da quello dopo http.

    grep -nv '^http://'

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.