IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Free Pascal Discussion :

Stocker des sous-parties d'une chaîne : isoler les paramètres d'une commande [Free Pascal]


Sujet :

Free Pascal

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    72
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Octobre 2011
    Messages : 72
    Points : 38
    Points
    38
    Par défaut Stocker des sous-parties d'une chaîne : isoler les paramètres d'une commande
    Bonjour,
    Voilà pour le contexte : je voudrais faire un jeu de hacking.
    Le joueur rentrerait des commandes pour trouver une adresse IP, essayer de passer le firewall, etc, etc... je n'y connais rien en hacking mais j'ai toujours eu envie de m'y essayer, malheureusement ce n'est pas simple
    Du coup, je me suis dit que j'allais faire un jeu pour ceux qui sont comme moi intéressés par le hacking, bien sûr ce serait pour de faux mais assez réaliste tout de même
    Donc voilà, je veux imiter le comportement d'une console UNIX.
    Je définirai mes propres commandes, écrirai le mode d'emploi de chaque commande moi-même, etc... Voilà pour la théorie

    Malheureusement en pratique ce beau rêve est gâché par un problème ma foi relativement simple : et pourtant, je ne vois pas l'erreur !
    Il s'agit de récupérer la commande que va entrer l'utilisateur et d'en repérer les composantes :
    [cmd] [-opt] [arg]
    Cette syntaxe ne doit pas vous être inconnue
    Donc en gros, l'utilisateur saisit une chaîne de caractères, et moi j'identifie les morceaux et je stocke le nom de la commande dans une variable, l'option dans une autre, et l'argument dans une troisième variable.
    Et évidemment, si la saisie de l'utilisateur n'est pas de cette forme, eh bien ça plante
    Voilà le code que j'ai déjà écrit.

    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
    program hack;
     
    var cmd, ch, opt, arg : string;
    	i, comptEsp : integer;
    begin
    	comptEsp := 0; cmd := ' '; opt := ' '; arg := ' ';
    	readln(ch);
    	for i := 1 to length(ch) do
    		if ch[i] = ' ' then comptEsp := comptEsp + 1;
    	if comptEsp = 0 then cmd := ch // Si il n'y a pas d'espace dans la chaîne alors la chaîne est une commande seule
    	else // Sinon, alors on va séparer la chaîne en morceaux
    	begin
    		i := 1;
    		while (ch[i] <> ' ') do // On commence par mettre le premier morceau de la chaîne dans la var. cmd
    		begin
    			cmd[i] := ch[i];
    			i := i + 1
    		end;
    		i := i + 1; // Au sortir du while, la position i correspond dans la chaîne à l'espace, donc on itère pour passer au premier caractère du morceau suivant
    		if comptEsp = 1 then 
    			while (ch[i] <> ' ') do // Si il y a un espace alors on met le second morceau de la chaîne dans la var. opt
    			begin
    				opt := ch[i];
    				i := i + 1
    			end;
    		i := i + 1;
    		if comptEsp = 2 then // Si il y a deux espaces alors la chaîne était de la forme [cmd] [-opt] [arg]  ( comme par ex. "ls -a MesDocuments" en code console )
    			while (i <= length(ch)) do
    			begin
    				arg[i] := ch[i];
    				i := i + 1
    			end
    		else // Si finalement il n'y avait qu'un espace alors le deuxième morceau ne peut qu'être un argument et non pas une option et donc on exécute le bloc suivant pour éviter de confondre à l'avenir
    		begin
    			arg := opt;
    			opt := ' ' // On réinitialise opt.
    		end
    	end;
    	writeln(cmd);
    	writeln(opt);
    	writeln(arg)
    end.
    Je précise qu'il n'y a pas de problème à la compilation, seulement le résultat obtenu n'est pas satisfaisant.
    Exemple : testez mon code avec ls -a Documents. ( bien sûr je ne reprendrai pas les mêmes commandes que celles existantes c'est juste un exemple )
    Le programme vous renverra, ligne après ligne :
    - La commande
    - L'option
    - L'argument
    Donc il devrait renvoyer :
    ls
    -a
    Documents
    Et pourtant il me renvoie :
    l
    [rien]
    [rien]
    Consternant !!!

  2. #2
    Rédacteur/Modérateur
    Avatar de M.Dlb
    Inscrit en
    Avril 2002
    Messages
    2 464
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Avril 2002
    Messages : 2 464
    Points : 4 311
    Points
    4 311
    Par défaut
    Et non ce n'est pas consternant !

    Le problème vient d'une "mauvaise" gestion des chaînes de caractères. Premièrement, il faut savoir que le premier octet d'une chaîne en Pascal contient la longueur de la chaîne, ce premier octet on peut y accéder par l'intermédiaire de l'index 0 (souvenez vous les chaînes de caractères ne sont pas autres choses que des tableaux). Donc, la valeur stockée dans ch[0] doit retourner le nombre de caractères dans la chaîne ch...

    Autre chose, l'affectation d'une valeur à une variable String modifie également ce nombre. Le fait d'initialiser cmd à ' ' va donc écrire 1 dans cmd[0] puis l'équivalent du caractère ASCII espace (c'est-à-dire le code 32 en décimal). Cependant, si on travaille comme avec un tableau, case par case (comme tu le fais), aucun mécanisme ne met à jour ce compteur en positon [0].

    Sachant ces deux points, tu as affecté à cmd, opt et arg les valeurs ' ', donc la taille de la chaîne que ces variables contiennent sera de 1 caractère (mais attention comme ce sont des variables de type string standard sans spécification de taille, 256 octets sont alloués...). Cela veut dire que si tu fais des ajouts dans la chaîne (sans dépasser 255 caractères !) le compteur en position [0] n'est jamais modifié, donc l'ordinateur croira que ta chaîne fait toujours 1 caractère de long. D'où le fait que ton affichage ne fasse qu'une seule lettre (pour le deuxième et troisième il n'affiche rien du tout ou il affiche un espace te faisant croire qu'il n'affiche rien ?...)

    Heureusement y'a une parade. Au lieu de travailler case par case, tu peux utiliser la concaténation de chaîne (opérateur +) qui met à jour le fameux compteur... La ligne

    devient

    Pareil pour les autres !
    M.Dlb - Modérateur z/OS - Rédacteur et Modérateur Pascal

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    72
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Octobre 2011
    Messages : 72
    Points : 38
    Points
    38
    Par défaut
    Merci beaucoup pour ces explications je me suis repenché sur mon code, corrigé et optimisé deux-trois trucs par là et voilà le résultat 100% fonctionnel pour ceux que ça intéresse !!!!

    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
    program hack;
     
    var cmd, ch, arg : string[50];
    	i, comptEsp : integer;
    	opt : char;
    begin
    	comptEsp := 0;
    	readln(ch);
    	for i := 1 to length(ch) do
    		if ch[i] = ' ' then comptEsp := comptEsp + 1;
    	if comptEsp = 0 then cmd := ch // Si il n'y a pas d'espace dans la chaîne alors la chaîne est une commande seule
    	else // Sinon, alors on va séparer la chaîne en morceaux
    	begin
    		i := 1;
    		while (ch[i] <> ' ') do // On commence par mettre le premier morceau de la chaîne dans la var. cmd
    		begin
    			cmd := cmd + ch[i];
    			i := i + 1
    		end;
    		i := i + 1; // Au sortir du while, la position i correspond dans la chaîne à l'espace, donc on itère pour passer au premier caractère du morceau suivant
    		if comptEsp = 2 then // Si il y a 2 espaces dans la chaine on sait que le deuxième morceau est une option
    		begin
    			i := i + 1;   // On itère pour passer au-dessus du tiret de l'option qui ne nous intéresse pas
    			while (ch[i] <> ' ') do
    			begin
    				opt := ch[i];
    				i := i + 1
    			end;
    			i := i + 1;
    			while (i <= length(ch)) do
    			begin
    				arg := arg + ch[i];
    				i := i + 1
    			end
    		end
    		else if comptEsp = 1 then // Si la chaîne n'a qu'un espace alors le second morceau est un argument
    			while (i <= length(ch)) do
    			begin
    				arg := arg + ch[i];
    				i := i +1
    			end
    		else // Si la chaîne ne comporte pas 0, 1 ou 2 espaces alors ça plante
    			writeln('La requête n''a pu être effectuée.')
    	end;
    	writeln('|',cmd,'|');
    	writeln('|',opt,'|');
    	writeln('|',arg,'|')
    end.
    Merci encore à toi

  4. #4
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    un petit conseil, tu devrais écrire une fonction qui lit une chaîne et retourne un argument...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    function GetSubString(const Str: string; var Start: Integer; var SubString: string): Boolean;
    begin
      // SubString = la sous-chaine à partir de Start jusque la fin ou un espace
     // au retour, Start pointe sur la nouvelle position de départ
     // retour False si on est en fin de chaîne
    end.
    Et voici à quoi pourrait ressemble le code...
    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
     
    var
      Ch: string;
      Cmd: string;
      Index: Integer;
    begin
      ReadLn(Ch);
      Index := 1;
      if GetSubString(Ch, Index, Cmd) then
      begin
        if Cmd = 'ls' then
         ls(Ch, Index)
        else
         WritELn('Commande inconnue');
      end;
    end;
    de même, la fonction LS
    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
     
    procedure ls(const Ch: string; Index: Integer);
    var
      Opt : string;
      OptA: Boolean;
      Arg : string;
    begin
      while GetSubString(ch, Index, Opt) do
      begin
        if Opt[1] = '-' then
        begin
          if Opt = '-a' then
             OptA := True
          else begin
             WriteLn('Option inconnue');
             Exit;
          end;
        end else 
          if Arg = '' then
            Arg := Opt
          else begin
            WriteLn('Trop de paramètres');
            Exit;
          end;
      end;
      if Arg = '' then
      begin  
        WriteLn('Paramètre obligatoire');
        Exit;
      end;
      ...
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 15
    Dernier message: 17/03/2015, 22h42
  2. Réponses: 4
    Dernier message: 19/11/2014, 16h44
  3. Réponses: 4
    Dernier message: 30/11/2011, 22h31
  4. Réponses: 2
    Dernier message: 09/03/2009, 14h28
  5. Réponses: 4
    Dernier message: 22/05/2007, 14h42

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo