web-dev-qa-db-fra.com

Comment convertir wstring en string?

La question est de savoir comment convertir wstring en chaîne?

J'ai l'exemple suivant:

#include <string>
#include <iostream>

int main()
{
    std::wstring ws = L"Hello";
    std::string s( ws.begin(), ws.end() );

  //std::cout <<"std::string =     "<<s<<std::endl;
    std::wcout<<"std::wstring =    "<<ws<<std::endl;
    std::cout <<"std::string =     "<<s<<std::endl;
}

la sortie avec la ligne commentée est:

std::string =     Hello
std::wstring =    Hello
std::string =     Hello

mais en dehors c'est seulement:

std::wstring =    Hello

Quelque chose ne va pas dans l'exemple? Puis-je faire la conversion comme ci-dessus?

EDIT

Un nouvel exemple (en tenant compte de certaines réponses) est

#include <string>
#include <iostream>
#include <sstream>
#include <locale>

int main()
{
    setlocale(LC_CTYPE, "");

    const std::wstring ws = L"Hello";
    const std::string s( ws.begin(), ws.end() );

    std::cout<<"std::string =     "<<s<<std::endl;
    std::wcout<<"std::wstring =    "<<ws<<std::endl;

    std::stringstream ss;
    ss << ws.c_str();
    std::cout<<"std::stringstream =     "<<ss.str()<<std::endl;
}

La sortie est:

std::string =     Hello
std::wstring =    Hello
std::stringstream =     0x860283c

par conséquent, le stringstream ne peut pas être utilisé pour convertir wstring en chaîne.

185
BЈовић

Voici une solution élaborée basée sur les autres suggestions:

#include <string>
#include <iostream>
#include <clocale>
#include <locale>
#include <vector>

int main() {
  std::setlocale(LC_ALL, "");
  const std::wstring ws = L"ħëłlö";
  const std::locale locale("");
  typedef std::codecvt<wchar_t, char, std::mbstate_t> converter_type;
  const converter_type& converter = std::use_facet<converter_type>(locale);
  std::vector<char> to(ws.length() * converter.max_length());
  std::mbstate_t state;
  const wchar_t* from_next;
  char* to_next;
  const converter_type::result result = converter.out(state, ws.data(), ws.data() + ws.length(), from_next, &to[0], &to[0] + to.size(), to_next);
  if (result == converter_type::ok or result == converter_type::noconv) {
    const std::string s(&to[0], to_next);
    std::cout <<"std::string =     "<<s<<std::endl;
  }
}

Cela fonctionnera généralement sous Linux, mais créera des problèmes sous Windows.

29
Philipp

