manipulation fichier texte

Forum d'assistance et d'échange sur l'installation, la configuration, et l'utilisation des système Linux et BSD. Vous pouvez y poster vos questions concernant ces systèmes d'exploitation en faisant l'effort préalable de rechercher dans le forum, dans les manuels et les documentations que la réponse n'y figure pas.

Modérateur: modos Ixus

manipulation fichier texte

Messagepar fredmarseil » 15 Nov 2004 21:03

Bonjour,

Je cherche une solution pour pouvoir traiter une ligne particuliere d'un fichier texte.

mon fichier se presente ainssi:

ligne1
ligne2
ligne3
....
ligne x

Je voudrai pouvoir afficher le contenu de la ligne 3 entière, et uniquement celle-là.
Les commandes head et tail se rapprochent, mais ne se limitent pas à l'unique ligne..

Merci pour votre aide :-)
fredmarseil
Matelot
Matelot
 
Messages: 10
Inscrit le: 15 Nov 2004 20:58

Messagepar tomtom » 15 Nov 2004 21:17

Il n'y a qu'à combiner les 2 :)

cat monfichier.txt | head -x | tail -1

simple, isn't it ? 8)

t.
One hundred thousand lemmings can't be wrong...
Avatar de l’utilisateur
tomtom
Amiral
Amiral
 
Messages: 6035
Inscrit le: 26 Avr 2002 00:00
Localisation: Paris

Messagepar micjack » 15 Nov 2004 21:21

Salut,

Et avec avec grep ?

grep ligne3 NomDuFichier ..... Il t'affichera la ligne complete

Mais tes lignes sont vraiement numerotées ligne1, ligne2, ligne3 dans ton fichier??

EDIT: Il me sembles raconter des $%#&!*ries :roll: Ca serrait trop facile

---> [ ]
micjack
Amiral
Amiral
 
Messages: 3113
Inscrit le: 06 Juin 2003 00:00
Localisation: Varois

re

Messagepar fredmarseil » 15 Nov 2004 21:30

non mon fichier se présente comme ceci :

Code:Societe:C.A.:C.A.prevu:Variation:Contact:Fonction:Adresse:Ville
ALFKI:Alfred's Futterkiste:77.8050525:103.85014:1.33474803:Maria Anders:Representante:Obere Str.57:Berlin
ANATL:L'anatra laccata:771.700421:834.332148:1.08116068:Anna Grandi:Proprietaire:Via Gramsci 209:Verona
ANTOB:Antonio Berbi Salumi:290.31742:411.875041:1.41870592:Antonio Berbi:Proprietaire:V
AROUT:Around the Horn:322.144627:642.261176:1.99370445:Thomas Hardy:Representant:Brook Farm Stratford St. Marc:Colchester
BERGS:Berglunds snabbkop:382.752211:467.260175:1.22079027:Christina Berglun:Proprietaire:Berguvsvgen 8:Lulea
LONP:Bldere et fils:592.578837:1142.53984:1.92808073:Frederique Citeau:Directeur du marketing:24, place Kleber:Strasbourg
BOLID:Bolinderska boden:892.210157:1434.03482:1.60728367:Kalle Berglund:Proprietaire:Lannavagen 16:Halmstad
BONAP:Bon app':344.275299:460.997874:1.33903848:Laurence Lebihan:Proprietaire:12, rue des Bouchers:Marseille

