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.