C ++ Konwertuj ciąg (lub char *) na wstring (lub wchar_t *)


171
string s = "おはよう";
wstring ws = FUNCTION(s, ws);

Jak przypisać zawartość s do ws?

Przeszukałem google i zastosowałem kilka technik, ale nie mogą przypisać dokładnej treści. Treść jest zniekształcona.


7
Myślę, że nie stringsakceptuje znaków> 8-bitowych. Czy jest już zakodowany w UTF-8?
— kennytm

3
Jakie jest kodowanie twojego systemu, że "おはよう"utworzyłoby łańcuch zakodowany w systemie?
— sbi

Wierzę, że MSVC to zaakceptuje i sprawi, że będzie to kodowanie wielobajtowe, może UTF-8.
— Potatoswatter

1
@Potatoswatter: MSVC nie używa domyślnie UTF-8 do WSZYSTKIEGO. Jeśli wprowadzisz te znaki, zapyta, do jakiego kodowania przekonwertować plik, i domyślnie przyjmuje stronę kodową 1252.
— Mooing Duck

2
@Samir: ważniejsze jest, jakie jest kodowanie pliku ? Czy możesz przenieść ten ciąg na początek pliku i wyświetlić zrzut heksowy tej części? Prawdopodobnie możemy to zidentyfikować na podstawie tego.
— Mooing Duck

Odpowiedzi:


239

Zakładając, że ciąg wejściowy w twoim przykładzie (お は よ う) jest zakodowany w UTF-8 (wygląda na to, że tak nie jest, ale załóżmy, że to ze względu na to wyjaśnienie :-)) reprezentacja ciągu Unicode Twojego zainteresowania, możesz go w pełni rozwiązać za pomocą samej biblioteki standardowej (C ++ 11 i nowsze).

Wersja TL; DR:

#include <locale>
#include <codecvt>
#include <string>

std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::string narrow = converter.to_bytes(wide_utf16_source_string);
std::wstring wide = converter.from_bytes(narrow_utf8_source_string);

Dłuższy przykład do kompilacji i uruchomienia online:

(Wszystkie pokazują ten sam przykład. Jest ich tylko wiele do nadmiarowości ...)

Uwaga (stara) :

Jak wskazano w komentarzach i wyjaśniono w https://stackoverflow.com/a/17106065/6345, zdarzają się przypadki, gdy użycie standardowej biblioteki do konwersji między UTF-8 i UTF-16 może dać nieoczekiwane różnice w wynikach na różnych platformach . Aby uzyskać lepszą konwersję, rozważ rozwiązania std::codecvt_utf8opisane na http://en.cppreference.com/w/cpp/locale/codecvt_utf8

Uwaga (nowa) :

Ponieważ codecvtnagłówek jest przestarzały w C ++ 17, pojawiły się pewne obawy dotyczące rozwiązania przedstawionego w tej odpowiedzi. Jednak komitet normalizacyjny C ++ dodał ważne oświadczenie w http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html mówiąc

ten element biblioteki należy wycofać do Załącznika D, obok siebie, do czasu znormalizowania odpowiedniego zamiennika.

Zatem w dającej się przewidzieć przyszłości codecvtrozwiązanie zawarte w tej odpowiedzi będzie bezpieczne i przenośne.


2
Sprawdź, jakim kodowaniem zapisujesz pliki VS
— Johann Gerell

9
Pamiętaj, że to jest tylko C ++ 11!
— bk138

1
W minGW (gcc / g ++ 4.8.1 i -std = c ++ 11) nagłówek codecvt nie istnieje. Czy jest alternatywa?
— Brian Jack,

1
Proszę podać przykład std::codecvt_utf8dla początkujących
— Noitidart

14
Należy pamiętać, że <codecvt>jest to przestarzałe od C ++ 17.
— tambre

47
int StringToWString(std::wstring &ws, const std::string &s)
{
    std::wstring wsTmp(s.begin(), s.end());

    ws = wsTmp;

    return 0;
}

93
Działa to tylko wtedy, gdy wszystkie znaki są jednobajtowe, np. ASCII lub ISO-8859-1 . Wszystko wielobajtowe zakończy się niepowodzeniem, w tym UTF-8. Pytanie wyraźnie zawiera znaki wielobajtowe.
— Mark Ransom

28
Ta odpowiedź jest oczywiście niewystarczająca i nie robi nic poza kopiowaniem wąskich znaków, tak jak w przypadku szerokich znaków. Zobacz inne odpowiedzi, szczególnie tę autorstwa Johanna Gerella, aby dowiedzieć się, jak prawidłowo przejść z ciągu wielobajtowego lub utf8 do łańcucha utf16.
— DLRdave