en fait je dois pouvoir traiter ligne par ligne, compter le nombre de champs separés par ":" (mais ca g trouvé, et compter le nombre de caractere de chaque champ..

donc le grep ne marchera pas, car acune ligne commence pareil..
l'idée du head et tail fonctionne bien, mais g peur ke mon prof prefere une solution plus clean..
fredmarseil
Matelot
Matelot
 
Messages: 10
Inscrit le: 15 Nov 2004 20:58

Messagepar tomtom » 15 Nov 2004 21:36

head | tail est le moyen le plus "simple" et "propre" pour sortir une ligne au hasard d'un fichier;

Mais si ton objectif est de traiter ligne par ligne, tu as u programme fait exprès pour ça qui s'appelle awk.

Je te conseille la lecture de son man, c'est un outil extremement puissant.

AMHA, tu as un devoir sur les batches unix à faire. Si tun nous donnais plus de précisions sur ce que tu veux faire, on pourrait mieux t'aider.

t.
One hundred thousand lemmings can't be wrong...
Avatar de l’utilisateur
tomtom
Amiral
Amiral
 
Messages: 6035
Inscrit le: 26 Avr 2002 00:00
Localisation: Paris

re

Messagepar fredmarseil » 15 Nov 2004 21:49

ok, alors nous avons un fichier de données (ci dessous)..
La premiere ligne represente le nom des colonnes (code, societe, ca, ....)
et les lignes suivantes sont les données de chaque colonnes, mais tout est en vrac (enfin presque)
les données sont séparées par ":" .

Le but du "jeu" est de faire un script qui affichera le nom de la colonne et la largeur maximale qui faudra lui attribuer (en supposant qu'on voudra l'inserer dans une bdd)
Par exmple pour la colonne "code" il devra afficher "la colonne code a une largeur de: 5 caracteres max", la colonne "societe a pour largeur max: 15" etc...

j'ai donc deja effectué un calcul automatique du nombre de ligne, et j'arrive a distinguer les champs avec un cut.
Seulement faut mettre tout ca en route c pas evident.
J'avais dans l'idée de faire une boucle ki scane d'abord la 1ere colonne, ligne par ligne (d où mon post), ensuite on passe a la 2eme colonne et ligne 1, ligne 2....

Pour ce qui est de awk, on n'a pas étudier, et le prof veut kon se debrouille avec cut, sed, grep, wc, head, ...

merci

voici le fichier a traiter :

Code:Societe:C.A.:C.A.prevu:Variation:Contact:Fonction:Adresse:Ville
ALFKI:Alfred's Futterkiste:77.8050525:103.85014:1.33474803:Maria Anders:Representante:Obere Str.57:Berlin
ANATL:L'anatra laccata:771.700421:834.332148:1.08116068:Anna Grandi:Proprietaire:Via Gramsci 209:Verona
ANTOB:Antonio Berbi Salumi:290.31742:411.875041:1.41870592:Antonio Berbi:Proprietaire:V
AROUT:Around the Horn:322.144627:642.261176:1.99370445:Thomas Hardy:Representant:Brook Farm Stratford St. Marc:Colchester
BERGS:Berglunds snabbkop:382.752211:467.260175:1.22079027:Christina Berglun:Proprietaire:Berguvsvgen 8:Lulea
LONP:Bldere et fils:592.578837:1142.53984:1.92808073:Frederique Citeau:Directeur du marketing:24, place Kleber:Strasbourg
BOLID:Bolinderska boden:892.210157:1434.03482:1.60728367:Kalle Berglund:Proprietaire:Lannavagen 16:Halmstad
BONAP:Bon app':344.275299:460.997874:1.33903848:Laurence Lebihan:Proprietaire:12, rue des Bouchers:Marseille
BOTTM:Bottom-Dollar Markets:908.267935:1190.48452:1.31071953:Elizabeth Lincoln:Chef comptable:23 Tsawassen Blvd.:Tsawassen
BSBEV:B's Beverages:787.856889:1381.77378:1.75383855:Victoria Ashworth:Representante:Fauntleroy Circus:London
CACTP:Cactus Pete's Family Market:659.72765:921.363771:1.39658201:Murray Soderholm:Assistant des ventes:87 Yuca Dr.:Albuquerque
CENTC:Centro Commerciale Bonoli:43.294606:60.9437437:1.40765212:Olivia Monti:Directeur du marketing:Plazza Locatelli 76:Napoli
EASTC:Eastern Connection:762.751309:909.149732:1.19193467:Ann Devon:Assistante des ventes:35 King George:London
ERNSH:Ernst Handel:201.751954:326.287311:1.61726964:Roland Mendel:Chef des ventes:Kirchgasse 6:Graz
FOLIG:Folies gourmandes:416.470845:571.186391:1.3714919:Martine Rance:Assistante representant:184, chaussee de Tournai:Lille
FOLKO:Folk och fa HB:633.300708:1176.40537:1.85757786:Maria Larsson:Proprietaire:kergotan 24:Brucne
FRANK:Frankenversand:483.47381:680.545535:1.40761613:Peter Franken:Directeur du marketing:Berliner Platz 43:München
FRANR:France restauration:409.378826:704.447839:1.72077253:Carine Schmitt:Directeur du marketing:54, rue Royale:Nant
FRANS:Franchi S.p.A.:918.387741:1558.80813:1.69733116:Paolo Accorti:Representant:Via Monte Bianco 34:Torino
FUJO:Furia Bacalhau e Frutos do:700.655465:944.937356:1.34864766:Lino Rodriguez:Chef des ventes:Jardim das rosas n. 32:Lisboa
fredmarseil
Matelot
Matelot
 
Messages: 10
Inscrit le: 15 Nov 2004 20:58

Messagepar jdh » 15 Nov 2004 23:22

Pourtant, typiquement, ce type de fichier est à traiter avec "awk".

Dans la section BEGIN, on lit une ligne (la première donc c'est à dire les noms de champs).
- la commande split va permettre de découper chaque élément de la ligne grace au séparateur ":".
- on sauve les nom de champs.
- on initialise les longeurs de chaque champ à 0.

Dans la boucle de traitement,
- même usage de split
- boucle sur chaque élément : si longueur > longueurmax alors longueurmax=longueur

Dans la section END,
- boucle sur chaque champ : print nom du champ, longueurmax

12-15 lignes, efficace !


Si on veut faire un bash script (avec les cut, sed, et autres ...) c'est plus difficile car la manipulation des chaines (notamment la longueur) n'est pas simple.
Avatar de l’utilisateur
jdh
Amiral
Amiral
 
Messages: 4741
Inscrit le: 29 Déc 2002 01:00
Localisation: Nantes

Messagepar tomtom » 15 Nov 2004 23:45

Hum, ca m'a fait un bon exercice de révision.

Cadeau :

Code: Tout sélectionner
#!/bin/bash


####
# Parsing d'un fichier texte
#
# Ca serait mieux avec awk mais 'parait que c'est pas bien
#
# TomTom
#
###

### Quelques initialisations

FICHIER_TXT=./test.txt
NB_LIGNE=$(wc -l $FICHIER_TXT)
NB_COL=1
while [ ""$(head -1 $FICHIER_TXT | cut -d ":"  -f $[$NB_COL+1]) != "" ]
do
        NB_COL=$[NB_COL+1]
done

for ((COL=1;COL<=$NB_COL;COL++))
do
        COLONE[$COL]=$(head -1 $FICHIER_TXT | cut -d ":" -f $COL)
        MAX_CHAMP[$COL]=0
done



### Boucle de calcul

for ((NUM_LIGNE=2;NUM_LIGNE<=$NB_LIGNE;NUM_LIGNE++))
do
        LIGNE=$(head -$NUM_LIGNE $FICHIER_TXT | tail -1)
        for ((COL=1;COL<=$NB_COL;COL++))
        do
                CHAMP=$(echo $LIGNE | cut -d ":" -f $COL)
                LONG_CHAMP=$[$(echo $CHAMP |wc -m)-1]
                if (( $LONG_CHAMP > ${MAX_CHAMP[$COL]} ))
                then
                        MAX_CHAMP[$COL]=$LONG_CHAMP
                fi
        done
done


### Affichage

for ((COL=1;COL<=$NB_COL;COL++))
do
        echo "La longueur maximale du champ n°$COL \"${COLONE[$COL]}\" est : ${MAX_CHAMP[$COL]}"
done



Essaye quand mêem de comprendre tout ce que ça fait, ça t'evitera des ennuis.
Et ce serait quand même vachement mieux avec awk... Mais peut-etre que ton prof utilise ce pretexte pour vous convaincre que awk c'est moins fatiguant :lol:

a+

T.
One hundred thousand lemmings can't be wrong...
Avatar de l’utilisateur
tomtom
Amiral
Amiral
 
Messages: 6035
Inscrit le: 26 Avr 2002 00:00
Localisation: Paris

Messagepar jdh » 15 Nov 2004 23:56

Une fois de plus, Tomtom est le plus rapide !


En awk, j'ai trouvé :

Code: Tout sélectionner
#!/usr/bin/awk -f

BEGIN { FS=":";
        getline;
        for (i=1; i<=NF; i++) { ch[i]=$(i); lg[i]=0; };  }

      { for (i=1; i<=NF; i++) { l=length($(i)); if (l>lg[i]) lg[i]=l; };  }

END   { for (i=1; i<=NF; i++) {print "Champ \"" ch[i] "\" longueur max " lg[i];};  }


Je suis désolé 6 lignes (dont 1 pour shabang (?) : #!xxxx)

Je vais regarder quand même le script bash de Tomtom !
Avatar de l’utilisateur
jdh
Amiral
Amiral
 
Messages: 4741
Inscrit le: 29 Déc 2002 01:00
Localisation: Nantes

Messagepar neox » 16 Nov 2004 00:16

on doit pouvoir le faire en perl aussi, mais là j'ai la flemme

ca donnerait un truc dans le style
Code: Tout sélectionner
#!/bin/perl -w
FIC=open(monfichier.txt)
while (<FIC>)
{
   $LIGNE=$_;
    compte les champs($LIGNE);
    compte_les_caracteres($LIGNE);
....

}

close(FIC);



bon ok pas mieux que 6 lignes :-(
Avatar de l’utilisateur
neox
Enseigne de vaisseau
Enseigne de vaisseau
 
Messages: 179
Inscrit le: 29 Fév 2004 01:00

re

Messagepar fredmarseil » 16 Nov 2004 00:24

Ah merci tout le monde, c genial tout ca, bon v essayer de comprendre quand même;
v avoir une bonne note je sens lol., si le prof il comprend pas que je me suis fait aidé ;-)

merci pour le temps que vous y avait passé -)


à bientôt :-)
fredmarseil
Matelot
Matelot
 
Messages: 10
Inscrit le: 15 Nov 2004 20:58

Messagepar jdh » 16 Nov 2004 03:25

Désolé, Tomtom, j'ai eu du mal à comprendre ton script.
Surtout il saute au yeux qu'il lit le fichier sans arret (head -1, tail , ...)
Il me semblait important de traiter le fichier en une seule lecture si possible.

Après un travail assez long, à me repencher sur le scripting bash, j'ai abouti à ceci (certes plus long mais plus rapide) :

Code: Tout sélectionner
#!/bin/bash
#
#  calcul des longueurs de champs
#


# le fichier a analyser est en parametre $1
FIC=$1


# declaration des variables
numch=0             # nb de champs (+1)
ch=(" ")            # noms des champs
lg=(0)              # longueurs max des champs
trchamp=1           # 1 pour la premiere ligne puis 0


# boucle de parcours du fichier (noter que le fichier est au "done" final)
while read ligne
do
  OIFS=$IFS
  # astuce : decoupage de $ligne grace au : (=$IFS)
  IFS=":"
  set $ligne
  if [ "$trchamp" = "1" ]
  then
    # traiter la ligne des champs
    numch=1
    until [ -z "$1" ]
    do
      ch[$numch]=$1        # nom du champ
      lg[$numch]=0
      shift
      numch=$[numch+1]
    done
    trchamp=0
  else
    # traiter les autres lignes
    i=1
    until [ -z "$1" ]
    do
      v=$1
      l=${#v}              # longueur du champ
      if [ "$l" -gt "${lg[$i]}" ]
      then
        lg[$i]=$l
      fi
      shift
      i=$[i+1]
    done
  fi
  IFS=$OIFS
done <$FIC


# affichage resultat
i=1
while [ "$i" -lt $numch ]
do
  echo "champ \"${ch[$i]}\" longueur max : ${lg[$i]}"
  i=$[i+1]
done




A noter l'astuce essentielle :
IFS=":" -> séparateur :
set $ligne -> transformation de la variable en parametre ($x)
until [-z "$1] do ... shift; done -< boucle de parcours des paramêtres un par un !

J'ai copié ta syntaxe pour incrémenter mais j'écrit parfois "let "i+=1" qui marche aussi.
A noter que j'écris "while read do ... done <$FIC" et non "cat $FIC | while read do ... done".
Cela posait un sérieux problème de variable mis à jour à l'intérieur d'un sous-shell ;=)
J'ai mis un temps énnnnaurme (oui) à comprendre ce qui clochait.

J'ai testé : cela donne les mêmes résultats que mon script awk (ouf!). Mais awk est vraiment idéal.
Avatar de l’utilisateur
jdh
Amiral
Amiral
 
Messages: 4741
Inscrit le: 29 Déc 2002 01:00
Localisation: Nantes

Messagepar Jacques- » 16 Nov 2004 09:45

Pour lire juste la ligne 3 du fichier, essayez donc avec :
awk 'NR==3' MonFichierQueJeVeuxLire

(NR : Number Record, == : Egalité stricte, 3 : Valeur demandée)

Jacques
Avatar de l’utilisateur
Jacques-
Vice-Amiral
Vice-Amiral
 
Messages: 952
Inscrit le: 23 Jan 2003 01:00

Messagepar tomtom » 16 Nov 2004 10:23

jdh a écrit:Désolé, Tomtom, j'ai eu du mal à comprendre ton script.
Surtout il saute au yeux qu'il lit le fichier sans arret (head -1, tail , ...)
Il me semblait important de traiter le fichier en une seule lecture si possible.


Tu as tout à fait raison..

J'ai ecrit ca en 5 minutes et j'ai fait au plus court... Je me suis bien rendu compte que je relisais tout le fichier à chaque fois, mais j'ai eu le flemme ( :oops: ) de réécrire la boucle à ta façon ;)

Le shift est élégant... [-o<

a+

t.
One hundred thousand lemmings can't be wrong...
Avatar de l’utilisateur
tomtom
Amiral
Amiral
 
Messages: 6035
Inscrit le: 26 Avr 2002 00:00
Localisation: Paris

Messagepar Franck78 » 16 Nov 2004 11:30

@jdh

Ton algo quelques messages plus avant est bon cependant la traduction en bash n'est pas top.

Tu inclus dans la boucle principale un test complétement inutile puisqu'exécuté une fois et pour la première ligne ;-) Rien à faire dans la boucle...

read ligne
init des champs

while read ligne
traitement de chaque ligne de data


Utilité de OIFS=IFS dans la boucle ?



Bye
Franck
L'art de poser une question sur ce site afin d'obtenir la réponse
A LIRE
Avatar de l’utilisateur
Franck78
Amiral
Amiral
 
Messages: 5625
Inscrit le: 20 Fév 2004 01:00
Localisation: Paris

Suivant

Retour vers Linux et BSD (forum généraliste)

Qui est en ligne ?

Utilisateur(s) parcourant actuellement ce forum : Aucun utilisateur inscrit et 0 invité(s)