Partage

Ecrire lettre avec code ascii

Le 29 septembre 2010 à 9:32:43

Bonjour :p
Voilà : je voudrais faire un programme qui m'écrit toutes les suites de lettres possibles entre AAAAA et ZZZZZ, et qu'il stocke chaque suite dans un fichier.

Sauf qu'il faut que mon programme soit le plus rapide possible. J'ai donc faire une boucle qui incrémente la variable nb, qui contient le nombre à transformer de base 10 à base 26. Je fais donc la conversion et j'obtiens donc 6 nombres entre 0 (A) et 25 (Z). Je voudrais donc écrire dans un fichier cette lettre. Je pourrais faire 26 tests pour savoir quel est cette lettre (Si lettre = 0, écrire A, si lettre = 1, ecrire B... Mais je trouve ça un peu long, et un peu bourrin.

Je voudrais donc ajouter 65 à ce nombre, puis écrire dans mon fichier le code ascii de la lettre :
A=65
B=66
...
Ce qui ferait que je n'aurais pas de tests à effectuer.

Comment faire ?

Merci d'avance ^^

--EDIT--
Oupss... J'ai posté trop vite.

J'arrive à faire ce que je veux faire via un tableau de char... :
char a=66;
cout << a << endl;

Désolé pour le dérangement !
Publicité
Le 29 septembre 2010 à 9:32:43
Zéro d'honneur Le 29 septembre 2010 à 9:50:50

Les codes ASCII (ou autre) pour ça, c'est de l'obfuscation.

Reste simple: for (char c='A' ; c<='Z' ; ++c)
Le 29 septembre 2010 à 14:25:38

L'obfuscation ? C'est quoi le rapport ? Ce n'est pas dissimuler une information sous une masse de fausses ?

En fait comme je veux avoir 6 caractères, for (char c='A' ; c<='Z' ; ++c) ne me permet pas de le faire. Sauf si on peut faire for ([inconnu] c='AAAAAA' ; c<='ZZZZZZ' ; ++c)

Est-ce possible ? Si oui comment ?

Merci d'avance ^^

--EDIT--
Je pourrais utiliser ta technique en imbriquant ces conditions... Je vais regarder. Mais si vous avez une solution pour le faire directement pour optimiser le code, je suis prenant !
Zéro d'honneur Le 29 septembre 2010 à 14:36:11

Obfusquer, c'est écrire 65 au lieu de 'A'. 65 est une constante magique que tout le monde ne connaitra pas par coeur, et en plus c'est un nombre. 'A', ben ... c'est un caractère. C'est sémantiquement très fort et sans le moindre doute quant à nos intention. Et en plus, c'est facile à maintenir.

Tu devras réaliser 26^6 itérations. Donc, avoir 6 boucles imbriquées est la solution canonique.
Tu pourras très certainement l'émuler avec un curseur à déplacer, mais de nouveau, ce sera de l'obfuscation : un code plus compliqué, incompréhensible au premier abord, et certainement plus lent par la même occasion.
Le 29 septembre 2010 à 14:44:11

Mon but n'est pas de cacher mon code... Juste qu'il soit le plus rapide possible.

Et à quoi servirait le curseur ? Je n'ai pas compris...

Et comment concaténer char et string ? J'ai fait :
char a='A';
    char b='B';
    string chaine2 = a+b;
    cout << chaine2 << endl;

Et j'ai une erreur :
main.cpp:42: error: invalid conversion from ‘int’ to ‘const char*’
main.cpp:42: error:   initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]’


Comment faire ?
Zéro d'honneur Le 29 septembre 2010 à 15:04:44

=string(a)+b;

Pour le "curseur" oublie.
Le 29 septembre 2010 à 15:17:38

Je vais essayer ce que tu as mis, mais entre temps j'ai trouvé ça (en bas : conversion depuis d'autres types.)
Ce qui me donne à la fin ce code qui marche plutôt bien :
for(char lettre1='A'; lettre1<='Z'; lettre1++)
	{
		for(char lettre2='A'; lettre2<='Z'; lettre2++)
		{
			for(char lettre3='A'; lettre3<='Z'; lettre3++)
			{
				for(char lettre4='A'; lettre4<='Z'; lettre4++)
				{
					
					strstream s;  
					s << lettre1 << lettre2 << lettre3 << lettre4 << endl;  
					string s2 = s.str(); // conversion en string  
					cout << s.str(); // affichage
					
					
				}
			}
		}
	}


