Sommaire
>
CString et Conversions Comment convertir un BSTR en chaîne ordinaire et vice-versa ? Comment convertir une CString en int, double, long ? Comment convertir une CString vers une std::string et vice et versa ? Comment convertir des chaînes hexa en integer, inversement et plus largement comment changer la base d'une valeur dans une string ? Quel est le nombre de caractères maximum que peut contenir une CString ? Comment faire une extraction dans une CString avec des séparateurs ? Comment créer une chaîne de type BSTR ? Comment concaténer 2 BSTR ? Comment libérer une chaîne de type BSTR ? Comment convertir un entier, un double, un float, etc, en chaîne de caractères ? Comment convertir des chaînes de caractères en ANSI et en UNICODE avec les MFC ? Comment convertir une CString ? Comment récupérer directement le contenu d'un contrôle dans un entier,double,long, etc.? Comment affecter directement le contenu d'une variable de type int,long,double ... à un contrôle ? Comment encoder une chaîne au format UTF8 ? Comment décoder une chaîne au format UTF8 ? Comment convertir une chaîne UNICODE en Char * ?
| auteur : nico-pyright(c) |
Si vous développez des ActiveX ou objets COM avec Visual C++, vous allez devoir utiliser les BSTR lors de manipulation de chaînes de caractères.
Le type BSTR (Basic STRing) est un pointeur de 32 bits (comme tous les pointeurs) qui pointe vers un tableau de caractères UNICODE.
En représentation UNICODE, 1 caractère est codé sur 2 octets, alors qu'en ASCII, 1 caractère est codé sur 1 octet.
Ce type est utilisé par Automation pour la manipulation de chaînes de caractères. Il est défini dans les spécifications d'OLE 2.0 comme
typedef OLECHAR FAR* BSTR;
|
Avec un type comme celui-ci, on se demande souvent comment s'en servir dans son code C++
Voici deux fonctions utilisant l'api windows pour le transtypage en char*:
void BSTRtoASC (BSTR str, char * & strRet)
{
if ( str ! = NULL )
{
unsigned long length = WideCharToMultiByte (CP_ACP, 0 , str, SysStringLen (str),
NULL , 0 , NULL , NULL );
strRet = new char [length];
length = WideCharToMultiByte (CP_ACP, 0 , str, SysStringLen (str),
reinterpret_cast < char * > (strRet), length, NULL , NULL );
strRet[length] = ' \0 ' ;
}
}
void ASCtoBSTR (char * str, BSTR * strRet)
{
if ( str ! = NULL )
{
unsigned long length = strlen (str);
int ResultLength = MultiByteToWideChar (CP_ACP,MB_PRECOMPOSED,
reinterpret_cast < char * > (str),length,NULL ,0 );
* strRet = SysAllocStringLen ( NULL , ResultLength);
MultiByteToWideChar (CP_ACP,MB_PRECOMPOSED,
reinterpret_cast < char * > (str),length,* strRet,ResultLength);
}
}
|
Si on utilise des CString MFC, voici une manière très simple de créer un BSTR:
CString strResult (" un test de chaîne " );
return strResult.AllocSysString ();
|
|
| auteur : Farscape | La classe CString dispose de l'opérateur (const char *), de ce fait toutes les fonctions C disposant d'une signature const char * peuvent être utilisées.
Exemple de conversions d'une CString en différents types de données :
Cet exemple est adapté d'un code trouvé sur MSDN pour la fonction strtoul.
char * stopstring;
double x;
long l;
int base;
unsigned long ul;
CString string= " 3.1415926This stopped it " ;
x = strtod ( string, & stopstring );
TRACE ( " string = %s\n " ,(const char * )string );
TRACE (" strtod = %f\n " , x );
TRACE (" Stopped scan at: %s\n\n " , stopstring );
string = " -10110134932This stopped it " ;
l = strtol ( string, & stopstring, 10 );
TRACE ( " string = %s " ,(const char * )string );
TRACE (" strtol = %ld " , l );
TRACE (" Stopped scan at: %s " , stopstring );
string = " 10110134932 " ;
TRACE ( " string = %s\n " ,(const char * )string );
for ( base = 2 ; base <= 8 ; base * = 2 )
{
ul = strtoul ( string, & stopstring, base );
TRACE ( " strtol = %ld (base %d)\n " , ul, base );
TRACE ( " Stopped scan at: %s\n " , stopstring );
}
|
résultats:
string = 3 .1415926This stopped it
strtod = 3 .141593
Stopped scan at: This stopped it
string = - 10110134932This stopped it strtol = - 2147483647
Stopped scan at: This stopped itstring = 10110134932
strtol = 45 (base 2 )
Stopped scan at: 34932
strtol = 4423 (base 4 )
Stopped scan at: 4932
strtol = 2134108 (base 8 )
Stopped scan at: 932
|
Autre possibilité utiliser la STL et la gestion des flux avec istringstream
|
# include <sstream>
template < typename T>
bool ConvertCString ( const CString & Str, T & Dest )
{
std:: istringstream iss ( static_cast < const char * > ( Str) );
return iss > > Dest ! = 0 ;
}
CString str= " 25 " ;
int nInt;
ConvertCString ( str, nInt );
ConvertCString ( " 25 " , nInt );
|
Cette dernière version est adaptée d'un post de la Faq C++
|
| auteur : Farscape | Convertion d'une CString vers une std::string:
CString str (" Bonjour " );
std:: string s ((LPCTSTR)str);
|
Convertion d'une std::string vers une CString:
std:: string s (" Bonjour " );
CString str (s.c_str ());
|
|
| auteur : matazz |
CString COutils:: ChangeBase (CString Value, int BaseFrom, int BaseTo)
{
CString ValBaseChanged;
if (! ( BaseFrom < 2 | | BaseFrom > 36 | | BaseTo < 2 | | BaseTo > 36 ) )
{
char * StopScan= NULL ;
char StrBaseOut[250 ];
intValIn= 0 ;
ValIn = strtol (Value.GetBuffer (0 ), & StopScan, BaseFrom);
_itoa (ValIn, StrBaseOut, BaseTo);
ValBaseChanged = CString (StrBaseOut, strlen (StrBaseOut));
}
else
{ ValBaseChanged = " Invalid Base Number " ;}
return ValBaseChanged;
}
|
char * StopScan= NULL ;
intValIn= 0 ;
ValIn = strtol (Value.GetBuffer (0 ), & StopScan, BaseFrom);
|
remarque transformer un int en string hexa peut être fait grâce à la fonction format de CString :
CString StrHexa;
int Val = 125 ;
StrHexa.Format (" %X " ,Val);
|
|
lien : Comment convertir une CString en int, double, long ?
|
| auteur : Farscape | On trouve la réponse dans le code de la classe CString avec la fonction AllocBuffer qui est utilisée pour allouer la mémoire nécessaire :
void CString:: AllocBuffer (int nLen)
{
ASSERT (nLen >= 0 );
ASSERT (nLen <= INT_MAX- 1 );
if (nLen = = 0 )
Init ();
else
{
CStringData* pData;
# ifndef _DEBUG
if (nLen <= 64 )
{
pData = (CStringData* )_afxAlloc64.Alloc ();
pData- > nAllocLength = 64 ;
}
else if (nLen <= 128 )
{
pData = (CStringData* )_afxAlloc128.Alloc ();
pData- > nAllocLength = 128 ;
}
else if (nLen <= 256 )
{
pData = (CStringData* )_afxAlloc256.Alloc ();
pData- > nAllocLength = 256 ;
}
else if (nLen <= 512 )
{
pData = (CStringData* )_afxAlloc512.Alloc ();
pData- > nAllocLength = 512 ;
}
else
# endif
{
pData = (CStringData* )
new BYTE[sizeof (CStringData) + (nLen+ 1 )* sizeof (TCHAR)];
pData- > nAllocLength = nLen;
}
pData- > nRefs = 1 ;
pData- > data ()[nLen] = ' \0 ' ;
pData- > nDataLength = nLen;
m_pchData = pData- > data ();
}
}
|
La longueur maximale est donc égale à INT_MAX -1 avec INT_MAX = 2147483647
Soit 2 G° .
Au passage on remarquera le traitement différent quand le bloc de mémoire à allouer est inférieur à 512 octets.
Noter aussi le traitement différent dans le cas du mode release et debug qui peut expliquer que certains bugs de mémoire n'apparaissent pas en release .
|
| auteur : Farscape | classiquement cette opération pourrait être réalisée avec la fonction C strtok.
Mais celle-ci à l'inconvénient de perturber la chaîne d'origine et de ne pas fonctionner dans certains cas :
CString str= " aaa,bbb,ccc,,123 " ;
CString strTemp= str;
char * pWord = strtok (strTemp.GetBuffer (0 ), " , " );
while ( pWord ! = NULL )
{
afxDump < < " \nWord: " < < " \" " < < pWord < < " \" " ;
pWord = strtok ( NULL , " , " );
}
strTemp.ReleaseBuffer ();
|
donnera successivement:
Word:"aaa"
Word:"bbb"
Word:"ccc"
Word:"123"
et saute la valeur vide ...
code proposé pour fonctionner avec des CString:
CString CStringTok (const char * szToken,const char * szDelimit,bool & rbEndParse)
{
CString strOrg,str;
static const char * pszOrg= NULL ;
ASSERT (szDelimit! = NULL );
if (szToken)
{
pszOrg= szToken;
rbEndParse= false ;
}
if (! pszOrg)
{
rbEndParse= true ;
return " " ;
}
strOrg= pszOrg;
int nPos= strOrg.Find (szDelimit);
if (nPos! = - 1 )
{
str= strOrg.Left (nPos);
pszOrg+ = (nPos+ strlen (szDelimit));
}
else
{
str= strOrg;
pszOrg= NULL ;
}
return str;
}
CString strOrg= " aaa,bbb,,ccc " ;
bool bEndParse;
CString strWord = CStringTok (strOrg," , " ,bEndParse);
while (! bEndParse)
{
afxDump < < " \nWord: " < < " \" " < < strWord < < " \" " ;
strWord = CStringTok (NULL ," , " ,bEndParse);
}
|
donnera successivement:
Word:"aaa"
Word:"bbb"
Word:""
Word:"ccc"
|
| auteur : nico-pyright(c) | BSTR chaine = SysAllocString (L" Exemple de chaine " );
|
On pourra utiliser SysStringLen pour faire une copie par exemple
BSTR chaine2 = SysAllocStringLen (chaine, SysStringLen (chaine));
|
char t1[] = " Exemple " ;
BSTR test1;
ASCtoBSTR (t1, & test1);
|
|
| auteur : nico-pyright(c) | BSTR chaine1 = SysAllocString (L" Exemple " );
BSTR chaine2 = SysAllocString (L" de chaine " );
BSTR chaine3;
VarBstrCat (chaine1, chaine2, & chaine3);
|
|
| auteur : nico-pyright(c) | Il ne faudra pas oublier de libérer la mémoire avec SysFreeString
SysFreeString (chaineBstr);
|
|
| auteur : Gabrielly |
{
CStringW wstr = L" Chaine UNICODE " ;
CStringA str (wstr);
LPCSTR lpcstr = str;
AfxMessageBox (lpcstr);
}
{
CComBSTR bstr = L" Chaine UNICODE " ;
CStringA str (bstr);
LPCSTR lpcstr = str;
AfxMessageBox (lpcstr);
}
{
CStringA str = " Chiane ANSI " ;
CStringW wstr (str);
LPCWSTR lpcwstr = wstr;
}
{
CStringA str = " Chaine ANSI " ;
CComBSTR bstr (str);
BSTR b = bstr;
}
{
GUID guid;
:: CoCreateGuid (& guid);
CComBSTR bstrGuid (guid);
CStringA strGuid (bstrGuid);
AfxMessageBox (strGuid);
AfxMessageBox (CStringA (CComBSTR (guid)));
}
|
Les constructeurs des classes CString et CComBSTR réalisent aisément des conversions pour nous.
Encore mieux CComBSTR convertit un GUID (Global Unique Identifier ou UUID= Universal Unique ID) en Unicode BSTR.
|
| auteur : Farscape |
# include <string>
# include <iostream>
# include <sstream>
template < typename T,typename S>
bool FromString ( const S & Str, T & Dest )
{
# ifdef _UNICODE
std:: wistringstream iss ( Str );
# else
std:: istringstream iss ( Str );
# endif
return iss > > Dest ! = 0 ;
}
|
Utilisation:
int nInt ;
FromString (_T (" 10 " ), nInt );
double dDouble;
FromString ( _T (" 3.14107 " ), dDouble );
CString str= _T (" 1200 " );
# ifdef _UNICODE
std:: wstring strstl;
# else
std:: string strstl;
# endif
strstl= _T (" 1200 " );
int n= 0 ;
FromString (str.GetString (),n);
n= 0 ;
FromString (strstl,n);
|
|
| auteur : Farscape | les MFC comme l'api 32 permettent avec GetDlgItemInt la récupération du contenu d'un contrôle sous forme d'entier.
mais quid des autres types long,float,double ?
pour ces autres types il faudra attacher au contrôle une variable correspondante au type de donnée souhaité et appeler la méthode UpdateData(TRUE) pour disposer de la valeur.
le code qui suit permet de se passer de l'association d'une variable à un contrôle et donc de récupérer directement la valeur pour le type souhaité.
# include <sstream>
# include <string>
# include <iostream>
template < typename T>
bool FromCtrl ( const CWnd & Ctrl, T & Dest )
{
CString Str;
Ctrl.GetWindowText (Str);
# ifdef _UNICODE
std:: wistringstream iss ( static_cast < LPCTSTR> ( Str) );
# else
std:: istringstream iss ( static_cast < LPCTSTR> ( Str) );
# endif
return iss > > Dest ! = 0 ;
}
|
Utilisation:
double d;
FromCtrl (* GetDlgItem (IDC_EDITNOM),d);
if (d= = 10 .0 )
{
}
|
|
| auteur : Farscape | Les MFC comme l'api 32 permettent avec SetDlgItemInt l'affection d'un entier au contrôle.
mais quid des autres types long,float,double ?
pour ces autres types il faudra attacher au contrôle une variable correspondante au type de donnée traité et appeler la méthode UpdateData(FALSE) pour mettre à jour le contrôle.
le code qui suit permet de se passer de l'association d'une variable à un contrôle et donc d'affecter directement une valeur d'un type donné au contrôle.
# include <sstream>
# include <string>
# include <iomanip>
# include <iostream>
class FormatNum
{
public :
FormatNum (){ }
template < typename T>
FormatNum (const T& t)
{
operator < < (t);
}
template < typename T>
FormatNum & operator < < (const T& t)
{
m_ss < < t;
return * this ;
}
public :
# ifdef _UNICODE
std:: wstringstream m_ss;
# else
std:: stringstream m_ss;
# endif
} ;
template < typename T>
void ToCtrl (CWnd & Ctrl,const T & Src,FormatNum & rFormat= FormatNum ())
{
rFormat < < Src;
# ifdef _UNICODE
std:: wstring s= rFormat.m_ss.str ();
# else
std:: string s= rFormat.m_ss.str ();
# endif
Ctrl.SetWindowText (s.c_str ());
}
|
Utilisation:
ToCtrl (* GetDlgItem (IDC_EDITNUM),10 .345 );
ToCtrl (* GetDlgItem (IDC_EDITNUM),10 .345 ,
FormatNum ()< < std:: setprecision (4 ));
|
Le traitement se décompose en deux parties :
La fonction modèle ToCtrl permettant la transformation du type utilisateur et l'affectation de la chaîne au contrôle passée en argument.
Un objet fonction FormatNum optionnel qui permet le formatage du flux pour contrôler la conversion, l'enchainement des arguments, et qui fournit l'objet flux de conversion de la classe stringstream à la fonction modèle ToCtrl.
Voyons son utilisation dans les exemples qui suivent :
Dans le cas d'un double ou float si on veut maitriser la précision du nombre envoyé on pourra utiliser la fonction setprecision définie dans l'entête standard iomanip pour fixer le nombre de digits souhaités.
Vous pouvez bien-sûr utiliser les autres fonctions et compléter le flux
Exemples:
Contrôler la longueur de la chaîne créée, et spécifier un caractère de remplissage.
ToCtrl (* GetDlgItem (IDC_EDITNUM),10 .345 ,
FormatNum ()< < std:: setprecision (4 )< < std:: setfill (_TCHAR (' 0 ' ))< < std:: setw (10 ));
|
Cet exemple impose une précision de 4 digits, une chaîne de 10 caractères remplie avec des '0'.
Dans le même ordre d'idée on pourra fixer la base de conversion ...
ToCtrl (* GetDlgItem (IDC_EDITNUM),32 ,
FormatNum ()< < std:: setprecision (4 )< < std:: setfill (_TCHAR (' 0 ' ))< < std:: setw (10 )< < setbase (16 ));
|
Enfin rajouter du texte devant la conversion :
ToCtrl (* GetDlgItem (IDC_EDITNUM),
32 ,
FormatNum ()< < _T (" Conversion Hexa: 0x " ) < < std:: setprecision (4 )< < std:: setfill (_TCHAR (' 0 ' ))< < std:: setw (10 )< < setbase (16 ));
ToCtrl (* GetDlgItem (IDC_EDITNUM),
32 ,
FormatNum (_T (" Conversion Hexa: 0x " ))< < std:: setprecision (4 )< < std:: setfill (_TCHAR (' 0 ' ))< < std:: setw (10 )< < setbase (16 ));
|
Ou encore une syntaxe plus aérée:
FormatNum format;
format < < _T (" Conversion Hexa: 0x " ) < < std:: setprecision (4 )< < std:: setfill (_TCHAR (' 0 ' ))< < std:: setw (10 )< < setbase (16 );
ToCtrl (* GetDlgItem (IDC_EDITNUM),32 ,format);
|
Vous noterez aussi l'utilisation optionnelle de la spécification du format de conversion (FormatNum).
On pourra compléter notre traitement par une fonction de conversion vers une CString ou string de la STL.
template < typename T,typename S>
void ToString (S & rstr,const T & Src,FormatNum & rFormat= FormatNum ())
{
rFormat < < Src;
# ifdef _UNICODE
std:: wstring s= rFormat.m_ss.str ();
# else
std:: string s= rFormat.m_ss.str ();
# endif
rstr= s.c_str ();
}
|
Utilisation :
CString str;
ToString (str,1200 );
# ifdef _UNICODE
std:: wstring strstl;
# else
std:: string strstl;
# endif
ToString (strstl,1200 );
|
|
| auteur : Farscape | UTF-8 (UCS transformation format 8 bits) permet de coder de l'Unicode sous une suite de 4 octets maximum.
Cette codification est par exemple utilisée dans le fichier document.xml au format OpenXml de Word 2007.
Les caractères dont le code ASCII est supérieur à 127 sont codés sur plusieurs octets.
Le code ci-dessus s'appuie sur la description donnée dans Wikipédia
Notes:
-Une chaîne au format UTF8 peut être contenue dans une CString multi bytes .
-j'ai implémenté la codification sur 4 octets mais l'exemple de Wikipédia avec le "?" ne semble pas correct dans ce cas puisque son code ASCII est égal à 63.
-ce code ne fonctionne pas directement avec Visual 6.0 à cause des CString: On ne dispose pas de CStringA et de CStringW...
CStringA UTF8EncodeString (const CStringA str)
{
CStringW input (str);
CStringA output;
for (int i= 0 ; i < input.GetLength (); i+ + )
{
if (input[i] < 0x80 )
{
output+ = input[i];
}
else if ((input[i] > 0x7F ) & & (input[i] < 0x800 ))
{
output+ = static_cast < unsigned char > (0xC0 | (input[i] > > 6 ));
output+ = static_cast < unsigned char > (0x80 | (input[i] & 0x3F ));
}
else if (input[i] < 0x8000 )
{
output+ = static_cast < unsigned char > (0xE0 | (input[i] > > 12 ));
output+ = static_cast < unsigned char > (0x80 | (input[i] > > 6 & 0x3F ));
output+ = static_cast < unsigned char > (0x80 | (input[i] & 0x3F ));
}
else
{
output+ = static_cast < unsigned char > (0xF0 | (input[i] > > 12 )> > 6 );
output+ = static_cast < unsigned char > (0x80 | (input[i] > > 12 & 0x3F ));
output+ = static_cast < unsigned char > (0x80 | (input[i] > > 6 & 0x1F ));
output+ = static_cast < unsigned char > (0x80 | (input[i] & 0x3F ));
}
}
return output;
}
CStringA input,output;
input= " é " ;
output= UTF8EncodeString (input);
TRACE (" \n output é:%s " ,static_cast < const char * > (output));
input= " ? " ;
output= UTF8EncodeString (input);
TRACE (" \n output ?:%s " ,static_cast < const char * > (output));
|
|
| auteur : Farscape |
CStringA UTF8DecodeString (const CStringA input)
{
CStringW output;
for (int i= 0 ; i < input.GetLength ();i+ + )
{
if ((input[i] & 0xFE ) = = 0xFE )
{
output+ = static_cast < wchar_t > (((input[i] & 7 )< < 18 ) | ((input[i+ 1 ] & 0x3F ) < < 12 ) | ((input[i+ 2 ] & 0x3F ) < < 6 ) | (input[i+ 3 ] & 0x3F ));
i + = 3 ;
}
else
if ((input[i] & 0xE0 ) = = 0xE0 )
{
output+ = static_cast < wchar_t > (((input[i] & 0x0F ) < < 12 ) | ((input[i+ 1 ] & 0x3F ) < < 6 ) | (input[i+ 2 ] & 0x3F ));
i + = 2 ;
}
else if ((input[i] & 0xC0 ) = = 0xC0 )
{
output+ = static_cast < wchar_t > (((input[i] & 0x1F ) < < 6 ) | (input[i+ 1 ] & 0x3F ));
i + + ;
}
else if (input[i] < 0x80 ) output+ = input[i];
}
return CStringA (output);
}
CStringA input,output;
input= " é " ;
output= UTF8EncodeString (input);
TRACE (" \n output é:%s " ,static_cast < const char * > (output));
output= UTF8DecodeString (output);
TRACE (" \n output é:%s " ,static_cast < const char * > (output));
input= " ? " ;
output= UTF8EncodeString (input);
TRACE (" \n output ?:%s " ,static_cast < const char * > (output));
output= UTF8DecodeString (output);
TRACE (" \n output é:%s " ,static_cast < const char * > (output));
|
|
lien : Comment encoder une chaîne au format UTF8 ?
|
| auteur : Farscape | La conversion d'une chaîne UNICODE en chaîne de caractères est relativement aisée avec les MFC récentes (après visual 6.0).
En effet la classe CString permet de travailler sur les deux conventions UNICODE et Multi-Byte dans un même projet.
On aura alors :
La classe CString qui suivra le paramétrage du projet : UNICODE ou Multi-Byte
La classe CStringA pour travailler avec des chaînes de caractères Multi-Byte (char *) .
La classe CStringW pour travailler avec des chaînes Unicode.
Une conversion d'une chaîne UNICODE en Multi-Byte devient une chose aisée :
CString strWide= _T (" chaine de caractères " );
CStringA strA (strWide);
CString strWideDup (strA);
VERIFY (strWideDup= = strWide);
char * sz= strA.GetBuffer (0 );
|
L'exemple ci-dessus part d'une CString UNICODE et fournit une CString Multi-Byte et un pointeur sur char *.
et enfin fabrique une CString UNICODE à partir d'une CString Multi-Byte
Avec Visual 6.0:
CString travaille dans le mode réglé dans le projet.
Pour transformer une CString Unicode en chaîne de caractères il faudra utiliser l'api WideCharToMultiByte :
CString strWide= _T (" chaine de caractères " );
char * sz= new char [strWide.GetLength ()+ 1 ] ;
WideCharToMultiByte ( CP_ACP, 0 , strWide, - 1 , sz, strWide.GetLength ()+ 1 , NULL , NULL );
delete [] sz;
|
Pour transformer une chaîne de caractères en chaîne Unicode on utilisera MultiByteToWideChar
CString strWide= _T (" chaine de caractères " );
char * sz= new char [strWide.GetLength ()+ 1 ] ;
WideCharToMultiByte ( CP_ACP, 0 , strWide, - 1 , sz, strWide.GetLength ()+ 1 , NULL , NULL );
int nSize= strlen (sz)+ 1 ;
TCHAR * wsz= new TCHAR[nSize];
MultiByteToWideChar ( CP_ACP, 0 , sz,nSize,wsz,nSize);
CString strWideDup= wsz;
VERIFY (strWideDup= = strWide);
delete [] wsz;
delete [] sz;
|
|
Consultez les autres F.A.Q.
|
|