Premières manipulation de chaînes


Dernière intervention le 25/11/2003

Premier projet : lire le fichier texte contenu dans cette page et le transformer en fichier HTML en ajoutant :
- un en-tête simplifié
- les balises de fin de paragraphe
- la fin de fichier

10. Définition et manipulation de chaine


Sur l'internet, on peut trouver des propositions de code pour la concaténation en utilisant la syntaxe de Gforth. C'est le cas de la séquence suivante ...
Le codage me semble très inspiré de "la pensée unique C" et ne me satisfait pas. Ce qui suit propose une approche différente, dont on pourra choisir de s'inspirer ou non.
Le texte contenu dans la chaine exemple servira pour différents tests, ce qui explique son contenu un peu surprenant.

Mettre une chaine sur la pile


Gforth dispose du mot ***D s" ***F pour empiler une chaîne (sur la pile... pléonasme).
Par exemple, la séquence :
s" ##Ceci est le titre de rang deux, il sera suivi d'une [http://lerautal.free.fr->Adresse de mon site ainsi que du texte {en italique} et même {{en gras}}" ok
...empile successivement l'adresse de la chaîne puis sa longueur.
Il est facile de vérifier ceci en entrant frappant .s
{ exemple : }
.s <2> 134691336 152 ok
(ici <2> = profondeur de la pile, 134691336 = adresse de la chaîne, 152 = longueur de celle-ci.
Remarque : la documentation de Gforth signale que le comportement de s" est différent à la compilation et à l'exécution.

Créer une variable destinée à stocker une chaîne


Gforth dispose de deux mots complémentaires :
create
crée une entrée dans le dictionnaire (et crée donc la variable);
nb allot
réserve "nb" octets pour le stockage dans cette variable.

Application : créer une variable appelée source, de 256 octets (dont le premier sera réservé pour stocker la "longueur utile".
create depart 256 allot
Vérification :
depart
(empile l'adresse de la variable)
.s <3> 134691336 152 1075479616 ok
(ici la pile s'est augmentée d'une valeur qui correspond à l'adresse de la variable).

Stockage d'une chaine de caractères


Les conventions suivantes ont été choisies :
- le premier octet contient la "longueur utile" de la chaine
- les octets suivants contiennent le texte lui-même
- la longueur utile représente le nombre d'octets du texte effectivement rangé dans la chaine. Par exemple si l'on range "pomme" dans une chaine de 256 caractères, la longueur utile sera de 5 caractères.
On charge la longueur utile ainsi :
depart longueur c!

Charger une variable chaîne


Gforth dispose du mot ***Dcmove ***F qui est décrit ainsi dans la documentation de Gforth :
cmove c-from c-to u -- string ``c-move''

Copy the contents of ucount characters from data space at c-from to c-to. The copy proceeds char-by-char from low address to high address; i.e., for overlapping areas it is safe if c-to=<c-from.
Syntaxe :
addr_source addr_cible 1 + nb_d'octets_à_copier cmove
Si maintenant, nous entrons au clavier
s" texte..." variable cmove
... nous allons rencontrer une difficulté du fait que nb_d'octets_à_copier est mal placé dans l'ordre d'exécution.
La bonne liste d'instruction est
s" ##Ceci est le titre de rang deux, il sera suivi d'une [http://lerautal.free.fr->Adresse de mon site] ainsi que du texte {en italique} et même {{en gras}}" ( texte et longueur empilés -- adr1 nb )
dup ( -- adr1 nb nb )
depart dup ( -- adr1 nb nb adr2 adr2 )
rot ( -- adr1 nb adr2 adr2 nb )
swap ( -- adr1 nb adr2 nb adr2 )
c! ( -- adr1 nb adr2 )
1 + ( -- adr1 nb adr2+1 )
swap cmove ( -- ) ***F

Afficher le contenu d'une variable


Gforth dispose du mot
type
Syntaxe :
addr_variable nb_caracteres type
{Exemples :}
depart 1 + 10 type
donne
"##Ceci est "
depart 11 + 20 type
donne
"le titre de rang de"
Remarque : l'adresse de début d'affichage a été décalée de 10 caractères.
Pour afficher la chaîne sans connaitre sa longueur, on fera :
depart dup c@ swap 1 + swap type
On pourra même créer un mot listant le contenu de la chaîne :
: l-chaine dup c@ swap 1 + swap type ;
que l'on utilisera ainsi :
depart l-chaine

11. Récapitulation


Voici le code source du script chaine.fs qui permet de vérifier ce qui a été écrit ci-dessus.
#! /usr/local/bin/gforth

create depart 256 allot
: l-chaine dup c@ swap 1 + swap type ;

s" ##Ceci est le titre de rang deux, il sera suivi d'une [http://lerautal.free.fr->Adresse de mon site] ainsi que du texte {en italique} et même {{en gras}}" ( texte et longueur empilés -- adr1 nb )
dup ( -- adr1 nb nb )
depart dup ( -- adr1 nb nb adr2 adr2 )
rot ( -- adr1 nb adr2 adr2 nb )
swap ( -- adr1 nb adr2 nb adr2 )
c! ( -- adr1 nb adr2 )
1 + ( -- adr1 nb adr2+1 )
swap cmove ( -- )

depart l-chaine

bye

11. Concaténation


Nous allons nous occuper d'un traitement élémentaire : ajouter <br /> à la fin de chaque paragraphe du fichier texte.
Pour pouvoir manipuler, nous garderons la chaîne "depart" de l'exemple précédent.

Opérations successives commentées


adr1 désigne l'adresse du contenu à concaténer (ici "<br />")
nb1 désigne la longueur de cette chaine
adr2 désigne l'adresse de début de la chaine cible (ici la variable depart)
nb2 désigne la longueur utile de cette chaine
adr3+1 désigne le début de la zone où doit s'effectuer la copie de "<br/>"
A la fin, le premier octet de depart contient la nouvelle longueur.

Le programme Gforth résultant


depart ( -- adr2 )
dup ( -- adr2 adr2 )
c@ ( -- adr2 nb2 )
+ ( -- adr3 )
s" <br/>" ( -- adr3 adr1 nb1 )
dup ( -- adr3 adr1 nb1 nb1 )
depart ( -- adr3 adr1 nb1 nb1 adr2 )
c@ ( -- adr3 adr1 nb1 nb1 nb2 )
+ ( -- adr3 adr1 nb1 nb3 )
depart ( -- adr3 adr1 nb1 nb3 adr2 )
c! ( -- adr3 adr1 nb1 )
rot ( -- adr1 nb1 adr3 )
1 + ( -- adr1 nb1 adr3+1 )
swap ( -- adr1 adr3+1 nb1 )
cmove ( -- )
Vérification par
depart l-chaine ( liste le contenu )

12. Remise en cause de ce qui précède


Le travail présenté aux paragraphes 10 et 11 est-il cohérent avec ce qui se passe quand on lit et écrit un fichier de texte ?
Le script suivant permet de vérifier :

#! /usr/local/bin/gforth

256 Constant max-line
Create chaine max-line allot
( ouverture du fichier source )
s" page.txt" r/o open-file throw Value fd-in
( charge la première ligne du fichier texte dans la variable chaine )
chaine max-line fd-in read-line throw
fd-in close-file throw ( ferme le fichier source )

Test sur la longueur de la chaîne

chaine c@ . donne 71
Or 71 est le code ASCI de la lettre G, qui est la première lettre du texte, et non dsa longueur.
Si maintenant, on veut lister la chaine, on pourra entrer :
chaine 50 type qui donne :
Gloire au dix-septième
ÌÌÌÌÌ3...
On constate un saut à la ligne (FF = code ASCI 12) au 22ème caractère.

Déterminer la longueur de la chaine lue


La longueur utile de la chaine est ici de 22 caractères. Y avait-il un moyen de le savoir ?
Une réponse un peu plus attentive de la documentation de Gforth permet de répondre oui.
La définition de read-line est la suivante :
read-line c_addr u1 wfileid - u2 flag wior
... et l'on remarque que u2, longueur de la chaine fait partie des éléments transmis par le mot Forth.
Nous allons récupérer cette valeur dans une variable numérque appelée n1 par :
variable n1 0
chaine max-line fd-in read-line drop drop n1 !
... puis vérifier que le résultat est le bon par :
." longueur : " n1 @ .
cr
." contenu : " chaine n1 @ type

Résultat


Il est possible de récupérer la longueur de la chaîne au moment où on la lit dans le fichier texte.
Le script ci-dessous, appelé creepageconcat.fs, permet la réalisation du premier projet :
#! /usr/local/bin/gforth

256 Constant max-line
Create chaine max-line allot
variable n1 0 ( longueur de la chaine lue dans le fichier )
variable n2 0 ( longueur de la chaine ajoutée )
variable n3 0 ( longueur résultante )

( ouverture des fichiers cible et source )
s" /home/alain/siteforth/page3.html" w/o create-file throw Value fd-out
s" /home/alain/siteforth/page3.txt" r/o open-file throw Value fd-in

: ecri-dans ( écrit dans le fichier cible ) fd-out write-file throw ;
: ferm-dans ( ferme le fichier cible ) fd-out close-file throw ;
: ferm-source ( ferme le fichier source ) fd-in close-file throw ;
: concat_br ( concaténation )
s" <br />" dup n2 !
chaine n1 @ + swap cmove
n2 @ n1 @ + n3 !
12 n3 @ chaine + c! ;

: vide-fichier ( effectue le transfert source vers cible )
begin
chaine max-line fd-in read-line throw
swap dup n1 ! swap

while
concat_br
chaine n3 @ ecri-dans
repeat ;

( création de la page web cible )
s" <html> <body>" ecri-dans
vide-fichier
s" </body></html>" ecri-dans

( fermeture des deux fichiers )
ferm-source
ferm-dans

bye

13. Conclusions provisoires


Le script permet de réaliser le premier projet.
Il faut parfois expérimenter beaucoup... surtout si on lit mal la documentation.


Haut de page