J'essaye ton code et je te redis !

--EDIT--
Je viens de l'essayer et il plante :
char a='A';
	char b='B';
	string test = string(a)+b;
	cout << test << endl;

main.cpp:51: error: invalid conversion from ‘char’ to ‘const char*’
main.cpp:51: error:   initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]’

J'ai essayé plusieurs syntaxes de string(a)+b, mais rien... (string(a+b), string(a)+string(b)...)

Une idée ?

Note : j'aimerais trouver une autre méthode que celle que j'ai trouvée car à la compilation j'ai un message qui me dit que cette librairie va être remplacée... Donc si vous connaissez celle qui la remplace, dites le moi ^^
/usr/include/c++/4.3/backward/backward_warning.h:33:2: warning: #warning This file includes at least one deprecated or antiquated header which may be removed without further notice at a future date. Please use a non-deprecated interface with equivalent functionality instead. For a listing of replacement headers and interfaces, consult the file backward_warning.h. To disable this warning use -Wno-deprecated.


Merci d'avance ^^

--EDIT2--
#include <sstream> (stringstream) à l'air d'être mieux acceptée. Après, si vous avez des informations sur le technique de lmghs dites le moi ^^

Et merci beaucoup lmghs pour l'aide que tu m'as apporté !
Zéro d'honneur Le 29 septembre 2010 à 15:47:53

Tiens. C'est vrai.
Il fallait utiliser string(1,a).

Autre façon (non testée):
string res(6 /*, ' ' ?*/);
res[0] = 'A';
for (char & a = res[0] ; a <= 'Z' ; ++a) {
    res[1] = 'A';
    for (char & b = res[1] ; b <= 'Z' ; ++b) {
        res[2] = 'A';
        ....
              res[5] = 'A';
              for (char & f = res[5] ; f <= 'Z' ; ++f) {
                  cout << res;
              }
        ...
    }
}

Le 29 septembre 2010 à 16:07:17

Merci beaucoup lmghs pour ton aide ^^ Ca marche !
Voilà le fichier final :
#include <iostream>
#include <math.h>
#include <string.h>
#include <sstream>
#include <fstream>

using namespace std;

int main()
{
	ofstream fichier("/home/leo/Desktop/programmation/BIOS/bigdico", ios::out | ios::trunc);  //déclaration du flux et ouverture du fichier
	        
	if(fichier)  // si l'ouverture a réussi
	{
		// instructions
		
		for(char lettre1='A'; lettre1<='Z'; lettre1++)
		{
			for(char lettre2='A'; lettre2<='Z'; lettre2++)
			{
				for(char lettre3='A'; lettre3<='Z'; lettre3++)
				{
					for(char lettre4='A'; lettre4<='Z'; lettre4++)
					{
						for(char lettre5='A'; lettre5<='Z'; lettre5++)
						{
							for(char lettre6='A'; lettre6<='Z'; lettre6++)
							{
								stringstream s;  
								s << lettre1 << lettre2 << lettre3 << lettre4 << lettre5 << lettre6 << endl;  
								string s2 = s.str(); // conversion en string  
								cout << s2; // affichage
								fichier << s2;
	           					}
	           				}
	       										
						
					}
				}
			}
		}
		fichier.close();  // on referme le fichier
	}
	else
	cerr << "Erreur à l'ouverture !" << endl;
	
	return 0;
}


Bon, il faut pas être pressé... (en 8mn il a à peine fini le A pour première lettre.) et si mes calculs sont bon (bon, je les ai fait à 22h hier soir, donc il sont peux être faux) il prendra environ 1.2Go...

Par contre, peux-tu expliquer à quoi correspond les param. de string(1,a) ? A quoi correspond le 1 ? il faut donc faire string(1,a)+string(1,b) si j'ai bien compris.

