IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

matser

Un convertisseur unix2dos

Noter ce billet
par , 31/03/2021 à 03h13 (16917 Affichages)
Un convertisseur unix2dos
Les fichiers texte unix ont les fin de lignes indiqué avec le caractère de contrôle LF (line feed), tandis que ceux de windows sont les deux caractère
de contrôle CR (carriage return) et LF (line feed).
Pour convertir un fichier unix en fichier dos, rien de plus simple, il exste
des lignes de commandes pour cela. par exemple :

unix2dos -n fichier_entrée fichier_sortie

ou bien :

sed -i 's/\n/\r\n/g' fichier

Je vais vous proposer ici de faire la même chose, mais en un peu plus
compliqué, pour le fun. Il s'agit d'écrire un convertisseur unix2dos à la
manière d'un compilateur prar descente récursive prédictive suivant une
grammaire LL.
Ce convertisseur est constitué de 3 modules:
* Le scanneur, qui fournis à l'analyseur lexical les caractères du fichier
d'entrée.
* L'analyseur lexical, qui rassemble ces caractères en lexèmes et fournit
les unité lexicales pour l'analyseur syntaxique.
* L'analyseur syntaxique qui vérifie la bonne syntaxe du flot d'unité
lexical, puis émet une traduction.

le scanneur et l'analyse lexical

je ne m'attarderais pas sur le scanneur et l'analyseur lexical, car ce n'est
pas le but du sujet.
Sachez seulement que le scanneur lit les caractères du fichier source, et
les transmet,un par un, à l'analyseur lexical. Quant à ce dernier, il
regroupe ces caractères, en fait un lexème et retourne l'unité lexicale
correspondant à ce lexème, à l'analyseur syntaxique.

commun.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
#ifndef COMMUN_HPP
#define COMMUN_HPP

enum terminal{rc,car,dolar}; /*dolar est le terminal qui n'apparait
     			     dans  aucune grammaire. Il est utilisé pour
			     signifier la fin du fichier d'entrée*/
#endif
unilex.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef UNILEX_HPP
#define UNILEX_HPP

#include <string>
#include <vector>
#include "commun.hpp"

class unilex{
public:
  terminal getlex();
  void setlex(terminal const &x);
  std::string getlexeme();
  void ajouterlexeme(char const &c);
  void raz();
protected:
  terminal unitlex;
  std::string lexeme;
};

#endif
unilex.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <string>
#include "unilex.hpp"
#include "commun.hpp"


terminal unilex::getlex(){
  return unitlex;
}
void unilex::setlex(terminal const &x){
  unitlex=x;
}
std::string unilex::getlexeme(){
  return lexeme;
}
void unilex::ajouterlexeme(char const &c){
  lexeme+=c;
}

void unilex::raz(){
  lexeme.clear();
}
scanneur.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef SCANNEUR_HPP
#define SCANNEUR_HPP

#include <fstream>
#include <string>

constexpr int taille=3000;

class scanneur{
public:
  scanneur(std::string const& nomFichier);
  char carsuiv();
  void reculer(int const &n);
  int getTailleFichier();
private:
  std::ifstream fichier;
  char tampon[taille+1];// +1 car sentinelle EOF
  int Ntampon;
  int enAvant;
  int derniers;
  int tailleFichier;
};

#endif
scanneur.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <fstream>
#include <iostream>
#include "scanneur.hpp"

scanneur::scanneur(std::string const& nomFichier):Ntampon(1),enAvant(0),
						  derniers(0){
  fichier.open(nomFichier.c_str(),std::ios::binary|std::ios::in);
  fichier.seekg(0,std::ios_base::end);
  tailleFichier=fichier.tellg();
  fichier.seekg(0,std::ios_base::beg);
  if(taille<=tailleFichier){
    fichier.read(tampon,taille);
    tampon[taille]=EOF;
  }
  else{
    derniers=tailleFichier;
    fichier.read(tampon,derniers);
    Ntampon=1;
    tampon[derniers]=EOF;
    enAvant=0;
  }
}