Comme Cubbi l'a souligné dans l'un des commentaires, std::wstring_convert (C++ 11) fournit une solution simple et soignée (vous devez #include<locale> et <codecvt>):

std::wstring string_to_convert;

//setup converter
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;

//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
std::string converted_str = converter.to_bytes( string_to_convert );

J'utilisais une combinaison de wcstombs et d'allocation/désallocation fastidieuse de mémoire avant de tomber sur cela.

http://en.cppreference.com/w/cpp/locale/wstring_convert

update (2013.11.28)

Une doublure peut être déclarée ainsi (merci Guss pour votre commentaire):

std::wstring str = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes("some string");

Les fonctions de wrapper peuvent être définies comme suit: (merci ArmanSchwarz pour votre commentaire)

std::wstring s2ws(const std::string& str)
{
    using convert_typeX = std::codecvt_utf8<wchar_t>;
    std::wstring_convert<convert_typeX, wchar_t> converterX;

    return converterX.from_bytes(str);
}

std::string ws2s(const std::wstring& wstr)
{
    using convert_typeX = std::codecvt_utf8<wchar_t>;
    std::wstring_convert<convert_typeX, wchar_t> converterX;

    return converterX.to_bytes(wstr);
}

Remarque: la question de savoir si string/wstring doit être transmise aux fonctions en tant que références ou en tant que littéraux (en raison de C++ 11 et des mises à jour du compilateur) est controversée. Je laisserai la décision à la personne chargée de la mise en œuvre, mais cela vaut la peine de le savoir.

Remarque: J'utilise std::codecvt_utf8 dans le code ci-dessus, mais si vous n'utilisez pas UTF-8, vous devrez le changer pour le codage approprié que vous utilisez:

http://fr.cppreference.com/w/cpp/header/codecvt

286
dk123

Solution de: http://forums.devshed.com/c-programming-42/wstring-to-string-444006.html

std::wstring wide( L"Wide" ); 
std::string str( wide.begin(), wide.end() );

// Will print no problemo!
std::cout << str << std::endl;

Méfiez-vous du fait qu'il n'y a pas de conversion de jeu de caractères . Cela consiste simplement à affecter chaque wchar_t itéré à un char - une conversion tronquée. Il utilise le std :: string c'tor :

template< class InputIt >
basic_string( InputIt first, InputIt last,
              const Allocator& alloc = Allocator() );

Comme indiqué dans les commentaires:

les valeurs 0-127 sont identiques dans pratiquement tous les codages; par conséquent, les valeurs tronquées inférieures à 127 résultent dans le même texte. Mettez un caractère chinois et vous verrez l'échec.

-

les valeurs 128-255 de la page de codes Windows 1252 (valeur par défaut de Windows anglais) et les valeurs 128-255 de Unicode sont généralement identiques, donc si c'est la page de code que vous utilisez, la plupart de ces caractères doivent être tronqués aux valeurs correctes. (Je m'attendais totalement à et à fonctionner, je sais que notre code au travail dépend de cela pour é, que je corrigerai bientôt)

Et notez que les points de code dans la plage 0x80 - 0x9F dans Win1252 ne fonctionneront pas . Ceci inclut , œ, ž, Ÿ, ...

120
namar0x0309

Au lieu d'inclure les paramètres régionaux et tout ce qui est compliqué, si vous savez que pour FACT, votre chaîne est convertible, procédez comme suit:

#include <iostream>
#include <string>

using namespace std;

int main()
{
  wstring w(L"bla");
  string result;
  for(char x : w)
    result += x;

  cout << result << '\n';
}

Exemple live ici

11
Justin Kirk

Je crois que la voie officielle est toujours de passer par les facettes codecvt (vous avez besoin d’une sorte de traduction tenant compte des paramètres régionaux), comme dans

resultCode = use_facet<codecvt<char, wchar_t, ConversionState> >(locale).
  in(stateVar, scratchbuffer, scratchbufferEnd, from, to, toLimit, curPtr);

ou quelque chose comme ça, je n'ai pas de code de travail traîner. Mais je ne suis pas sûr du nombre de personnes qui utilisent ces machines de nos jours et qui demandent simplement des pointeurs vers la mémoire et laissent ICU ou une autre bibliothèque gérer les détails sanglants.

7

Il y a deux problèmes avec le code:

  1. La conversion dans const std::string s( ws.begin(), ws.end() ); n'est pas nécessaire pour mapper correctement les caractères larges sur leur contrepartie étroite. Très probablement, chaque caractère large sera simplement transtypé en char.
    La résolution de ce problème est déjà donnée dans la réponse par kem et implique la fonction narrow de la facette ctype de l'environnement local.

  2. Vous écrivez une sortie sur std::cout et std::wcout dans le même programme. cout et wcout sont associés au même flux (stdout) et le résultat de l'utilisation du même flux à la fois en tant que flux orienté octets (comme cout le fait) et un flux orienté large (comme le fait wcout) n'est pas défini.
    La meilleure option consiste à éviter de mélanger des sorties étroites et larges dans le même flux (sous-jacent). Pour stdout/cout/wcout, vous pouvez essayer de changer l'orientation de stdout lorsque vous passez d'une sortie large à une sortie étroite (ou inversement):

    #include <iostream>
    #include <stdio.h>
    #include <wchar.h>
    
    int main() {
        std::cout << "narrow" << std::endl;
        fwide(stdout, 1); // switch to wide
        std::wcout << L"wide" << std::endl;
        fwide(stdout, -1); // switch to narrow
        std::cout << "narrow" << std::endl;
        fwide(stdout, 1); // switch to wide
        std::wcout << L"wide" << std::endl;
    }
    