10
ta odpowiedź jest niebezpieczna i prawdopodobnie zepsuje się w systemie innym niż ASCII. tzn. arabska nazwa pliku zostanie zniekształcona przez ten hack.
— Stephen

9
Ta odpowiedź jest przydatna, jeśli zignorujesz niuanse treści pytania i skupisz się na tytule pytania, który sprowadził mnie tutaj z Google. W obecnej sytuacji tytuł pytania jest wyjątkowo mylący i powinien zostać zmieniony, aby odzwierciedlić prawdziwe pytanie, które zostało zadane
— Anne Quinn

3
Działa to tylko dla 7-bitowych znaków ASCII. Dla latin1 działa tylko wtedy, gdy char jest skonfigurowany jako unsigned. Jeśli typ char jest podpisany (co ma miejsce w większości przypadków), znaki> 127 dadzą nieprawidłowe wyniki.
— huyc

32

Twoje pytanie jest nieokreślone. Ściśle rzecz biorąc, ten przykład jest błędem składniowym. Jednak,std::mbstowcs prawdopodobnie jest to, czego szukasz.

Jest to funkcja biblioteki C i działa na buforach, ale oto łatwy w użyciu idiom, dzięki uprzejmości TBohne (dawniej Mooing Duck):

std::wstring ws(s.size(), L' '); // Overestimate number of code points.
ws.resize(std::mbstowcs(&ws[0], s.c_str(), s.size())); // Shrink to fit.

1
string s = "お は よ う"; wchar_t * buf = new wchar_t [s.size ()]; size_t num_chars = mbstowcs (buf, s.c_str (), s.size ()); wstring ws (buf, num_chars); // ws = zniekształcone
— Samir

1
@Samir: Musisz upewnić się, że kodowanie w czasie wykonywania jest takie samo jak kodowanie w czasie kompilacji. Może być konieczne setlocaledostosowanie flag kompilatora. Nie wiem, ponieważ nie używam systemu Windows, ale dlatego nie jest to powszechna funkcja. Jeśli to możliwe, rozważ drugą odpowiedź.
— Potatoswatter

1
std::string ws(s.size()); ws.resize(mbstowcs(&ws[0], s.c_str(), s.size());RAII FTW
— Mooing Duck

2
@WaffleSouffle To jest nieaktualne. Ciągłe implementacje są wymagane od 2011 roku, a implementacje rezygnują z takich sztuczek na długo przed tym.
— Potatoswatter

1
a niektóre środowiska, takie jak mingw, nadal nie mają nagłówka codecvt, więc niektóre z „lepszych” rozwiązań wcześniej nie działają, co oznacza, że ​​ten problem nadal nie ma dobrych rozwiązań w mingw, nawet od grudnia 2014 r.
— Brian Jack,

18

Tylko Windows API, implementacja przed C ++ 11, na wypadek, gdyby ktoś tego potrzebował:

#include <stdexcept>
#include <vector>
#include <windows.h>

using std::runtime_error;
using std::string;
using std::vector;
using std::wstring;

wstring utf8toUtf16(const string & str)
{
   if (str.empty())
      return wstring();

   size_t charsNeeded = ::MultiByteToWideChar(CP_UTF8, 0, 
      str.data(), (int)str.size(), NULL, 0);
   if (charsNeeded == 0)
      throw runtime_error("Failed converting UTF-8 string to UTF-16");

   vector<wchar_t> buffer(charsNeeded);
   int charsConverted = ::MultiByteToWideChar(CP_UTF8, 0, 
      str.data(), (int)str.size(), &buffer[0], buffer.size());
   if (charsConverted == 0)
      throw runtime_error("Failed converting UTF-8 string to UTF-16");

   return wstring(&buffer[0], charsConverted);
}

Możesz to zoptymalizować. Nie ma potrzeby wykonywania podwójnej kopii ciągu przy użyciu rozszerzenia vector. Wystarczy zarezerwować znaków w ciąg wykonując wstring strW(charsNeeded + 1);a następnie używać go jako bufora dla konwersji: &strW[0]. Na koniec upewnij się, że ostatni null jest obecny po konwersji, wykonującstrW[charsNeeded] = 0;
— c00000fd

1
@ c00000fd, o ile wiem, wewnętrzny bufor std :: basic_string musi być ciągły tylko od standardu C ++ 11. Mój kod jest w wersji starszej niż C ++ 11, jak zaznaczono na górze postu. Dlatego kod & strW [0] nie byłby zgodny ze standardami i mógłby legalnie ulec awarii w czasie wykonywania.
— Alex Che,

13

Jeśli korzystasz z systemu Windows / Visual Studio i chcesz przekonwertować ciąg na wstring, możesz użyć:

#include <AtlBase.h>
#include <atlconv.h>
...
string s = "some string";
CA2W ca2w(s.c_str());
wstring w = ca2w;
printf("%s = %ls", s.c_str(), w.c_str());

Ta sama procedura konwertowania wstring na łańcuch znaków (czasami trzeba określić stronę kodową ):

#include <AtlBase.h>
#include <atlconv.h>
...
wstring w = L"some wstring";
CW2A cw2a(w.c_str());
string s = cw2a;
printf("%s = %ls", s.c_str(), w.c_str());

Możesz określić stronę kodową, a nawet UTF8 (to całkiem przyjemne podczas pracy z JNI / Java ). W tej odpowiedzi pokazano standardowy sposób konwersji std :: wstring na utf8 std :: string .

// 
// using ATL
CA2W ca2w(str, CP_UTF8);

// 
// or the standard way taken from the answer above
#include <codecvt>
#include <string>

// convert UTF-8 string to wstring
std::wstring utf8_to_wstring (const std::string& str) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
    return myconv.from_bytes(str);
}

// convert wstring to UTF-8 string
std::string wstring_to_utf8 (const std::wstring& str) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
    return myconv.to_bytes(str);
}

Jeśli chcesz dowiedzieć się więcej o stronach kodowych , jest ciekawy artykuł o Joel na temat oprogramowania: absolutne minimum Każdy programista Absolutnie, pozytywnie musi wiedzieć o Unicode i zestawach znaków .

Te makra CA2W (Convert Ansi to Wide = unicode) są częścią makr konwersji ciągów ATL i MFC , w tym przykłady.

Czasami będziesz musiał wyłączyć ostrzeżenie o zabezpieczeniach # 4995 ', nie znam innego obejścia (dla mnie to się zdarza, gdy kompilowałem dla WindowsXp w VS2012).

#pragma warning(push)
#pragma warning(disable: 4995)
#include <AtlBase.h>
#include <atlconv.h>
#pragma warning(pop)

Edycja: Cóż, zgodnie z tym artykułem artykuł Joela wygląda na: „podczas rozrywki, jest dość lekki, jeśli chodzi o rzeczywiste szczegóły techniczne”. Artykuł: Co każdy programista absolutnie i pozytywnie powinien wiedzieć o kodowaniu i zestawach znaków do pracy z tekstem .


Przepraszam, nie jestem rodzimym użytkownikiem języka angielskiego. Edytuj według własnego uznania.
— lmiguelmh

Co słychać u przeciwnika? Co jest nie tak z odpowiedzią?
— lmiguelmh

Prawdopodobnie fakt, że promuje nieprzenośny kod.
— Pavel Minaev

Tak, dlatego stwierdziłem, że działa to tylko w Windows / Visual Studio. Ale przynajmniej to rozwiązanie jest poprawne, a nie to:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
— lmiguelmh

Uwaga dodatkowa: CA2W znajduje się w przestrzeni nazw ATL. (ATL :: CA2W)
— Val

12

Oto sposób na połączenie string, wstringi mieszane stałe łańcuchowe do wstring. Skorzystaj z wstringstreamklasy.

To NIE działa w przypadku wielobajtowego kodowania znaków. Jest to po prostu głupi sposób na odrzucenie bezpieczeństwa typów i rozszerzenie 7-bitowych znaków ze std :: string do 7 niższych bitów każdego znaku std: wstring. Jest to przydatne tylko wtedy, gdy masz 7-bitowe ciągi ASCII i musisz wywołać interfejs API, który wymaga szerokich ciągów.

#include <sstream>

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

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

Odpowiedź wydaje się interesująca. Czy mógłbyś trochę wyjaśnić: czy to zadziała w przypadku kodowania wielobajtowego i dlaczego / jak?
— wh1t3cat1k

schematy kodowania są ortogonalne w stosunku do klasy pamięci. stringprzechowuje znaki 1-bajtowe i wstring2-bajtowe. coś w rodzaju utf8 przechowuje znaki wielobajtowe jako serie wartości 1-bajtowych, tj. w pliku string. klasy łańcuchowe nie pomagają w kodowaniu. Nie jestem ekspertem w kodowaniu klas w języku c ++.
— Mark Lakata