char scanneur::carsuiv(){
  if(tampon[enAvant]==EOF)
    if(enAvant!=taille)
      return tampon[enAvant++];//enAvant++ car on pourrait devoir reculer
    else if((Ntampon+1)*taille<=tailleFichier){//++ car tampon suivant
      fichier.read(tampon,taille);
      Ntampon++;
      enAvant=0;
      derniers=0;
      return tampon[enAvant++];
    }
    else{
      derniers=tailleFichier%(Ntampon*taille);
      fichier.read(tampon,derniers);
      Ntampon++;
      tampon[derniers]=EOF;
      enAvant=0;
      return tampon[enAvant++];
    }
  else
    return tampon[enAvant++];
}

void scanneur::reculer(int const &n){
  for(int i=0;i<n;i++){
    if(enAvant==0){
      if(Ntampon>1){
	fichier.seekg((Ntampon-2)*taille,std::ios_base::beg);
   	fichier.read(tampon,taille);
	enAvant=taille-1;
      }
      else if(derniers!=0)
	enAvant=derniers-1;
      else
	enAvant=taille-1;
      Ntampon--;
    }
    else
      enAvant--;
  }
}

int scanneur::getTailleFichier(){
  return tailleFichier;
}
lexical.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#ifndef LEXICAL_HPP
#define LEXICAL_HPP

#include <vector>
#include <string>
#include <map>
#include "unilex.hpp"
#include "scanneur.hpp"

class lexical{
public:
  lexical(std::string const &nomfichier);
  int getligne();
  unilex analex();
private:
  void obtenirrc();
  void obtenircar1();
  void obtenircar2();
  void obtenircar3();
  void obtenircar4();
  void obtenirdolar();
  std::vector<std::string>motscles;
  std::map<std::string,terminal>dicolex;
  int debutlex,etat,ligne;
  char c;
  unsigned char usc;
  bool continuer,reussi;
  unilex ulex;
  std::string lexeme;
  scanneur scan;
  void echec();
};
#endif
lexical.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#include <iostream>
#include <string>
#include <vector>
#include <map>

#include "unilex.hpp"
#include "commun.hpp"
#include "lexical.hpp"
#include "scanneur.hpp"

lexical::lexical(std::string const &nomfichier):lexeme(""),debutlex(0),etat(0),
					 scan(nomfichier),ligne(1),
					 continuer(true) {/*corps vide*/}

void lexical::echec(){
  scan.reculer(debutlex);
  continuer=true;
  debutlex=etat=0;
  ulex.raz();
}

unilex lexical::analex(){
  while(true){
    continuer=true;
    debutlex=etat=0;
    ulex.raz();
 
    obtenirrc();
    if(reussi)
      return ulex;
    else
      echec();
 
    obtenircar1();
    if(reussi)
      return ulex;
    else
      echec();

    obtenircar2();
    if(reussi)
      return ulex;
    else
      echec();

    obtenircar3();
    if(reussi)
      return ulex;
    else
      echec();

    obtenircar4();
    if(reussi)
      return ulex;
    else
      echec();

    obtenirdolar();
    if(reussi)
      return ulex;
    else echec();

    std::cerr<<std::endl<<"ligne "<<getligne()<<": symbole hors langage"
	     <<std::endl<<scan.carsuiv()<<std::endl;
  }
}


int lexical::getligne(){
  return ligne;
}


void lexical::obtenirrc(){
  while(continuer)
    switch(etat){
    case 0:
      c=scan.carsuiv();
      debutlex++;
      if(c=='\n'){
	ligne++;
	etat=1;
      }
      else{
	continuer=false;
	reussi=false;
      }
      break;
    case 1:
      ulex.setlex(rc);
      reussi=true;
      continuer=false;
    }
}

void lexical::obtenircar1(){
  while(continuer)
    switch(etat){
    case 0:
      c=scan.carsuiv();
      debutlex++;
      usc=c; //usc est non signé
      if(usc<=0x7f){
	ulex.ajouterlexeme(c);
	etat=1;
      }
      else{
	continuer=false;
	reussi=false;
      }
      break;
    case 1:
      ulex.setlex(car);
      reussi=true;
      continuer=false;
    }
}

void lexical::obtenircar2(){
  while(continuer)
    switch(etat){
    case 0:
      c=scan.carsuiv();
      debutlex++;
      usc=c;// usc est non signé
      if(usc>=0xc2 && usc<=0xdf){
	ulex.ajouterlexeme(c);
	etat=1;
      }
      else{
	continuer=false;
	reussi=false;
      }
      break;
    case 1:
      ulex.setlex(car);
      c=scan.carsuiv();
      ulex.ajouterlexeme(c);
      reussi=true;
      continuer=false;
    }
}