6

Vous pouvez aussi bien utiliser la méthode étroite de la facette ctype directement:

 # include <clocale> 
 # include <locale> 
 # include <chaîne> 
 # include <vecteur> 
 
 inline std :: string étroit (std :: wstring const & text) 
 {
 std :: locale const loc (""); 
 wchar_t const * from = text.c_str ( ); 
 std :: size_t const len ​​= text.size (); 
 std :: vecteur <char> tampon (len + 1); 
 std :: use_facet <std :: ctype <wchar_t>> (loc) .narrow (from, from + len, '_', & buffer [0]); 
 renvoie std :: string (& buffer [0], & buffer [len]) ; 
} 
6
legalize

Au moment d'écrire cette réponse, la recherche numéro un sur Google pour "convertir string wstring" vous mènerait sur cette page. Ma réponse montre comment convertir une chaîne en chaîne de caractères, bien que ce ne soit PAS la question à la base, et je devrais probablement supprimer cette réponse, mais celle-ci est considérée comme une mauvaise forme. Vous pouvez vouloir passer à cette réponse StackOverflow , qui est maintenant mieux classé que cette page.


Voici un moyen de combiner des constantes de chaîne, wstring et chaîne mixte pour wstring. Utilisez la classe wstringstream.

#include <sstream>

std::string narrow = "narrow";
std::wstring wide = "wide";

std::wstringstream cls;
cls << " abc " << narrow.c_str() << L" def " << wide.c_str();
std::wstring total= cls.str();
6
Mark Lakata

Encodage par défaut sur:

  • Windows UTF-16.
  • Linux UTF-8.
  • MacOS UTF-8.

Ce code a deux formes pour convertir std :: string en std :: wstring et std :: wstring en std :: string. Si vous niez #if défini WIN32, vous obtenez le même résultat.

1. std :: string à std :: wstring

MultiByteToWideChar WinAPI

_ mbstowcs_s_l

#if defined WIN32
#include <windows.h>
#endif

std::wstring StringToWideString(std::string str)
{
    if (str.empty())
    {
        return std::wstring();
    }
    size_t len = str.length() + 1;
    std::wstring ret = std::wstring(len, 0);
#if defined WIN32
    int size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, &str[0], str.size(), &ret[0], len);
    ret.resize(size);
#else
    size_t size = 0;
    _locale_t lc = _create_locale(LC_ALL, "en_US.UTF-8");
    errno_t retval = _mbstowcs_s_l(&size, &ret[0], len, &str[0], _TRUNCATE, lc);
    _free_locale(lc);
    ret.resize(size - 1);
#endif
    return ret;
}

2. std :: wstring à std :: string

WideCharToMultiByte WinAPI

_ wcstombs_s_l

std::string WidestringToString(std::wstring wstr)
{
    if (wstr.empty())
    {
        return std::string();
    }
#if defined WIN32
    int size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &wstr[0], wstr.size(), NULL, 0, NULL, NULL);
    std::string ret = std::string(size, 0);
    WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &wstr[0], wstr.size(), &ret[0], size, NULL, NULL);
#else
    size_t size = 0;
    _locale_t lc = _create_locale(LC_ALL, "en_US.UTF-8");
    errno_t err = _wcstombs_s_l(&size, NULL, 0, &wstr[0], _TRUNCATE, lc);
    std::string ret = std::string(size, 0);
    err = _wcstombs_s_l(&size, &ret[0], size, &wstr[0], _TRUNCATE, lc);
    _free_locale(lc);
    ret.resize(size - 1);
