OVH Cloud OVH Cloud

Convertir un nombre en lettres

14 réponses
Avatar
Michael
Bonjour à tous,

je voudrais convertir un index de colonne depuis un int vers une chaine.

Toujours pour ma classe Excel, je voudrais obtenir 'A' depuis 1, 'AB'
depuis 28 par exemple.

J'ai trouvé la fonction suivante sur le net:

AnsiString XLCells::Get_Indice_Colonne(int colonne)
{
int diviseur;
int reste;
AnsiString indice;
char carac;

do
{
diviseur = colonne / 26;
reste = colonne % 26;

// Conversion en caractère
carac = reste + 64; // Code Ascii du A = 65

indice += carac;

colonne = diviseur;
}
while (diviseur != 0);

//Les lettres de la colonne sont inversés, donc on remet dans l'ordre
AnsiString chaine;
for(int i = indice.Length(); i > 0; i--)
chaine += indice.SubString(i, 1);

return chaine;
}

Problème: dès qu'il y a un 'Z' dans le retour ça ne marche plus, j'ai
'@'.

Comment je peux résoudre ça?

Merci d'avance

10 réponses

1 2
Avatar
Jean-Marc Bourguet
Michael writes:

Bonjour à tous,

je voudrais convertir un index de colonne depuis un int vers une chaine.

Toujours pour ma classe Excel, je voudrais obtenir 'A' depuis 1, 'AB'
depuis 28 par exemple.


Ok, donc ce que tu veux c'est une representation en base 26 de
colonne-1 (puisque tu commences a 1). Ta fonction est presque bonne.

J'ai trouvé la fonction suivante sur le net:

AnsiString XLCells::Get_Indice_Colonne(int colonne)
{
int diviseur;
int reste;
AnsiString indice;
char carac;


--colonne;

do
{
diviseur = colonne / 26;
reste = colonne % 26;

// Conversion en caractère
carac = reste + 64; // Code Ascii du A = 65


carac = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[reste];

fera ce que tu veux avec n'importe quel encodage. Si tu insistes pour
l'ASCII,

carac = 'A' + reste;

mais je te le deconseille.


indice += carac;

colonne = diviseur;
}
while (diviseur != 0);


Quand je fais des conversions, j'ai tendance a etablir une borne
superieure du nombre de caracteres (sizeof(int)*CHAR_BIT + 1 est
valable pour toutes les bases), a allouer un tableau de cette taille
et a le remplir en sens inverse. Ca evite de devoir faire:

//Les lettres de la colonne sont inversés, donc on remet dans l'ordre
AnsiString chaine;
for(int i = indice.Length(); i > 0; i--)
chaine += indice.SubString(i, 1);

return chaine;
}


A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Vincent Lascaux
AnsiString XLCells::Get_Indice_Colonne(int colonne)
{
int diviseur;
int reste;
AnsiString indice;
char carac;


On devrait utiliser les variables dans le scope le plus restraint possible.
reste et carac devraient être définies dans la boucle


do
{
diviseur = colonne / 26;
reste = colonne % 26;

// Conversion en caractère
carac = reste + 64; // Code Ascii du A = 65


reste est entre 0 et 25. Si reste vaut 0, tu vas avoir '@'. Il faut faire
carac = reste + 65, ou, ce qui elimine le commentaire, carac = reste + 'A';
(en fait là je suis pas sur, est ce qu'on sait que la valeur de 'A' c'est
celle utilisée dans la table ASCII ?)

indice += carac;

colonne = diviseur;
}
while (diviseur != 0);

//Les lettres de la colonne sont inversés, donc on remet dans l'ordre
AnsiString chaine;
for(int i = indice.Length(); i > 0; i--)
chaine += indice.SubString(i, 1);

return chaine;
}

Problème: dès qu'il y a un 'Z' dans le retour ça ne marche plus, j'ai
'@'.

Comment je peux résoudre ça?


