J'ai récemment posé une question sur la façon de supprimer le caractère de nouvelle ligne s'il se produit après un autre caractère spécifique.
Les outils de traitement de texte Unix sont très puissants, mais presque tous traitent des lignes de texte, ce qui est très bien la plupart du temps lorsque l'entrée tient dans la mémoire disponible.
Mais que dois-je faire si je souhaite remplacer une séquence de texte dans un énorme fichier qui ne contient pas de retour à la ligne?
Par exemple, remplacer <foobar>
par \n<foobar>
sans lire l'entrée ligne par ligne? (car il n'y a qu'une seule ligne et il fait 2,5 G de long).
text-processing
MattBianco
la source
la source
perl
oupython
?gsar
( home.online.no/~tjaberg ) que j'essaierai.Réponses:
La première chose qui me vient à l'esprit face à ce type de problème est de changer le séparateur d'enregistrement. Dans la plupart des outils, ce paramètre est défini
\n
par défaut, mais il peut être modifié. Par exemple:Perl
Explication
-0
: ceci définit le séparateur d'enregistrement d'entrée sur un caractère étant donné sa valeur hexadécimale . Dans ce cas, je le mets à>
la valeur hexadécimale3E
. Le format général est-0xHEX_VALUE
. C'est juste une astuce pour briser la ligne en morceaux gérables.-pe
: imprime chaque ligne d'entrée après avoir appliqué le script donné par-e
.s/<foobar>/\n$&/
: une simple substitution. C'est$&
ce qui a été apparié, dans ce cas<foobar>
.awk
Explication
RS="<"
: définissez le séparateur d'enregistrement d'entrée sur>
.gsub(/foobar>/,"\n<foobar>")
: remplacer tous les cas defoobar>
avec\n<foobar>
. Notez que parce queRS
a été défini sur<
, tous<
sont supprimés du fichier d'entrée (c'est ainsi que celaawk
fonctionne), nous devons donc faire correspondrefoobar>
(sans a<
) et remplacer par\n<foobar>
.printf "%s",$0
: affiche la "ligne" courante après la substitution.$0
est le record actuelawk
, il contiendra donc tout ce qui était avant le<
.J'ai testé ceux-ci sur un fichier à ligne unique de 2,3 Go créé avec ces commandes:
Tant le
awk
et lesperl
utilisés quantités négligeables de mémoire.la source
Tie::File
perldoc.perl.org/Tie/File.html . Je pense que ce sont les meilleures fonctionnalitésPerl
lorsque l'on traite des fichiers volumineux.Tie::File
est un module de base depuisv5.7.3
.gsar (recherche générale et remplacement) est un outil très utile exactement à cette fin.
La plupart des réponses à cette question utilisent des outils basés sur des enregistrements et diverses astuces pour les adapter au problème, telles que la commutation du caractère de séparation d'enregistrements par défaut sur quelque chose supposé se produire assez fréquemment dans l'entrée pour ne pas rendre chaque enregistrement trop volumineux à gérer.
Dans de nombreux cas, c'est très fin et même lisible. Je fais comme des problèmes qui peuvent être facilement / efficacement résolus avec des outils partout, disponibles tels que
awk
,tr
,sed
et la bourne shell.Effectuer une recherche binaire et remplacer dans un énorme fichier arbitraire avec un contenu aléatoire ne convient pas très bien à ces outils Unix standard.
Certains d'entre vous pensent peut-être que c'est de la triche, mais je ne vois pas comment utiliser le bon outil pour le travail peut être mauvais. Dans ce cas, il s'agit d'un programme C appelé
gsar
sous licence GPL v2 , donc cela me surprend un peu qu'il n'y ait pas de package pour cet outil très utile dans ni gentoo , redhat , ni ubuntu .gsar
utilise une variante binaire de l' algorithme de recherche de chaînes de Boyer-Moore .L'utilisation est simple:
où
-F
signifie mode "filtre", c'est-à-dire lecture /stdin
écriturestdout
. Il existe également des méthodes pour opérer sur les fichiers.-s
spécifie la chaîne de recherche et-r
le remplacement. La notation deux-points peut être utilisée pour spécifier des valeurs d'octets arbitraires.Le mode insensible à la casse est pris en charge (
-i
), mais les expressions régulières ne sont pas prises en charge , car l'algorithme utilise la longueur de la chaîne de recherche pour optimiser la recherche.L'outil peut également être utilisé uniquement pour la recherche, un peu comme
grep
.gsar -b
renvoie les décalages d'octets de la chaîne de recherche correspondante, etgsar -l
imprime le nom de fichier et le nombre de correspondances le cas échéant, un peu comme la combinaisongrep -l
avecwc
.L'outil a été écrit par Tormod Tjaberg (initial) et Hans Peter Verne (améliorations).
la source
gsar
.Dans le cas étroit où la cible et les chaînes de remplacement sont de la même longueur, le mappage de la mémoire peut venir à la rescousse. Ceci est particulièrement utile si le remplacement doit être effectué sur place. Vous mappez essentiellement un fichier dans la mémoire virtuelle d'un processus, et l'espace d'adressage pour l'adressage 64 bits est énorme. Notez que le fichier n'est pas nécessairement mappé en une seule fois dans la mémoire physique , de sorte que les fichiers dont la taille de la mémoire physique disponible sur la machine peut être traitée plusieurs fois.
Voici un exemple Python qui remplace
foobar
parXXXXXX
la source
Il existe de nombreux outils pour cela:
dd
est ce que vous voulez utiliser si vous voulez bloquer un fichier - ne lisez de manière fiable qu'un certain nombre d'octets qu'un certain nombre de fois. Il gère de manière portative le blocage et le déblocage des flux de fichiers:tr -dc '[:graph:]' </dev/urandom | dd bs=32 count=1 cbs=8 conv=unblock,sync 2>/dev/null
UI(#Q5\e BKX2?A:Z RAxGm:qv t!;/v!)N
J'utilise également
tr
ci-dessus car il peut gérer la conversion de tout octet ASCII en un autre (ou, dans ce cas, la suppression de tout octet ASCII qui n'est pas un caractère imprimable sans espace). C'est ce que j'ai utilisé en réponse à votre autre question ce matin, en fait, quand j'ai fait:Il en existe de nombreux similaires . Cette liste devrait fournir un sous-ensemble de dénominateurs communs le plus bas avec lequel vous pourriez vous familiariser.
Mais, si je devais faire du traitement de texte sur 2,5 Go de fichier binaire, je pourrais commencer par
od
. Il peut vous donner unoctal dump
ou plusieurs autres formats. Vous pouvez spécifier toutes sortes d'options - mais je ne ferai qu'un octet par ligne dans un\C
format d'échappement:Les données que vous obtiendrez
od
seront régulières à n'importe quel intervalle que vous spécifiez - comme je le montre ci-dessous. Mais d'abord - voici une réponse à votre question:Ce petit peu au- dessus sur délimite
\n
ewlines,\0
nulls,\t
abs et<spaces>
tout en préservant la\C
chaîne échappée pour le delimiter. Notez les fonctionsH
etx
utilisées - à chaque fois qu'ilsed
rencontre un délimiteur, il échange le contenu de ses tampons de mémoire. De cette façon,sed
ne conserve que les informations nécessaires pour délimiter le fichier de manière fiable et ne succombe pas aux dépassements de tampon - ce n'est pas le cas, tant qu'il rencontre réellement ses délimiteurs. Tant qu'il le fera,sed
continuera à traiter son entrée etod
continuera à la fournir jusqu'à ce qu'elle rencontreEOF
.En l'état, sa sortie ressemble à ceci:
Donc si je veux
foobar
:Maintenant, si vous voulez utiliser les
C
échappements, c'est assez facile - car lased
double\\
barre oblique inversée a déjà échappé à toutes ses barres obliques inverses, doncprintf
exécutée depuisxargs
n'aura aucun problème à produire la sortie selon vos spécifications. Maisxargs
mange des guillemets shell , vous devrez donc le répéter:Cela aurait pu tout aussi bien être enregistré dans une variable shell et sorti plus tard de la même manière. Le dernier
sed
insère une\
barre oblique inverse avant chaque caractère dans son entrée, et c'est tout.Et voici à quoi tout cela ressemble avant de
sed
s'en emparer:la source
Awk fonctionne sur des enregistrements successifs. Il peut utiliser n'importe quel caractère comme séparateur d'enregistrement (sauf l'octet nul sur de nombreuses implémentations). Certaines implémentations prennent en charge des expressions régulières arbitraires (ne correspondant pas à la chaîne vide) comme séparateur d'enregistrements, mais cela peut être compliqué car le séparateur d'enregistrements est tronqué à partir de la fin de chaque enregistrement avant d'être rangé dans
$0
(GNU awk définit la variableRT
sur le séparateur d'enregistrements) qui a été retiré de la fin du record actuel). Notez queprint
termine sa sortie avec le séparateur d'enregistrement de sortieORS
qui est une nouvelle ligne par défaut et défini indépendamment du séparateur d'enregistrement d'entréeRS
.Vous pouvez sélectionner efficacement un autre caractère comme séparateur d'enregistrement pour d' autres outils (
sort
,sed
...) en échangeant avec des sauts de ligne ce caractère avectr
.De nombreux utilitaires de texte GNU prennent en charge l'utilisation d'un octet nul au lieu d'une nouvelle ligne comme séparateur.
la source