#endif
    return ret;
}

3. Sous Windows, vous devez imprimer unicode à l’aide de WinAPI.

WriteConsole

#if defined _WIN32
    void WriteLineUnicode(std::string s)
    {
        std::wstring unicode = StringToWideString(s);
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), unicode.length(), NULL, NULL);
        std::cout << std::endl;
    }

    void WriteUnicode(std::string s)
    {
        std::wstring unicode = StringToWideString(s);
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), unicode.length(), NULL, NULL);
    }

    void WriteLineUnicode(std::wstring ws)
    {
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), ws.length(), NULL, NULL);
        std::cout << std::endl;
    }

    void WriteUnicode(std::wstring ws)
    {
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), ws.length(), NULL, NULL);
    }

4. Au programme principal.

#if defined _WIN32
int wmain(int argc, WCHAR ** args)
#else
int main(int argc, CHAR ** args)
#endif
{
    std::string source = u8"ÜüΩωЙ你月曜日\na????èéøÞǽлљΣæča????????";
    std::wstring wsource = L"ÜüΩωЙ你月曜日\na????èéøÞǽлљΣæča????????";

    WriteLineUnicode(L"@" + StringToWideString(source) + L"@");
    WriteLineUnicode("@" + WidestringToString(wsource) + "@");
    return EXIT_SUCCESS;
}

5. Enfin, vous avez besoin d’un support puissant et complet pour les caractères unicode dans la console. Je recommande ConEm et défini comme terminal par défaut sous Windows . Vous devez connecter Visual Studio à ConEmu. Rappelez-vous que le fichier exe de Visual Studio est devenv.exe

Testé sur Visual Studio 2017 avec VC++; std = c ++ 17.

Résultat

Result1

3
Joma

Cette solution est inspirée de la solution de dk123, mais utilise une facette codecvt dépendante de la localisation. Le résultat est une chaîne codée en langue locale au lieu de utf8 (si elle n'est pas définie en tant que locale):

std::string w2s(const std::wstring &var)
{
   static std::locale loc("");
   auto &facet = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t>>(loc);
   return std::wstring_convert<std::remove_reference<decltype(facet)>::type, wchar_t>(&facet).to_bytes(var);
}

std::wstring s2w(const std::string &var)
{
   static std::locale loc("");
   auto &facet = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t>>(loc);
   return std::wstring_convert<std::remove_reference<decltype(facet)>::type, wchar_t>(&facet).from_bytes(var);
}

Je le cherchais, mais je ne le trouve pas. Enfin, j’ai trouvé que je pouvais obtenir la facette droite de std :: locale en utilisant la fonction std :: use_facet () avec le nom de type correct. J'espère que cela t'aides.

3
Vizor

Dans mon cas, je dois utiliser un caractère multi-octets (MBCS) et je veux utiliser std :: string et std :: wstring. Et ne peut pas utiliser c ++ 11. J'utilise donc mbstowcs et wcstombs.

Je fais la même chose avec new, delete [], mais c'est plus lent que ça.

Cela peut aider Comment: convertir entre différents types de chaînes

EDIT

Cependant, dans le cas d'une conversion en chaîne wstring et chaîne source, il n'y a pas d'alphabet ni de chaîne multi-octets, cela ne fonctionne pas. Je change donc de wcstombs en WideCharToMultiByte.

#include <string>

std::wstring get_wstr_from_sz(const char* psz)
{
    //I think it's enough to my case
    wchar_t buf[0x400];
    wchar_t *pbuf = buf;
    size_t len = strlen(psz) + 1;

    if (len >= sizeof(buf) / sizeof(wchar_t))
    {
        pbuf = L"error";
    }
    else
    {
        size_t converted;
        mbstowcs_s(&converted, buf, psz, _TRUNCATE);
    }

    return std::wstring(pbuf);
}

std::string get_string_from_wsz(const wchar_t* pwsz)
{
    char buf[0x400];
    char *pbuf = buf;
    size_t len = wcslen(pwsz)*2 + 1;

    if (len >= sizeof(buf))
    {
        pbuf = "error";
    }
    else
    {
        size_t converted;
        wcstombs_s(&converted, buf, pwsz, _TRUNCATE);
    }

    return std::string(pbuf);
}

ÉDITEZ pour utiliser 'MultiByteToWideChar' au lieu de 'wcstombs'

#include <Windows.h>
#include <boost/shared_ptr.hpp>
#include "string_util.h"

std::wstring get_wstring_from_sz(const char* psz)
{
    int res;
    wchar_t buf[0x400];
    wchar_t *pbuf = buf;
    boost::shared_ptr<wchar_t[]> shared_pbuf;

    res = MultiByteToWideChar(CP_ACP, 0, psz, -1, buf, sizeof(buf)/sizeof(wchar_t));

    if (0 == res && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        res = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);

        shared_pbuf = boost::shared_ptr<wchar_t[]>(new wchar_t[res]);

        pbuf = shared_pbuf.get();

        res = MultiByteToWideChar(CP_ACP, 0, psz, -1, pbuf, res);
    }
    else if (0 == res)
    {
        pbuf = L"error";
    }

    return std::wstring(pbuf);
}