Je trouve le code assez mal écrit. Il utilise beaucoup de variables pour
rien (ca peut être au gout de certains, moi ca me dérange un peu). En fait
c'est un changement de base de colonne-1 avec A=0, B=1, ..., Z&. Le code
suivant n'est pas testé, mais je pense qu'il devrait fonctionner (attention
au cas particulier de colonne=1, qui vaudrait "" dans la nouvelle base alors
qu'on veut "A").

std::string getIndiceColonne(int colonne)
{
colonne--; //parceque 1 c'est A. 0 n'a pas de sens
std::string accu;
while(colonne > 0)
{
accu += (char)( colonne%26 + 'A' );
colonne /= 26;
}
if(accu.empty())
return "A";

//Les lettres de la colonne sont inversés, donc on remet dans l'ordre
std::reverse(accu.begin(), accu.end());
return accu;
}

--
Vincent

Avatar
Jean-Marc Bourguet
"Vincent Lascaux" writes:

(en fait là je suis pas sur, est ce qu'on sait que la valeur de 'A' c'est
celle utilisée dans la table ASCII ?)


Non. On n'est pas sur non plus que les lettres soient consecutives.

std::string getColumnDisplayRepresentation(int column)
{
assert(column > 0);

static char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int const numDigits = sizeof(digits) - 1;
static int const maxResultSize = sizeof(column)*CHAR_BIT+1

char result[maxResultSize];
int index = maxResultSize;

--column;
result[--index] = '';
do {
result[--index] = digits[column % numDigits];
colonne /= numDigits;
} while (column != 0);

return result+index;
}

ainsi le jour ou on decide que O et I risquent d'etre confondus avec 0
et 1 et doivent donc etre evites, on n'a qu'une chose a faire.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Serge Paccalin
Le mercredi 22 juin 2005 à 13:20, Michael a écrit dans
fr.comp.lang.c++ :

Bonjour à tous,

je voudrais convertir un index de colonne depuis un int vers une chaine.

Toujours pour ma classe Excel, je voudrais obtenir 'A' depuis 1, 'AB'
depuis 28 par exemple.

J'ai trouvé la fonction suivante sur le net:

AnsiString XLCells::Get_Indice_Colonne(int colonne)
{
int diviseur;
int reste;
AnsiString indice;
char carac;

do
{
diviseur = colonne / 26;
reste = colonne % 26;


reste est compris entre 0 et 25 (0 pour les multiples non nuls de 26,
bien sûr, puisque colonne démarre à 1, j'imagine).


// Conversion en caractère
carac = reste + 64; // Code Ascii du A = 65


carac est compris entre 64 et 89, soit '@' à 'Y' en ASCII.

indice += carac;

colonne = diviseur;
}
while (diviseur != 0);

//Les lettres de la colonne sont inversés, donc on remet dans l'ordre
AnsiString chaine;
for(int i = indice.Length(); i > 0; i--)
chaine += indice.SubString(i, 1);

return chaine;
}

Problème: dès qu'il y a un 'Z' dans le retour ça ne marche plus, j'ai
'@'.

Comment je peux résoudre ça?


À ta place, je travaillerais sur `colonne - 1' pour démarrer à 0, puis
j'écrirais : carac = reste + 'A'; // plus parlant que + 65

--
___________ 22/06/2005 14:06:56
_/ _ _`_`_`_) Serge PACCALIN -- sp ad mailclub.net
_L_) Il faut donc que les hommes commencent
-'(__) par n'être pas fanatiques pour mériter
_/___(_) la tolérance. -- Voltaire, 1763

Avatar
Marc Duflot
Jean-Marc Bourguet wrote:

std::string getColumnDisplayRepresentation(int column)
{
assert(column > 0);

static char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int const numDigits = sizeof(digits) - 1;


Je pense qu'il ne faut pas de -1 ci-dessus.

static int const maxResultSize = sizeof(column)*CHAR_BIT+1

char result[maxResultSize];
int index = maxResultSize;

--column;
result[--index] = '';
do {
result[--index] = digits[column % numDigits];
colonne /= numDigits;
} while (column != 0);

return result+index;
}


Avatar
Jean-Marc Bourguet
Marc Duflot writes:

Jean-Marc Bourguet wrote:
std::string getColumnDisplayRepresentation(int column)
{
assert(column > 0);
static char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";



Oops, il manque un const

static int const numDigits = sizeof(digits) - 1;


Je pense qu'il ne faut pas de -1 ci-dessus.


Si; il y a le final.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Michael
Merci à tous pour vos réponses :)
Avatar
kanze
Jean-Marc Bourguet wrote:

[...]
carac = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[reste];


Je suis content de voir que je ne suis pas le seul qui utilise
cette forme d'écriture.

En fait, j'aurais écrit tout simplement :

<dest> = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[ colonne % 26 ] ;
colonne /= 26 ;

(où <dest> dépendrait de comment je collectionnais les
caractères -- typiquement, soit *--p soit *iter ++.)

indice += carac;

colonne = diviseur;
}
while (diviseur != 0);


Quand je fais des conversions, j'ai tendance a etablir une
borne superieure du nombre de caracteres (sizeof(int)*CHAR_BIT
+ 1 est valable pour toutes les bases), a allouer un tableau
de cette taille et a le remplir en sens inverse.


C'est exactement ce que je faisais dans le temps (y compris le
moyen d'établir la limite supérieur de la taille). Et encore
aujourd'hui, souvent. Les alternatifs, c'est d'utiliser
std::deque<char> et push_front, ou std::string, push_back, et
std::reverse à la fin.

Pour un débuttant, ces dernières solutions ont l'avantage de se
réprocher plus à ce qu'il doit faire en général.

Enfin, dans le cas spécifique d'un tableur, on pourrait vouloir
limiter artificiellement le nombre de colonnes à 676, de façon à
ne jamais dépasser deux chiffres. Dans ce cas-là, on pourrait
aussi considérer des structures spécifiques pour les contenir
(et générer toujours deux caractères, en forçant le deuxième à
' ' s'il n'est pas générer autrement.)

