Substitution de chaînes dans un très gros fichier

10

J'ai une très longue série d'URL sans caractère de séparation, dans le même format que ci-dessous:

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

Je veux que chaque URL soit sur une nouvelle ligne. J'ai essayé de le faire en remplaçant toutes les instances de "http: //" par "\ nhttp: //" en utilisant sed

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

mais un défaut de segmentation se produit (violation de mémoire). Je ne peux que supposer que la taille du fichier (c'est plus de 100 Go) fait que sed dépasse une certaine limite.

Je pourrais diviser le fichier en plusieurs fichiers plus petits pour le traitement, mais toutes les instances de "http: //" devraient être conservées intactes.

Y a-t-il une meilleure manière de faire cela?

C Sawyer
la source
Je pense que sed n'aime pas le 100 Go sans fin de ligne car il essaie de lire une seule ligne dans son tampon.
jippie
le fractionnement (quel que soit le "lieu" de la coupe), le traitement, puis le remontage doivent cependant donner le résultat correct.
enzotib
3
Si vous avez vraiment un fichier texte de 100 Go contenant une seule longue ligne, vous feriez mieux d'écrire un programme C rapide pour faire le travail.
fpmurphy

Réponses:

11

Avec, awkvous pouvez éviter de lire une énorme quantité de texte à la fois:

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

Le succès peut dépendre de l' awkimplémentation utilisée . Par exemple gawkfonctionne bien, mais se mawkbloque.

homme au travail
la source
6

Cela fera le travail:

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

En définissant $ / , j'ai changé la définition d'une ligne pour qu'elle se termine par //au lieu d'une nouvelle ligne. Cela fait que Perl lit une URL à la fois. Il est peu probable qu'une URL contienne, //sauf après le schéma, mais c'est correct si c'est le cas, l'expression régulière l'empêchera d'ajouter de nouvelles lignes parasites.

Si vous souhaitez éviter d'ajouter une ligne vide avant la première URL:

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

Vous pourriez essayer de comparer si s!http://\z!\nhttp://!c'est plus rapide. Ils sont équivalents. Notez que le /gdrapeau n'est pas nécessaire sur la substitution, car il ne peut y avoir un match par « ligne ».

cjm
la source
Le moteur perge regexp est-il correct avec des lignes de plusieurs gigaoctets?
Alexios
2
@Alexios, probablement pas, mais ce n'est pas nécessaire. Depuis que j'ai changé $/, il ne traitera qu'une seule URL à la fois.
cjm
Ah, je vois ce que tu as fait là-bas. Cela fait un moment depuis les années 90, et je devais le faire man perlvar, mais c'est logique de cette façon.
Alexios
Linux permet aux URL d'avoir intégré plusieurs barres obliques dans les chemins, donc ce code peut échouer si vous en avez. Tester la chaîne entière, http et tout, n'aura pas ce problème.
Joe
@Joe, je teste la http:partie dans l'expression régulière. Il examinera chaque élément //, mais il n'ajoutera pas de nouvelle ligne à moins qu'il ne le trouve http://.
cjm
5
  1. Modifiez toutes les occurrences d'un :avec une nouvelle ligne, pour couper le fichier.
  2. Remplacer
    • http à la fin de la ligne avec
    • une nouvelle ligne suivie http:et y ajouter la ligne suivante
  3. Répétez une fois pour que les lignes paires et impaires soient mises à jour

Ces étapes ressemblent à:

tr ':' '\n' | sed -e '/http$/{N;s/http\n/\nhttp:/}' | sed -e '/http$/{N;s/http\n/\nhttp:/}'
  1. Vérifiez s'il y a des lignes qui ne commencent pas par http://, imprimez les numéros de ligne. Cela ne se produirait que si a: se trouve quelque part dans l'URL autre qu'après le http.

    grep -nv '^http://'

jippie
la source