std::string get_string_from_wcs(const wchar_t* pcs)
{
    int res;
    char buf[0x400];
    char* pbuf = buf;
    boost::shared_ptr<char[]> shared_pbuf;

    res = WideCharToMultiByte(CP_ACP, 0, pcs, -1, buf, sizeof(buf), NULL, NULL);

    if (0 == res && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        res = WideCharToMultiByte(CP_ACP, 0, pcs, -1, NULL, 0, NULL, NULL);

        shared_pbuf = boost::shared_ptr<char[]>(new char[res]);

        pbuf = shared_pbuf.get();

        res = WideCharToMultiByte(CP_ACP, 0, pcs, -1, pbuf, res, NULL, NULL);
    }
    else if (0 == res)
    {
        pbuf = "error";
    }

    return std::string(pbuf);
}
3
heon

Au cas où quelqu'un d'autre serait intéressé: j'avais besoin d'une classe pouvant être utilisée indifféremment partout où l'on s'attendait à une string ou wstring. La classe suivante convertible_string, basée sur solution de dk12 , peut être initialisée avec un string, char const*, wstring ou wchar_t const* et peut être assigné à ou converti implicitement en string ou wstring (peut donc être passé à une fonction prenant l'une ou l'autre).

class convertible_string
{
public:
    // default ctor
    convertible_string()
    {}

    /* conversion ctors */
    convertible_string(std::string const& value) : value_(value)
    {}
    convertible_string(char const* val_array) : value_(val_array)
    {}
    convertible_string(std::wstring const& wvalue) : value_(ws2s(wvalue))
    {}
    convertible_string(wchar_t const* wval_array) : value_(ws2s(std::wstring(wval_array)))
    {}

    /* assignment operators */
    convertible_string& operator=(std::string const& value)
    {
        value_ = value;
        return *this;
    }
    convertible_string& operator=(std::wstring const& wvalue)
    {
        value_ = ws2s(wvalue);
        return *this;
    }

    /* implicit conversion operators */
    operator std::string() const { return value_; }
    operator std::wstring() const { return s2ws(value_); }
private:
    std::string value_;
};
1
James Hirschorn
#include <boost/locale.hpp>
namespace lcv = boost::locale::conv;

inline std::wstring fromUTF8(const std::string& s)
{ return lcv::utf_to_utf<wchar_t>(s); }

inline std::string toUTF8(const std::wstring& ws)
{ return lcv::utf_to_utf<char>(ws); }
0
deep125