2
Czy jest jakiś powód, dla którego ta odpowiedź nie jest najlepszą, biorąc pod uwagę, jak krótka i prosta jest? Jakieś przypadki, których nie obejmuje?
— Ryuu

@MarkLakata, przeczytałem twoją odpowiedź na pierwszy komentarz, ale nadal nie jestem pewien. Czy zadziała w przypadku znaków wielobajtowych? Innymi słowy, czy nie jest podatna na taką samą pułapkę jak ta odpowiedź ?
— Marc. 2377

@ Marc.2377 To NIE działa w przypadku wielobajtowego kodowania znaków. Jest to po prostu głupi sposób na odrzucenie bezpieczeństwa typów i rozszerzenie 7-bitowych znaków std::stringna 7 niższych bitów każdego znaku std:wstring. Jest to przydatne tylko wtedy, gdy masz 7-bitowe ciągi ASCII i musisz wywołać interfejs API, który wymaga szerokich ciągów. Zajrzyj na stackoverflow.com/a/8969776/3258851, jeśli potrzebujesz czegoś bardziej wyrafinowanego.
— Mark Lakata,

11

Od char*do wstring:

char* str = "hello worlddd";
wstring wstr (str, str+strlen(str));

Od stringdo wstring:

string str = "hello worlddd";
wstring wstr (str.begin(), str.end());

Zauważ, że działa to dobrze tylko wtedy, gdy konwertowany ciąg zawiera tylko znaki ASCII.


7
Ponieważ działa to tylko wtedy, gdy kodowanie to Windows-1252, który nie może nawet pomieścić liter w pytaniu.
— Mooing Duck

3
jest to najmniej podatny na błędy sposób, gdy wiesz, że masz do czynienia z ASCII. Co jest znaczącym przypadkiem podczas przenoszenia aplikacji na nowsze interfejsy API.
— Sid Sarasvati,

To nie jest droga. Jeśli używasz programu Visual Studio, powinieneś użyć atlconv.h. Sprawdź inne odpowiedzi.
— lmiguelmh

7

przy użyciu Boost.Locale:

ws = boost::locale::conv::utf_to_utf<wchar_t>(s);

5

Ten wariant jest moim ulubionym w prawdziwym życiu. Konwertuje dane wejściowe, jeśli jest to poprawne UTF-8, na odpowiednie wstring. Jeśli dane wejściowe są uszkodzone, element wstringjest konstruowany z pojedynczych bajtów. Jest to niezwykle pomocne, jeśli nie masz pewności co do jakości danych wejściowych.

std::wstring convert(const std::string& input)
{
    try
    {
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        return converter.from_bytes(input);
    }
    catch(std::range_error& e)
    {
        size_t length = input.length();
        std::wstring result;
        result.reserve(length);
        for(size_t i = 0; i < length; i++)
        {
            result.push_back(input[i] & 0xFF);
        }
        return result;
    }
}

1
Właśnie uruchomiłem to pytanie w oparciu o twoją odpowiedź stackoverflow.com/questions/49669048/ ... czy możesz uprzejmie
— rzucić

2

Jeśli masz QT i jesteś leniwy, aby zaimplementować funkcję i inne rzeczy, których możesz użyć

std :: string str; QString (str) .toStdWString ()


Prawie, ale powinieneś zacząć od a QString, ponieważ QStringkonstruktor z jakiegoś powodu nie może zaakceptować łańcucha.
— bobsbeenjamin


To jest miłe. Możesz także użyć .c_str (), aby pozwolić QString zaakceptować twój łańcuch w konstruktorze.
— miep

1

metoda s2ws działa dobrze. Nadzieja pomaga.

std::wstring s2ws(const std::string& s) {
    std::string curLocale = setlocale(LC_ALL, ""); 
    const char* _Source = s.c_str();
    size_t _Dsize = mbstowcs(NULL, _Source, 0) + 1;
    wchar_t *_Dest = new wchar_t[_Dsize];
    wmemset(_Dest, 0, _Dsize);
    mbstowcs(_Dest,_Source,_Dsize);
    std::wstring result = _Dest;
    delete []_Dest;
    setlocale(LC_ALL, curLocale.c_str());
    return result;
}

6
O co chodzi z tymi wszystkimi odpowiedziami, które przydzielają pamięć dynamiczną w niebezpieczny sposób, a następnie kopiują dane z bufora do łańcucha? Dlaczego nikt nie pozbywa się niebezpiecznego pośrednika?
— Mooing Duck

hahakubile, czy możesz pomóc z czymś podobnym dla ws2s?
— cristian

1

