 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Michael Guest
|
Posted: Wed Jun 22, 2005 11:20 am Post subject: Convertir un nombre en lettres |
|
|
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
|
|
| Back to top |
|
 |
Jean-Marc Bourguet Guest
|
Posted: Wed Jun 22, 2005 11:46 am Post subject: Re: Convertir un nombre en lettres |
|
|
Michael <michael_delva.enlever (AT) hotmail (DOT) com> writes:
| Quote: | 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.
| Quote: | 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;
| Quote: | 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.
| Quote: |
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:
| Quote: | //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
|
|
| Back to top |
|
 |
Vincent Lascaux Guest
|
Posted: Wed Jun 22, 2005 11:48 am Post subject: Re: Convertir un nombre en lettres |
|
|
| Quote: | 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
| Quote: |
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 ?)
| Quote: | 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=26. 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
|
|
| Back to top |
|
 |
Jean-Marc Bourguet Guest
|
Posted: Wed Jun 22, 2005 11:57 am Post subject: Re: Convertir un nombre en lettres |
|
|
"Vincent Lascaux" <nospam (AT) nospam (DOT) org> writes:
| Quote: | (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
|
|
| Back to top |
|
 |
Serge Paccalin Guest
|
Posted: Wed Jun 22, 2005 12:11 pm Post subject: Re: Convertir un nombre en lettres |
|
|
Le mercredi 22 juin 2005 à 13:20, Michael a écrit dans
fr.comp.lang.c++ :
| Quote: | 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).
| Quote: |
// Conversion en caractère
carac = reste + 64; // Code Ascii du A = 65
|
carac est compris entre 64 et 89, soit '@' à 'Y' en ASCII.
| Quote: | 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
|
|
| Back to top |
|
 |
Marc Duflot Guest
|
Posted: Wed Jun 22, 2005 1:06 pm Post subject: Re: Convertir un nombre en lettres |
|
|
Jean-Marc Bourguet wrote:
| Quote: |
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.
| Quote: | 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;
}
|
|
|
| Back to top |
|
 |
Jean-Marc Bourguet Guest
|
|
| Back to top |
|
 |
Michael Guest
|
Posted: Wed Jun 22, 2005 2:03 pm Post subject: Re: Convertir un nombre en lettres |
|
|
Merci à tous pour vos réponses
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Thu Jun 23, 2005 7:17 am Post subject: Re: Convertir un nombre en lettres |
|
|
Jean-Marc Bourguet wrote:
[...]
| Quote: | 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 ++.)
| Quote: | 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.)
| Quote: | 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
|
|
| Back to top |
|
 |
Jean-Marc Bourguet Guest
|
Posted: Thu Jun 23, 2005 7:33 am Post subject: Re: Convertir un nombre en lettres |
|
|
[email]kanze (AT) gabi-soft (DOT) fr[/email] writes:
| Quote: | 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.
| Quote: | 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
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Thu Jun 23, 2005 8:19 am Post subject: Re: Convertir un nombre en lettres |
|
|
Jean-Marc Bourguet wrote:
| Quote: | "Vincent Lascaux" <nospam (AT) nospam (DOT) org> 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
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Thu Jun 23, 2005 8:23 am Post subject: Re: Convertir un nombre en lettres |
|
|
Jean-Marc Bourguet wrote:
| Quote: | 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.
|
En passant, il est courant en Allemagne de ne pas distinguer
entre le I et le J. Au fond, l'ensemble de caractères utilisé
(et donc la base) doit dépendre du locale:-).
--
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
|
|
| Back to top |
|
 |
Jean-Marc Bourguet Guest
|
Posted: Thu Jun 23, 2005 6:14 pm Post subject: Re: Convertir un nombre en lettres |
|
|
[email]kanze (AT) gabi-soft (DOT) fr[/email] writes:
| Quote: | Tu l'as essayé pour le colonne 27 ?
|
Non. J'en suis confu. :-)
| Quote: | 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 ;
}
|
Tu l'as essayé pour les colonnes 26+26*26, 27+26*26,
26+26*26+26*26*26, 27+26*26+26*26*26 ? :-)
J'en arrive à:
std::string getColumnDisplayRepresentation(int column)
{
assert(column > 0);
static char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int const base = sizeof(digits) - 1;
static int const maxResultSize = sizeof(column)*CHAR_BIT+1;
char result[maxResultSize];
int index = maxResultSize;
result[--index] = ' ';
do {
--column;
result[--index] = digits[column % base];
column /= base;
} while (column != 0);
return result+index;
}
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
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Fri Jun 24, 2005 8:47 am Post subject: Re: Convertir un nombre en lettres |
|
|
Jean-Marc Bourguet wrote:
| Quote: | kanze (AT) gabi-soft (DOT) fr writes:
Tu l'as essayé pour le colonne 27 ?
Non. J'en suis confu. :-)
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 ;
}
Tu l'as essayé pour les colonnes 26+26*26, 27+26*26,
26+26*26+26*26*26, 27+26*26+26*26*26 ?
|
En effet. Je me suis arrêté à deux caractères dans mes tests.
| Quote: | J'en arrive à:
std::string getColumnDisplayRepresentation(int column)
{
assert(column > 0);
static char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int const base = sizeof(digits) - 1;
static int const maxResultSize = sizeof(column)*CHAR_BIT+1;
char result[maxResultSize];
int index = maxResultSize;
result[--index] = ' ';
do {
--column;
result[--index] = digits[column % base];
column /= base;
} while (column != 0);
return result+index;
}
|
Tout à fait. En fait, c'est beaucoup plus simple que je ne
croyais ; c'est réelement du base 26, mais avec chaque chiffre
incrémenté avant d'y être ajouté. Ou quelque chose comme ça.
--
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
|
|
| Back to top |
|
 |
Jean-Marc Bourguet Guest
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|