Et pourrais-tu m'expliquer comment fonctionne ton 2eme programme ? Je ne comprend même pas la première ligne : 6 /*, ' ' ?*/. Les /* */ sont juste des commentaires où ils font parti intégrale de la chaine ? Et à quoi correspond le 6 ? Et pourquoi est-ce que tu considère res comme un tableau ? J'avoue que je suis un peu perdu...
Zéro d'honneur Le 29 septembre 2010 à 16:30:15

string(1,' ') fait appel à la 8e surcharge: http://dinkumware.com/manuals/?manual= [...] :basic_string
Et donc "string(1, a) + b" suffit.

Les commentaires, correspondent à un doute de ma part. Je n'ai pas l'impression qu'il existe une surcharge du constructeur qui ne prennent qu'un seul argument => string(6, 'A') ira très bien.
Le 6 sert à dire que la chaine fera donc 6 caractères. Et std::string met à disposition un opérateur d'accès indexé qui renvoie une référence vers le n-ième caractère demandé.
J'aurai tout aussi bien pu écrire: "for ( res[0] = 'A' ; res[0] <= 'Z' ; ++res[0])" six fois.
Cela évite de construire une chaine à chaque itération.

Accessoirement, le fichier fera plutôt entre 2 et 2.3 Go (26^6 * (6+1 +1 si format dos) / 1024 /1024 -> 2062/23xx)
Le 29 septembre 2010 à 18:41:37

Oh nonnnnnnn !!! Je viens de m'apercevoir après 2h45 que ce n'était pas des lettre majuscules, mais minuscules !!!!!