Na podstawie moich własnych testów (w systemie Windows 8, vs2010) mbstowcs może w rzeczywistości uszkodzić oryginalny ciąg, działa tylko ze stroną kodową ANSI. Jeśli MultiByteToWideChar / WideCharToMultiByte może również powodować uszkodzenie ciągu - ale mają one tendencję do zastępowania znaków, których nie znają, znakiem „?” znaki zapytania, ale mbstowcs ma tendencję do zatrzymywania się, gdy napotyka nieznany znak i przecina ciąg w tym momencie. (Testowałem wietnamskie znaki na fińskich oknach).

Dlatego preferuj funkcję Multi * -windows API zamiast analogowych funkcji ANSI C.

Zauważyłem również, że najkrótszym sposobem zakodowania ciągu znaków z jednej strony kodowej na drugą nie jest użycie funkcji API MultiByteToWideChar / WideCharToMultiByte, ale ich analogowe makra ATL: W2A / A2W.

Tak więc funkcja analogowa, jak wspomniano powyżej, brzmiałaby tak:

wstring utf8toUtf16(const string & str)
{
   USES_CONVERSION;
   _acp = CP_UTF8;
   return A2W( str.c_str() );
}

_acp jest zadeklarowane w makrze USES_CONVERSION.

Lub też funkcja, której często brakuje mi podczas konwersji starych danych na nowe:

string ansi2utf8( const string& s )
{
   USES_CONVERSION;
   _acp = CP_ACP;
   wchar_t* pw = A2W( s.c_str() );

   _acp = CP_UTF8;
   return W2A( pw );
}

Ale proszę zauważyć, że te makra używają silnie stosu - nie używaj pętli do pętli ani pętli rekurencyjnych dla tej samej funkcji - po użyciu makra W2A lub A2W - lepiej zwracają jak najszybciej, więc stos zostanie zwolniony z tymczasowej konwersji.


1

Ciąg do sznurka

std::wstring Str2Wstr(const std::string& str)
{
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

wstring na String

std::string Wstr2Str(const std::wstring& wstr)
{
    typedef std::codecvt_utf8<wchar_t> convert_typeX;
    std::wstring_convert<convert_typeX, wchar_t> converterX;
    return converterX.to_bytes(wstr);
}

1
Ten Str2Wstr ma problem z zakończeniem 0. Nie jest już możliwe łączenie wygenerowanych ciągów znaków za pomocą „+” (jak w przypadku s3 = s1 + s2). Wkrótce opublikuję odpowiedź rozwiązującą ten problem. Najpierw muszę przeprowadzić testy na wycieki pamięci.
— thewhiteambit

-2

string s = "おはよう"; jest błędem.

Powinieneś użyć wstring bezpośrednio:

wstring ws = L"おはよう";

1
To też nie zadziała. Będziesz musiał przekonwertować te znaki inne niż BMP na sekwencje specjalne C.
— Dave Van den Eynde

3
@Dave: to działa, jeśli twój kompilator obsługuje Unicode w plikach źródłowych, a wszystkie te z ostatniej dekady tak (Visual Studio, gcc, ...)
— Thomas Bonini

Cześć, niezależnie od domyślnego kodowania systemu (na przykład mogę mieć arabski jako domyślne kodowanie systemowe), jakie powinno być kodowanie pliku kodu źródłowego dla L "お は よ う"? powinien być w formacie UTF-16, czy też mogę mieć UTF-8 bez BOM do kodowania pliku .cpp?
— Afriza N. Arief

2
@afriza: to naprawdę nie ma znaczenia, o ile Twoja kompilacja to obsługuje
— Thomas Bonini

2
To nie jest błąd; rozszerzone znaki w „wąskim” łańcuchu są definiowane w celu odwzorowania na sekwencje wielobajtowe. Kompilator powinien obsługiwać go tak długo, jak działa system operacyjny, o co najmniej możesz poprosić.
— Potatoswatter

-2

użyj tego kodu, aby przekonwertować swój ciąg na wstring

std::wstring string2wString(const std::string& s){
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}

int main(){
    std::wstring str="your string";
    std::wstring wStr=string2wString(str);
    return 0;
}

3
Zauważ, że w pytaniu nie ma wzmianki o systemie Windows, a ta odpowiedź dotyczy tylko systemu Windows.
— Johann Gerell

CP_ACPjest z pewnością błędnym argumentem. Nagle stan środowiska wątku wykonawczego ma wpływ na zachowanie kodu. Niewskazana. Określ stałe kodowanie znaków w konwersji. (I rozważ obsługę błędów.)
— Niespodziewane
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.