Sustituir cadenas en un archivo muy grande

10

Tengo una serie muy larga de URL sin caracteres de separación, en el mismo formato que a continuación:

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

Quiero que cada URL esté en una nueva línea. Traté de hacer esto reemplazando todas las instancias de "http: //" con "\ nhttp: //" usando sed

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

pero ocurre una falla de segmentación (violación de memoria). Solo puedo suponer que el tamaño del archivo (más de 100 GB) está causando que sed supere algún límite.

Podría dividir el archivo en varios archivos más pequeños para su procesamiento, pero todas las instancias de "http: //" tendrían que mantenerse intactas.

¿Hay una mejor manera de hacer esto?

C Sawyer
fuente
Creo que a sed no le gustan los 100 GB sin terminaciones de línea, ya que intenta leer una sola línea en su búfer.
jippie
división (independientemente de "dónde" se produce el corte), el procesamiento y el reensamblado deberían proporcionar el resultado correcto.
enzotib
3
Si realmente tiene un archivo de texto de 100GB que contiene una sola línea larga, es mejor que escriba un programa C rápido para hacer el trabajo.
fpmurphy

Respuestas:

11

Con awkusted puede evitar leer una gran cantidad de texto a la vez:

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

El éxito puede depender de la awkimplementación utilizada . Por ejemplo gawkfunciona bien, pero se mawkbloquea.

hombre trabajando
fuente
6

Esto hará el trabajo:

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

Al establecer $ / , he cambiado la definición de una línea para que termine en //lugar de una nueva línea. Esto hace que Perl lea una URL a la vez. Es poco probable que una URL contenga, //excepto después del esquema, pero está bien si uno lo hace, la expresión regular evitará que agregue nuevas líneas espurias.

Si desea evitar agregar una línea en blanco antes de la primera URL:

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

Puede probar la evaluación comparativa para ver si s!http://\z!\nhttp://!es más rápido. Son equivalentes. Tenga en cuenta que la /gbandera no es necesaria en la sustitución, porque solo puede haber una coincidencia por "línea".

cjm
fuente
¿Está bien el motor perl regexp con líneas de varios gigabytes de largo?
Alexios
2
@Alexios, probablemente no, pero no es necesario que lo sea. Desde que cambié $/, solo se tratará de una URL a la vez.
cjm
Ah, ya veo lo que hiciste allí. Ha pasado un tiempo desde los años 90, y tuve que hacerlo man perlvar, pero tiene sentido de esa manera.
Alexios
Linux permite que las URL tengan múltiples barras inclinadas en las rutas, por lo que este código puede fallar si tiene alguna de ellas. La prueba de toda la cadena, http y todo, no tendrá este problema.
Joe
@ Joe, estoy probando la http:parte de la expresión regular. Examinará todos //, pero no agregará una nueva línea a menos que encuentre http://.
cjm
5
  1. Cambie todas las apariciones de a :con una nueva línea, para cortar el archivo.
  2. Reemplazar
    • http al final de la línea con
    • una nueva línea seguida de http:y agregue la siguiente línea
  3. Repita una vez, para que las líneas pares e impares se actualicen

Estos pasos se ven así:

tr ':' '\n' | sed -e '/http$/{N;s/http\n/\nhttp:/}' | sed -e '/http$/{N;s/http\n/\nhttp:/}'
  1. Compruebe si hay líneas que no comienzan http://, imprima los números de línea. Esto solo ocurriría si a: está en algún lugar de la URL que no sea después de http.

    grep -nv '^http://'

jippie
fuente