void lexical::obtenircar3(){
  while(continuer)
    switch(etat){
    case 0:
      c=scan.carsuiv();
      debutlex++;
      usc=c;//usc est non signé
      if(usc>=0xe0 && usc<=0xef){
	ulex.ajouterlexeme(c);
	etat=1;
      }
      else{
	continuer=false;
	reussi=false;
      }
      break;
    case 1:
      ulex.setlex(car);
      c=scan.carsuiv();
      ulex.ajouterlexeme(c);
      c=scan.carsuiv();
      ulex.ajouterlexeme(c);
      reussi=true;
      continuer=false;
    }
}

void lexical::obtenircar4(){
  while(continuer)
    switch(etat){
    case 0:
      c=scan.carsuiv();
      debutlex++;
      usc=c;//usc est non signé
      if(usc>=0xf0 && usc<=0xf4){
	ulex.ajouterlexeme(c);
	etat=1;
      }
      else{
	continuer=false;
	reussi=false;
      }
      break;
    case 1:
      ulex.setlex(car);
      c=scan.carsuiv();
      ulex.ajouterlexeme(c);
      c=scan.carsuiv();
      ulex.ajouterlexeme(c);
      c=scan.carsuiv();
      ulex.ajouterlexeme(c);
      reussi=true;
      continuer=false;
    }
}

void lexical::obtenirdolar(){
  while(continuer)
    switch(etat){
    case 0:
      c=scan.carsuiv();
      debutlex++;
      if(c==EOF){
	etat=1;
      }
      else{
	continuer=false;
	reussi=false;
      }
      break;
    case 1:
      ulex.setlex(dolar);
      ulex.ajouterlexeme(c);
      reussi=true;
      continuer=false;
    }
}
syntaxique.hpp:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef SYNTAXIQUE_HPP
#define SYNTAXIQUE_HPP

#include <string>
#include "lexical.hpp"
#include "unilex.hpp"
#include "commun.hpp"

class syntaxique{
public:
  std::string F();
  syntaxique(std::string const &nomFichier);
private:
  std::string RF(std::string const &h);
  std::string P(std::string const &h);
  void consommer(terminal s);
  lexical pe;
  unilex a;
};
#endif

la grammaire hors contexte

la grammaire de ce compilateur est la suivante:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
fichier      -> paragraphe resteFichier
resteFichier -> retourChariot paragraphe resteFichier
	     -> epsilon (epsilon est la production vide)
paragraphe   -> caractère paragraphe
	     -> epsilon
"caractères" et "retourChariot" sont les terminaux.

"fichier" et "resteFichier" constituent une liste associative à gauche, et
éventuelllement vide, de paragraphes séparés par un retour chariot

"paragraphe" est une liste associative à droite, et éventuellement vide, de
caractères, sans séparateur.



le STDS (schéma de traduction dirigé par la syntaxe)

nous allons d'abord simplifier les noms:
F = fichier
P = paragraphe
RF = resteFichier
car = caractère
rc = retourChariot
dolar = fin de fichier (est un élément de suivant(F))

le stds résultant est le suivant:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
F  -> {P.h=""} P {RF.h = P.s} RF {F.s = RF.s}
RF -> rc {P.h=RF.h + "\r\n"} P {RF1.h = P.s}  RF1 {RF.s = RF1.s}
   -> epsilon {RF.s = RF.h}
P  -> car {P1.h = P.h + car.s} P1 {P.s = P1.s}
   -> epsilon {P.s = P.h}
les attributs se terminant par .s sont les attributs synthétisés et les
autres, se termaniant par .h, sont les attributs hérités.
dans un arbre syntaxique décoré (voir un exemple dans la figure suivante),
les attributs synthétisés d'un nœud se calculent avec les attributs venant
des nœuds frères de droite et des nœuds fils, du nœuds considéré.
Les attributs hérités d'un nœud, se calculent avec les attributs des nœud
frères de gauche et du nœud parent de ce nœud.

Les productions F et RF indiquent une traduction dirigée par la
syntaxe d'une liste de P séparés par "\n\r", qui est la fin de ligne pour
les fichiers texte brut Windows.
La production P décrit le paragraphe, qui est une suite
de caractères, différents de CR et de EOF, et qui n'utilise pas de séparateurs.
Nom : arbre2.png
Affichages : 13
Taille : 5,7 Ko