Ca evite de devoir faire:

//Les lettres de la colonne sont inversés, donc on remet dans l'ordre
AnsiString chaine;
for(int i = indice.Length(); i > 0; i--)
chaine += indice.SubString(i, 1);

return chaine;
}



Aujourd'hui, je pars du principe qu'on utiliserait std::string à
la place de AnsiString, et que la boucle ci-dessus s'écrira :
std::reverse( chaine.begin(), chaine.end() ) ;

Sinon, comme j'ai dit ci-dessus, il y a std::deque<char>, avec
des push_front, et à la fin :

return std::string( chaine.begin(), chaine.end() ) ;

(On pourrait aussi envisager std::vector<char>, push_back, et à
la fin :

return std::string( chaine.rbegin(), chaine.rend() ) ;

Comme on dit chez nous : « There's more than one way to skin a
cat .» Bien que je n'ai jamais compris ce qu'on a contre les
chats.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Jean-Marc Bourguet
writes:

Jean-Marc Bourguet wrote:

[...]
carac = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[reste];


Je suis content de voir que je ne suis pas le seul qui utilise
cette forme d'écriture.

En fait, j'aurais écrit tout simplement :

<dest> = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[ colonne % 26 ] ;
colonne /= 26 ;


Je donnais les corrections minimales. Voir mon autre message pour
quelque chose plus proche de ce que j'aurais ecris de scratch.

Aujourd'hui, je pars du principe qu'on utiliserait std::string à
la place de AnsiString,


C'est quelque chose de tres lie a l'interface et je supposais que la
bibliotheque utilisee exigeait un AnsiString. Si ce n'est pas le cas
naturellement.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
kanze
Jean-Marc Bourguet wrote:
"Vincent Lascaux" writes:

(en fait là je suis pas sur, est ce qu'on sait que la valeur
de 'A' c'est celle utilisée dans la table ASCII ?)


Non. On n'est pas sur non plus que les lettres soient consecutives.

std::string getColumnDisplayRepresentation(int column)
{
assert(column > 0);

static char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int const numDigits = sizeof(digits) - 1;
static int const maxResultSize = sizeof(column)*CHAR_BIT+1

char result[maxResultSize];
int index = maxResultSize;

--column;
result[--index] = '';
do {
result[--index] = digits[column % numDigits];
colonne /= numDigits;
} while (column != 0);

return result+index;
}

ainsi le jour ou on decide que O et I risquent d'etre
confondus avec 0 et 1 et doivent donc etre evites, on n'a
qu'une chose a faire.


Tu l'as essayé pour le colonne 27 ? D'après ces descriptions, je
conclus que son Excel est un espèce de tableur. (Il me semble,
d'ailleurs, qu'il y a déjà un tableur disponible sous Windows
avec ce nom.) Or, l'affichage des colonnes dans un tableur n'est
pas tout à fait une conversion en base 26. Du fait, justement,
qu'il n'y a pas de colonne 0 (ou qu'il n'y a pas de 0, à
proprement parler, dans l'affichage).

En fait, le cas est un peu étrange : on peut dire que c'est une
conversion en base 26, avec le chiffre 0 représenté par un 'A',
sauf que si le valeur se représent sur plus d'un chiffre, pour
le chiffre le plus à gauche (qui ne peut pas être 0), 'A'
représente un 1 (et on accepte de 1 à 26 compris, plutôt que de
0 à 25).

Je me retrouve avec quelque chose comme :

std::string
getColumnDisplayRepresentation( int column, int width = 2 )
{
static char const digits[]
= " ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;
int const base = sizeof( digits ) - 2 ;
std::string result ;
-- column ;
do {
result.push_back( digits[ column % base + 1 ] ) ;
column /= base ;
} while ( column > base ) ;
if ( column != 0 ) {
result.push_back( digits[ column ] ) ;
}
if ( result.size() < width ) {
result.resize( width, digits[ 0 ] ) ;
}
std::reverse( result.begin(), result.end() ) ;
return result ;
}

Note, en particulier, la condition à la fin de do...while, qui
laisse passe 26, même s'il n'est pas un chiffre valide dans la
base.

Si on se limite artificiellement à 702 colonnes (ce qui peut
être raisonable dans un tableur), on peut se passer de la boucle
complétement, et écrire quelque chose comme

std::string
getColumnDisplayRepresentation( int column )
{
static char const digits[]
= " ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;
int const base = sizeof( digits ) - 2 ;
std::string result ;
-- column ;
assert( column >= 0 && column < base * (base + 1) ) ;
result.push_back( digits[ column / base ] ) ;
result.push_back( digits[ column % base + 1 ] ) ;
return result ;
}

(En fait, en intern, je gèrerais le colonne en base 26, avec un
0 pour la première colonne. Ce qui rendrait le --column
superflu.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


1 2