Sed, remplacer les n premières occurrences - Shell/Batch - Programmation
Marsh Posté le 09-02-2011 à 15:48:46
Une solution serait de faire une boucle jusqu'à ce que le nombre de remplacement soit atteint ou que la fin de fichier soit atteinte.
A l'intérieur de la boucle, on ferait la recherche et le remplacement pour chaque ligne.
La boucle ferait une lecture du fichier de manière classique par :
cat thefichier.txt | while read theligne |
Ou bien on peut utiliser awk ou gawk ou nawk.
On doit aussi pouvoir utiliser Perl ou d'autres langages. Personnellement, j'écrirais un petit code en C, compilé avec gcc, et le problème aurait été résolu en beaucoup moins de temps qu'il n'en aura fallu pour poster la question sur le forum et attendre la première réponse.
Marsh Posté le 10-02-2011 à 02:08:41
Merci,
Cela permet de traiter chaque ligne du fichier, mais ça ne permet pas de remplacer les N premières occurrences de chaque ligne. À moins de faire encore une boucle pour chaque ligne, mais quand on m'a vendu sed, on m'a dit qu'il était beaucoup plus puissant
Marsh Posté le 10-02-2011 à 10:58:49
Je viens de jeter un oeil au man, et le chiffre qu'on peux mettre à la fin c'est pour remplacer la n-ième occurrence, pas le nombre d'occurrences à remplacer.
Visiblement il n'y a pas moyen non plus de faire comme le cut avec un truc du genre 1-4
awk doit pouvoir te permettre de réaliser ca, mais mes connaissances en awk sont assez limitées...
Marsh Posté le 10-02-2011 à 11:32:20
C'est très probablement faisable en sed, mais ça demande un niveau d'expertise que j'ai eu il y a bien longtemps, mais que je n'ai plus depuis que j'utilise perl, parce que ça doit être assez complexe à écrire en sed et plus simple en perl.
En perl je procéderais ainsi:
s/(a+)/{my $tmp = substr($1, 1); $tmp =~ tr!a!b!; $tmp}/eg;
On cherche le pattern a+, quand trouvé, on évalue (le e de /eg) {my $tmp = substr($1, 1); $tmp =~ tr!a!b!; $tmp}:
On récupère la sous chaine matchant le pattern (donc avec au moins un a) commençant au 2e caractère, donc ca fait la chaine avec un a de moins, on remplace les a par des b dans cette sous chaine, et enfin on renvoie la chaine (comme on est dans un eval, pas besoin de return...)
C'est donc le résultat de cette évaluation qui est substitué.
Testé ainsi:
Code :
|
>perl test.pl
>Bibbll the bd cbbbbbt
Bon ensuite l'intérêt, c'est qu'on peut en faire une procédure, plus modulaire et réemployable:
Code :
|
>perl test.pl
>Bixxxxxxll the bxxxxd cxxxx
>
>Biyyyll the byd cyyyyyyt
>
>Bizll the bd czzzzt
>
>Bisssll the bsd csssssst
A+,
Marsh Posté le 10-02-2011 à 13:50:57
Essais ça:
Code :
|
puis tu mets tes regex dans les variables.
Et si tu veux travailler dans un intervalle précis de lignes tu rajoutes:
Code :
|
Espérant t'avoir aidé.
Edit : L'incrémentation du flags devrait suffire pour définir le nombre d'occurence
Marsh Posté le 10-02-2011 à 13:59:54
Oui, mais tout ça répond pas a sa question initiale qui est si j'ai bien compris:
comment remplacer les toutes occurrences de n fois le caractère a par m(n) fois le caractère b, ou m dépend de n (par exemple, m(n) = n-1, ou n+1, ...)
A+,
Marsh Posté le 10-02-2011 à 15:33:12
Merci à tous,
En fait vous avez répondu à mes deux questions.
gilou: en effet c'est puissant la fonction en perl et ça répond à ma question subsidiaire donc intéressant.
Mon but était de le faire avec sed uniquement par curiosité à la base parce que je cherchais à faire les étoiles de ce post:
http://forum.hardware.fr/hfr/Progr [...] 2303_1.htm
donc je voulais essayer une technique où j'écrirais le nombre max d'étoiles et je remplace les N premières par des espaces
Marsh Posté le 12-02-2011 à 11:00:43
ptitchep a écrit : Mon but était de le faire avec sed uniquement par curiosité à la base parce que je cherchais à faire les étoiles de ce post. |
Pour cela, il suffit de remplacer n occurrences de a par n-1 occurrences de b, en perl, le plus simple serait ceci:
1) Remplacer n occurences de a par n-1 occurences de a: s/(a)(\1*)/$2/g; Dans le second groupe, \1 représente ce qui est matché dans le premier groupe
2) remplacer a par b: tr/a/b/
Pour le 1, en sed, cela se transpose a priori ainsi: s/\(a\)\(\1*\)/\2/g et pour le 2, s/a/b/g
donc au final sed 's/\(a\)\(\1*\)/\2/g;s/a/b/g' (testé sous cygwin)
On voit tout de suite aussi que pour remplacer n occurrences de a par n-1 occurrences de b, il va suffire de faire
sed 's/\(a\)\(\1*\)/\1\1\2/g;s/a/b/g' (testé sous cygwin)
echo "**********" | sed ':loop p;s/\(\*\)\(\1*\)/ \2/g;t loop;:pool p;s/\( \)\(\1*\)/*\2/;t pool;d'
sans doute améliorable
A+,
Marsh Posté le 17-02-2011 à 15:02:34
Merci, j'ai appris pas mal sur sed du coup.
En effet ton expression se simplifie mais je poste ça sur l'autre topic.
Marsh Posté le 07-02-2011 à 22:47:22
Bonjour,
Comme dit dans le titre, je cherche à remplacer les n premières occurrences d'un caractère donné d'une ligne par un autre.
Exemple pour les 4 premiers et a => b:
laaaaaal => lbbbbaal
lalalalalalala =>lblblblblalala
Google! ou man! me direz vous... Je ne trouve que ceci:
replace/substitute the N first occurences of each line
sed 's/<replaceThis>/<withThis>/<N>' <file>
Très bien, sauf que chez moi:
chep@Ludwig:~$ echo laaaaal |sed 's/a/b/2'
labaaal
Seule la deuxième occurrence est remplacée
J'ai aussi une petite question subsidiaire
Comment remplacer par exemple 4 'a' par 3 'b'? J'arrive a détecter les '4' a mais pas à remplacer par 3 'b'.
chep@Ludwig:~$ echo laaaal |sed 's/a\{4\}/b/'
lbl
mais
chep@Ludwig:~$ echo laaaal |sed 's/a\{4\}/b\{3\}/'
lb{3}l
Évidemment, dans mon cas il suffit d'écrire 'bbb' mais le but serait plutôt d'utiliser une variable pour remplacer n 'a' par m 'b'.
Merci d'avance.
---------------
deluser --remove-home ptitchep