déscente récursive prédictive

pour implémenter un stds par la méthode de la déscente récursive
prédictive, nous associerons les non terminaux à des fonctions.
Les terminaux seront représentés par un appel à la fonction "consommer" qui
vérifie que le lexème courrant est bien celui attendu, puis fournit l'unité
lexicale suivante. L'attribut synthétisé d'un non terminal est la
valeur de retour des fonctions qui représente ce non terminal. Les
attributs hérités sont les parmètres d'entrée de ces fonctions.
Pour éxecuter ces fonctions, on choisit dans cette fonction la séquence qui
correspond au premier symbole du non terminal. Ce symbole est indiqué par
la fonction (une fonction mathématique et non une fonction C++) appelé
"premier" dont la valeur dépend du non terminal courrant. Voici ces
valeurs:
premier(F)={car;rc;dolar}
premier(RF)={rc;epsilon}
premier(P)={car,epsilon}

la fonction F() devra s'écrire comme cela:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
std::string syntaxique::F(){
  std::string RFh,Ps,Fs,RFs;
  terminal x;
  x=a.getLex();
  if(x==car||x==rc||x==dolar){
    Ps=P("");
    RFh=Ps;
    RFs=RF(RFh);
    Fs=RFs;
    consommer(dolar);
  }
  return Fs;
}
voici le fichier syntaxique.cpp:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <string>
#include <iostream>
#include "syntaxique.hpp"

syntaxique::syntaxique(std::string const &nomFichier):pe(nomFichier){
  //pe est l'objet de la classe "lexical"
  a=pe.analex(); //ici, a est la première unité lexicale
}

std::string syntaxique::F(){
  std::string RFh,Ps,Fs,RFs,retour;
  terminal x;
  x=a.getlex();
  if(x==car||x==rc||x==dolar){
    Ps=P("");
    RFh=Ps;
    RFs=RF(RFh);
    Fs=RFs;
    consommer(dolar);
  }
  else{
    std::cerr<<"erreur ligne "<<pe.getligne();
    exit(1);
  }
  return Fs;
}

std::string syntaxique::RF(std::string const &h){
  std::string retour,Ps,RF1s,RF1h,RFs,Ph;
 if(a.getlex()==rc){ //unité lexicale rc
    consommer(rc);
    Ph=h+"\r\n";
    Ps=P(Ph);
    RF1h=Ps;
    RF1s=RF(RF1h);
    RFs=RF1s;
  }
  else
    RFs=h;
  return RFs;
}

std::string syntaxique::P(std::string const &h){
  std::string retour,cars,P1h,Ps,P1s;
  if(a.getlex()==car){
    cars=a.getlexeme();//getlexeme est le lexeme
    consommer(car);
    P1h=h+cars;
    P1s=P(P1h);
    Ps=P1s;
  }
  else
    Ps=h;
  return Ps;
}

void syntaxique::consommer(terminal s){
  if(a.getlex()==s){ // si l'unité lexicale lu est celle attendu
    if(s!=dolar)
      a=pe.analex();//unité lexicale suivante
  }
  else
    std::cerr<<"erreur à la ligne "<<pe.getligne()<<std::endl;
}
le fichier main.cpp:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
#include <iostream>
#include "syntaxique.hpp"


int main(int argc,char *argv[]){
  syntaxique S(argv[1]);
  std::cout<<S.F();
}
Par exemple:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
$ ./a.out fichier_entrée > fichier_sortie
$ xxd fichier_sortie

Envoyer le billet « Un convertisseur unix2dos » dans le blog Viadeo Envoyer le billet « Un convertisseur unix2dos » dans le blog Twitter Envoyer le billet « Un convertisseur unix2dos » dans le blog Google Envoyer le billet « Un convertisseur unix2dos » dans le blog Facebook Envoyer le billet « Un convertisseur unix2dos » dans le blog Digg Envoyer le billet « Un convertisseur unix2dos » dans le blog Delicious Envoyer le billet « Un convertisseur unix2dos » dans le blog MySpace Envoyer le billet « Un convertisseur unix2dos » dans le blog Yahoo

Mis à jour 12/08/2022 à 17h41 par matser

Catégories
Sans catégorie

Commentaires