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.