Est-ce que vous connaissez une technique pour RAPIDEMENT passer un fichier de 1,7Go (j'ai arrété à la 1ere lettre V) de majuscule à minuscule ?
Zéro d'honneur Le 29 septembre 2010 à 19:07:41

vim fichier
:s/.*/\L&\E/
:wq

Mais tu auras plus vite fait de regénérer le fichier.
Le 29 septembre 2010 à 19:14:23

Tu pense ?
Bon ben je ferais peut-être ça demain si j'ai le temps...

Merci beaucoup pour ton aide. Je vous redis si il y a du nouveau.
Le 30 septembre 2010 à 6:50:04

Citation : lmghs

Obfusquer, c'est écrire 65 au lieu de 'A'. 65 est une constante magique que tout le monde ne connaitra pas par coeur, et en plus c'est un nombre. 'A', ben ... c'est un caractère. C'est sémantiquement très fort et sans le moindre doute quant à nos intention. Et en plus, c'est facile à maintenir.

Tu devras réaliser 26^6 itérations. Donc, avoir 6 boucles imbriquées est la solution canonique.
Tu pourras très certainement l'émuler avec un curseur à déplacer, mais de nouveau, ce sera de l'obfuscation : un code plus compliqué, incompréhensible au premier abord, et certainement plus lent par la même occasion.


Il me semble aussi que c'est une histoire de portabilité, une machine pouvant posséder une table ASCII différente.
Le 30 septembre 2010 à 7:05:45

Que c'est moche toutes ces boucles imbriquées.
void inscrire_liste_chaines(std::ostream & out) /* tous les flux d'écriture héritent de ostream (si je ne me trompe pas) */
{
#define N 6 /* définit le nombre de caractères à mettre */
  char tab[N + 1]; /* un tableau pour retenir la combinaison actuelle */
  /* on remplit ce tableau */
  for(unsigned int i = 0;i<N;i++)
    tab[i] = 'a';
  tab[N] = '\0';

  /* on fait la boucle jusqu'à ce que le premier caractère ne soit plus une majuscule */
  while(tab[0] <= 'z')
  {
    /* on affiche dans n'importe quel flux la chaine */
    out << tab << std::endl;
    /* on incrémente le dernier caractère */
    tab[N-1]++;
    /* si on a un caractère supérieur à Z, on le fait revenir à A en augmentant la taille du précédent */
    for(unsigned int i = N-1;i > 0;i--)
    {
      if(tab[i] > 'z')
      {
        tab[i] = 'a';
        tab[i-1]++;
      }
      else
        break;
    }
  }
#undef N
}

On aura même la liste dans l'ordre croissant.
Si on veut afficher la liste dans plusieurs flux, il suffit de modifier très légèrement la fonction.

EDIT : modification du code pour avoir des minuscules.
Et n'oublie pas qu'il est plus rapide d'écrire dans un fichier que d'écrire dans std::cout (car ça doit être affiché à l'écran immédiatement).
Petite info : en 5 minutes qu'il tourne chez moi, il a rempli près de 300 Mio.
Zéro d'honneur Le 30 septembre 2010 à 8:48:58

Moche mouais. Pas évident.
Tout le monde ne va pas rentrer dedans aussi facilement. De plus, je ne serai pas surpris de constater un petit avantage de rapidité pour la version à 6 boucles.

PS: const, c'est bien.
Le 30 septembre 2010 à 12:43:27

Euh ca serait pas pour bruteforcer un mot de passe ton code?

Pour info
26^6 = 308 915 776
52^6 = 19 770 609 664
Le 30 septembre 2010 à 12:47:41

Les réinitialisations et incrémentations sont cachées dans les boucles for. Si on regarde, on verra que je fais exactement les mêmes opérations. Par ailleurs, je remplis le tampon en prenant 6 caractères à la fois.
Par ailleurs, et je l'ai déjà dit, l'opération la plus coûteuse est la mise dans un flux de chaque donnée. Il serait donc bon d'optimiser cette fonction pour le résultat attendu.
Je pourrais donc réécrire la ligne :
out << tab << std::endl;

en écrivant :
out.write(tab,N + 1);

Evidemment, il faudra avoir modifié la case tab[N] en '\n' au lieu de '\0' (write écrit les N + 1 premiers caractères de tab, sans vérification de '\0').

Pour ceux qui n'auraient pas suivi :
void inscrire_liste_chaines(std::ostream & out) /* tous les flux d'écriture héritent de ostream (si je ne me trompe pas) */
{
unsigned int const N = 6; /* définit le nombre de caractères à mettre */
  char tab[N + 1]; /* un tableau pour retenir la combinaison actuelle */
  /* on remplit ce tableau */
  for(unsigned int i = 0;i<N;i++)
    tab[i] = 'a';
  tab[N] = '\n';

  /* on fait la boucle jusqu'à ce que le premier caractère ne soit plus une majuscule */
  while(tab[0] <= 'z')
  {
    /* on affiche dans n'importe quel flux la chaine */
    out.write(tab,N+1);
    /* on incrémente le dernier caractère */
    tab[N-1]++;
    /* si on a un caractère supérieur à Z, on le fait revenir à A en augmentant la taille du précédent */
    for(unsigned int i = N-1;i > 0;i--)
    {
      if(tab[i] > 'z')
      {
        tab[i] = 'a';
        tab[i-1]++;
      }
      else
        break;
    }
  }
}

Je vais tester sous peu pour voir si l'exécution est plus rapide. (pour information, ma première exécution a duré environ 2 164 secondes)

EDIT pour le message plus haut : pourquoi voit-on tout de suite une tentative de piratage quand quelque chose s'y rapproche ?
Imaginons que je veuille coder un cryptage RSA. On va tout de suite me dire "c'est pour faire l'attaque par le milieu ?". Ca n'aurait absolument aucun sens.

EDIT : savoir ce qui prend le plus de temps de calculs permet d'améliorer nettement la vitesse d'exécution : 512 secondes (moins de 10 minutes) au lieu de 2 164 (environ 35 minutes).
Le 30 septembre 2010 à 13:20:42

Citation : Alienore

Les réinitialisations et incrémentations sont cachées dans les boucles for. Si on regarde, on verra que je fais exactement les mêmes opérations. Par ailleurs, je remplis le tampon en prenant 6 caractères à la fois.
Par ailleurs, et je l'ai déjà dit, l'opération la plus coûteuse est la mise dans un flux de chaque donnée. Il serait donc bon d'optimiser cette fonction pour le résultat attendu.
Je pourrais donc réécrire la ligne :

out << tab << std::endl;



en écrivant :

out.write(tab,N + 1);



Evidemment, il faudra avoir modifié la case tab[N] en '\n' au lieu de '\0' (write écrit les N + 1 premiers caractères de tab, sans vérification de '\0').

Pour ceux qui n'auraient pas suivi :

void inscrire_liste_chaines(std::ostream & out) /* tous les flux d'écriture héritent de ostream (si je ne me trompe pas) */
{
unsigned int const N = 6; /* définit le nombre de caractères à mettre */
  char tab[N + 1]; /* un tableau pour retenir la combinaison actuelle */
  /* on remplit ce tableau */
  for(unsigned int i = 0;i<N;i++)
    tab[i] = 'a';
  tab[N] = '\n';

  /* on fait la boucle jusqu'à ce que le premier caractère ne soit plus une majuscule */
  while(tab[0] <= 'z')
  {
    /* on affiche dans n'importe quel flux la chaine */
    out.write(tab,N+1);
    /* on incrémente le dernier caractère */
    tab[N-1]++;
    /* si on a un caractère supérieur à Z, on le fait revenir à A en augmentant la taille du précédent */
    for(unsigned int i = N-1;i > 0;i--)
    {
      if(tab[i] > 'z')
      {
        tab[i] = 'a';
        tab[i-1]++;
      }
      else
        break;
    }
  }
}




Je vais tester sous peu pour voir si l'exécution est plus rapide. (pour information, ma première exécution a duré environ 2 164 secondes)

EDIT pour le message plus haut : pourquoi voit-on tout de suite une tentative de piratage quand quelque chose s'y rapproche ?
Imaginons que je veuille coder un cryptage RSA. On va tout de suite me dire "c'est pour faire l'attaque par le milieu ?". Ca n'aurait absolument aucun sens.

EDIT : savoir ce qui prend le plus de temps de calculs permet d'améliorer nettement la vitesse d'exécution : 512 secondes (moins de 10 minutes) au lieu de 2 164 (environ 35 minutes).


Merci pour ce code ! il est très formateur. J'avais pensé à faire un code de ce type là mais j'avoue que j'avais peur que ça se complique. J'ai bien analysé le code et j'ai tout compris !

Merci beaucoup pour ce code très simple à paramétrer, et plus rapide de surcroit ^^

Et comment as-tu fais pour chronométrer ? Tu as mis une variable qui stocke la variable de début, puis un pour la variable de fin et tu compares les 2 ?

Merci beaucoup !
Zéro d'honneur Le 30 septembre 2010 à 13:40:55

Citation : Alienore

Les réinitialisations et incrémentations sont cachées dans les boucles for. Si on regarde, on verra que je fais exactement les mêmes opérations.


Pas de la même façon. Avec celle-ci, je ne suis pas sûr que le compilo puisse aussi bien exploiter le pipeline.
Le 30 septembre 2010 à 13:45:05

Citation : lmghs

Citation : Alienore

Les réinitialisations et incrémentations sont cachées dans les boucles for. Si on regarde, on verra que je fais exactement les mêmes opérations.


Pas de la même façon. Avec celle-ci, je ne suis pas sûr que le compilo puisse aussi bien exploiter le pipeline.


C'est à dire ? Qu'est-ce que tu entends par pipeline ? Ce n'est pas lorsque l'on met bout à bout plusieurs programmes ? En quoi est-ce que ca gène ?

Et j'ai essayé la première version du programme à Alienore, et en fait elle à l'air plus longue (presque 1h pour seulement 5 caractères...). Je vais essayer l'autre version après.
Zéro d'honneur Le 30 septembre 2010 à 13:50:18

pipeline, déroulage de boucle, ... http://en.wikipedia.org/wiki/Loop_unwinding
(à noter que les -O2/3 (?) le réalise déjà pour nous)
Le 30 septembre 2010 à 13:53:57

Donc en gros, si j'ai bien compris, pour le programme avec plein de boucle, il est plus rapide car le processeur peut pré calculer sur plusieurs "étages" alors que pour le 2eme programme, il est obligé de faire ligne par ligne et donc ne mobilise qu'une seule ligne ?

C'est ça ? Si oui, qu'est-ce qui détermine si tel programme facilite le pipeline et quel autre non ?
Zéro d'honneur Le 30 septembre 2010 à 14:08:03

Cela est possible, mais il faudra vérifier.
De toutes façons, comme signalé, c'est l'écriture fichier qui bouffera tout le temps.

En général, plus la boucle sera simple, plus il sera facile pour le compilo de la dérouler.
Le 30 septembre 2010 à 18:14:50

Ma boucle permet de faire de façon très rapide un code demandant un nombre paramétrable de caractères.

Pour le temps, j'ai juste écrit le main suivant :
#include <iostream>
#include <fstream>
#include <ctime>

int main()
{
    std::ofstream test("test.txt",std::ios::out | std::ios::trunc);
    time_t temps_debut = time(NULL);
    inscrire_liste_chaines(test);
    std::cout << time(NULL) - temps_debut << std::endl;
    test.close();
    return 0;
}
Zéro d'honneur Le 30 septembre 2010 à 18:21:30

C'est vrai. Pour faire la même chose avec les imbrications, il faut sortir de la meta-prog (simple).
Le 1 octobre 2010 à 8:44:38

C'est vrai que ton code est sans contexte beaucoup plus paramétrable qu'avec les boucles imbriquées. C'est ce qui en fait sa force.

Pour ce qui est de la rapidité, il faudra que je compare les 2 systèmes.

PS : et j'ai fait tourner le programme avec les boucles sous un windows XP du lycée, il a écrit les 2.3 Go en environ 2heures sur le serveur. (je l'ai lancé avant les 2 heures de cours de français et j'ai mis le programme dans le systray ^^ Lorsque je suis revenu, il en était à la fin des x). Le fichier fait bien 2.3 Go comme tu l'avais dit ^^ : je n'avais pas compté le caractère entré -> seulement x6 au lieu de x7...

Citation : lmghs

C'est vrai. Pour faire la même chose avec les imbrications, il faut sortir de la meta-prog (simple).


Qu'est-ce que tu entends par "sortir de la meta-prog" ? C'est faire un programme beaucoup plus compliqué ?
Le 1 octobre 2010 à 12:47:16

Zéro d'honneur Le 1 octobre 2010 à 15:09:34

template <size_t N, size_t M> struct Loop {
    static void do_it(char * p, ostream &os) {
        for (*p = 'a' ; *p <= 'z' ; ++*p) {
            Loop<N-1, M+1>::do_it(p+1, os);
        }
    }
};

template <size_t M> struct Loop<0,M> {
    static void do_it(char * p, ostream &os) {
        os.write(p-M, M); // on doit pouvoir jouer avec des foncteurs aussi pour éviter ce 'M'
    }
};

....
std::ofstream file("toto.txt");
char t[7]; t[6] = '\n';
Loop<6,1 /* 1 pour le 7e caractère: le saut de ligne*/>::do_it(t, file);

(Non testé ; il y a probablement plus propre).
Le 2 octobre 2010 à 14:39:34

Ok ! En gros la métaprogrammation c'est faire un programme qui génère lui même un autre programme.

Ouaaaa ! o_Oo_Oo_O:waw::waw::waw:
Dis, j'ai essayé le code d'Alienore sur un autre ordinateur : ubuntu, 3 coeurs, et il m'a couché les 2Go en 42 secondes !!! J'y crois pas ! Et lorsque je fais un cat sur ce fichier, l'affichage à lui tout seul met bien plus longtemps que 42 secondes !!!

Incroyable ! Je vais essayer de tester les autres.

Par contre lmghs, je ne comprends pas ton programme... Qu'est-ce que loop ? J'ai cherché un peu sur internet et je ne trouve rien dessus... C'est le nom d'une structure ? A quoi correspondent les < > sur Loop<N-1, M+1> ? Et à quoi sert une Template ? D'après ce que j'ai peu voir sur le net, ça sert à pouvoir utiliser plusieurs type de variable sans avoir besoin de recréer une nouvelle fonction pour ça ? Et tu parles de foncteurs... Késako ?

Ecrire lettre avec code ascii

× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
  • Editeur
  • Markdown