Forum Programmation.shell Comment remplacer l'espace entre les champs par ; avec sed ?

Posté par  .
Étiquettes : aucune
0
4
fév.
2005
Je voudrais modifier le formatage de mon fichier en remplaçant l'espace contenu entre mes 3 champs par un ";" histoire de traiter ce fichier avec open office.
Le problème est que je ne peux lancer la commande tr -s " " ";" du fait des espaces contenus le 2ème champ.

Extrait du fichier:

562910 Services d'assainissement 1
562990 Tous les autres services de gestion des déchets 1
611420 Formation en informatique 6
...

J'aimerais avoir en sortie:

562910;Services d'assainissement;1
562990;Tous les autres services de gestion des déchets;1
611420;Formation en informatique;6
...

D'avance merci pour votre aide.
  • # man sed

    Posté par  (site web personnel) . Évalué à 1.

    remplacer tab par " ":
    cat file | sed 's/\t/ /' > file-without-tabs
    je te dirais bien
    cat file | sed 's/ /;/' > file-without-tabs
    mais si tu as deux espaces consecutifs, ca met deux ';'
    je te propose donc:
    cat file | sed 's/ / /' | sed 's/ / /' | sed 's/ / /' | sed 's/ / /' | sed 's/ / /' | sed 's/ / /' | sed 's/ / /' | sed 's/ / /' | sed 's/ /;/' > file-without-tabs
    si tu as au plus 2^8 espaces consecutifs

    je pense qu il existe une solution plus elegante a base de
    sed 's/*{ }/ /'
    ou truc du genre, mais si tu veux du vite fait, ca ira pour aujourd hui.
    • [^] # Re: man sed

      Posté par  (site web personnel) . Évalué à 1.

      OK oublies j ai tout faux ... laisse moi reflechire 1 mn
      • [^] # Re: man sed

        Posté par  (site web personnel) . Évalué à 1.

        y a deux solutions:
        man awk, avec un for ... tu imprimes $1; puis tous les champs jusqu a l avant dernier
        man sed: tu fais une substitution par le debut, et une par la fin: pour le premier, tu cherches un pattern suivi d un espace, mais n en contenant pas, et tu le substitues par le meme pattern suivi d un ';', et la meme chose a l envers par la fin.

        Note que par default, une regexp commences toujours le matching par la fin.
        • [^] # Re: man sed

          Posté par  . Évalué à 2.

          Le problème principal que j'ai est que le nombre de mots dans le 2ème champ est variable et que je ne peux pas faire de formatage avec awk. Je pense que la solution est avec sed mais je ne l'ai pas trouvé pour l'instant.
  • # En une ligne

    Posté par  (site web personnel) . Évalué à 1.

    Le plus simple que j'ai trouvé
    cat file.txt | tr -s ' ' ';' | sed 's/\([a-zA-Z]\);\([a-zA-Z]\)/\1 \2/g'

    Remplacer tous les espaces par des ; puis remplacement des ; entre deux lettres par un espaces...

    https://damien.pobel.fr

    • [^] # Re: En une ligne

      Posté par  . Évalué à 2.

      En une autre ligne : sed -i 's:^\([0-9]*\) \(.*\) \([0-9]*\)$:$1;$2;$3:' fichier Ou encore : sed -i 's| |;|;s|\(.*\) |$1;|' fichier
      • [^] # Re: En une ligne

        Posté par  . Évalué à 4.

        Oups, pardon, j'en ai oublié la pédagogie :

        - le premier fait un remplacement de la ligne complète, dans laquelle 3 champs ont été identifiés avec des \(qqch\), par une ligne contenant ces 3 champs séparés par des ";" (c'est la partie "$1;$2;$3").

        - le deuxième procède en deux étapes, deux commandes "s" séparées par un ";". La première commande remplace le 1er espace de la ligne (la commande "s" s'occupe uniquement de la première occurence trouvée quand on ne lui adjoint pas un "g" à la fin). La deuxième remplace uniquement le dernier espace de la ligne (c'est le "\(.*\)" de bourrage au début qui fait que c'est au dernier qu'on s'attaque, ce bourrage étant ensuite réintroduit dans la chaine de remplacement avec le "$1").

        Bon y'a sûrement plus joli à faire, mais j'allais pas ouvrir la page info non plus...
        • [^] # Re: En une ligne

          Posté par  . Évalué à 1.

          Merci,

          Je viens d'éxécuter ta commande avec des \1 \2 \3 à la place de $1 $2 $3 et ça fonctionne.

          Merci à tous pour vos précieux conseils.
      • [^] # Re: En une ligne

        Posté par  . Évalué à 2.

        Beuhaaaa... Bon, ouais, j'ai mis partout des $ alors que c'est des \ (devant les nombres, pour récuperer des patterns). Je vais donc de ce pas me coucher.
        • [^] # Re: En une ligne

          Posté par  . Évalué à 3.

          Mais avant de me coucher, juste un mot pour dire que ma première expression, c'est le même principe que celle de Robin des Bulles un peu plus bas. La différence est que la sienne est beaucoup plus robuste (gestion des blancs en général et pas seulement des espaces, premiers et derniers champs qui ne sont pas obligatoirement des nombres, et chaine du milieu qui ne matchera que si elle n'est pas complètement vide). La mienne foirerait plus facilement si tu as des lignes qui ne sont pas strictement celles que tu nous a décrites (je veux dire, en plus du fait qu'elle foire à cause des "$"), donc si il ne fallait n'en retenir qu'une...
    • [^] # Re: En une ligne

      Posté par  . Évalué à 1.

      La commande fonctionne lorsqu'il n'y a pas de , de "à" et de parenthèse. Dans mon exemple, ne figure pas ces caratères-là mais ils sont présents dans le fichier. Voici le résultat de la commande:

      811310;Réparation et entretien de machines et de matériel d'usage commercial et industriel;(sauf les véhicules automobiles et le matériel électronique);9
      811420;Rembourrage et réparation de meubles;3
      812320;Services de nettoyage à;sec et de blanchissage;(sauf le libre-service);1
      812921;Laboratoires de développement et de tirage de photos;(sauf le service en une heure);1
      813910;Associations de gens d'affaires;24
      813920;Organisations professionnelles;6

      Je viens de modifier la commande en rajoutant "\(" "," avec les lettres mais je n'arrive pas à exclure le "à" du problème d'affichage.

      La solution est toute proche....
  • # Les regexps, ça roulaize !

    Posté par  . Évalué à 3.

    Une petite formule magique :

    echo -e "12345 Service d assainissement 3" | sed -e "s/\(^[^\t ]*\)[\t ]\(..*\)[\t ]\([^\t ]*$\)/\1;\2;\3/g"
    avec le lien qui va bien :

    http://www.greenend.org.uk/rjk/2002/06/regexp.html(...)


    Je t'invite quand même à essayer de décortiquer l'expression ci-dessus pour arriver à faire les tiennes.

    Bon courage.
  • # une suggestion

    Posté par  . Évalué à 2.

    J'ai une ptit question bête....
    oui je sais qu'on peu le faire en SED, bon.... mais pourquoi ne pas utiliser un autre outil ?
    Quand je vois la complexité et les effets de bords possible (dans certains cas ca ne marche pas en fonction de certains caractere... d'apres une des solution que j'ai lu : "à" ..) bref....
    pourquoi ne pas faire un joli petit script en perl ou php.
    moi je ne connais pas trop le perl, mais en php c'est relativement facile à faire : on lit la ligne, on la split, on prend le 1er item, on ajout le fameux ";" et on fusione les item suivant...
    et encore là c'est pas ce qu'il y a de mieux , j'ai juset voulu faire rapide (2 a 3 ligne quoi) mais on peu "analyser la ligne" (ercherche le 1er espace quoi), pour descouper la ligne en deux et y mettre un ";" entre les deux....

    enfin bref, c'est moins complexe, assez facile a maintenir et debugger.

    je ne vois pas pourquoi s'en priver (oui ca fait un peu moins geek mais bon.. )
    • [^] # Re: une suggestion

      Posté par  (site web personnel) . Évalué à 2.

      Avec sed, c est simple et rapide.

      Quelque soit le langage, la logique de l operation est la meme, et sed est le langage qui permet de le faire le plus vite, meme si j en conviens un debutant comprendra plus vite le script php que sed.

      en tant que Linuxien inveteres, nous nous devons de produire de la qualite. Et dans le cas present, la qualite maximale, c est sed. La preuve, une solution moins de 80 chars a ete proposee ici meme. Je te met au defi de le faire en moins de 80 char en php.

      Tu comprendra toute l empleur de ce post si tu etudies un jour un langage non sequentiel comme le Prolog ou le VHDL.
      • [^] # Re: une suggestion

        Posté par  . Évalué à 2.

        j'en conviens...

        juste deux remarquesn toutefois :

        D'une part, l'avantage de php est de donner une solution pedagogique.
        Avec le sed pondu ici ou là.. c'est pas evident, et je doute que le demandeur puisse reproduire la même chose.
        avec le php, j'ai même pas eut besoin de donner le code, l'explication en bon francais se suffit a lui même

        Deuxiement,
        Plus performant ? oui, mais a quel prix ? (la taille du source n'est pas revelateur d'ailleur)
        (il y a longtemps dans mes jeunes année) un jour un prof nous a fait plancher en TP sur la notion d'optimisation. pour nous montrer que le cout (comprendre la complexité) est exponentiel et qu'a un moment le gain est extrement faible.
        En gros a "un certain stade" tu va galerer comme un malade pour optimiser (repenser tes structures, apprendre un nouveau langague..) pour gagner trois fois rien.
        L'agorithme deviens inmaintenable, trop complexe...
        la qualité de developpeur (selon lui) est de savoir ou s'arreter, non pas de pondre l'algo le + rapide.
        ca se discute, mais c'est interressant, je garde cette reflexion en tête :)

        en fait, j'avoue, j'aimerais bien être capable de pondre du sed comme voue le faites :)
      • [^] # Re: une suggestion

        Posté par  . Évalué à 3.

        oui, bon, voilà le code en php, (pondu en 3 minutes)
        $h=fopen("fichier","r");
        while(!feof($h))
        {
        $l=fgets($h);
        $p=strpos($l,' ');
        echo substr($l,0,$p-1).";".".substr($l,$p+1);

        bien sur ca depasse le 80 car :)
        c'est facilement maintenable.
        J'ai pas testé si l'espace est trouvé (pas de test non plus dans les exemple en sed) mais c'est facile, il suffit de balancer un if apres le strpos.
        pour le nom du fichier, je l'ai mis en dur (comme les proposition sed hein) mais on peu le lire depuis la ligne de commande...

        au moins un non-3l33t peut comprendre :)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.