C ++ Konversi string (atau karakter *) ke wstring (atau wchar_t *)

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

Bagaimana saya menetapkan konten s ke ws?

Mencari Google dan menggunakan beberapa teknik tetapi mereka tidak dapat menetapkan konten yang tepat. Konten terdistorsi.

Samir
sumber
7
Saya tidak berpikir stringsmenerima> karakter 8-bit. Apakah sudah dikodekan dalam UTF-8?
kennytm
3
Apa pengkodean sistem Anda yang akan membuat "おはよう"string yang dikodekan sistem?
sbi
Saya percaya MSVC akan menerimanya dan membuatnya menjadi pengkodean multibyte, mungkin UTF-8.
Potatoswatter
1
@Potatoswatter: MSVC tidak menggunakan UTF-8 secara default untuk APA SAJA. Jika Anda memasukkan karakter tersebut, ia meminta pengkodean untuk mengkonversi file, dan default ke codepage 1252.
Mooing Duck
2
@ Samir: lebih penting apa pengkodean file ? Bisakah Anda memindahkan string itu ke awal file dan menunjukkan hexdump dari bagian itu? Kita mungkin bisa mengidentifikasinya dari itu.
Mooing Duck

Jawaban:

239

Mengasumsikan bahwa string input dalam contoh Anda (お は よ う) adalah UTF-8 yang dikodekan (yang tidak terlihat seperti itu, tetapi mari kita asumsikan itu demi penjelasan ini :-)) representasi dari string Unicode yang Anda minati, maka masalah Anda dapat diselesaikan dengan perpustakaan standar (C ++ 11 dan yang lebih baru) saja.

Versi 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);

Contoh kompilasi dan runnable online yang lebih panjang:

(Mereka semua menunjukkan contoh yang sama. Hanya ada banyak untuk redundansi ...)

Catatan (lama) :

Seperti yang ditunjukkan dalam komentar dan dijelaskan di https://stackoverflow.com/a/17106065/6345 ada beberapa kasus ketika menggunakan perpustakaan standar untuk mengkonversi antara UTF-8 dan UTF-16 mungkin memberikan perbedaan yang tidak terduga dalam hasil pada platform yang berbeda . Untuk konversi yang lebih baik, pertimbangkan std::codecvt_utf8seperti yang dijelaskan pada http://en.cppreference.com/w/cpp/locale/codecvt_utf8

Catatan (baru) :

Karena codecvttajuk sudah usang dalam C ++ 17, beberapa kekhawatiran tentang solusi yang disajikan dalam jawaban ini dimunculkan. Namun, komite standar C ++ menambahkan pernyataan penting di http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html mengatakan

komponen perpustakaan ini harus dipensiunkan pada Lampiran D, di samping, sampai penggantian yang sesuai distandarisasi.

Jadi di masa mendatang, codecvtsolusi dalam jawaban ini aman dan portabel.

Johann Gerell
sumber
2
Periksa pengkodean yang Anda simpan dengan file VS
Johann Gerell
9
Ketahuilah bahwa ini adalah khusus C ++ 11!
bk138
1
Dalam minGW (gcc / g ++ 4.8.1 dan -std = c ++ 11) header codecvt tidak ada. Apakah ada alternatif?
Brian Jack
1
Semoga Anda memberikan contoh std::codecvt_utf8untuk pemula
Noitidart
15
Harap dicatat bahwa <codecvt>sudah usang sejak C ++ 17.
tambre
47
int StringToWString(std::wstring &ws, const std::string &s)
{
    std::wstring wsTmp(s.begin(), s.end());

    ws = wsTmp;

    return 0;
}
Pietro M
sumber
93
Ini hanya berfungsi jika semua karakter adalah byte tunggal, yaitu ASCII atau ISO-8859-1 . Multi-byte apa pun akan gagal total, termasuk UTF-8. Pertanyaannya jelas berisi karakter multi-byte.
Mark Ransom
28
Jawaban ini jelas tidak cukup dan tidak melakukan apa-apa selain menyalin karakter sempit seperti ke karakter lebar. Lihat jawaban lain, terutama yang oleh Johann Gerell, untuk cara beralih dari multi-byte atau utf8 encoded string ke utf16 wstring.
DLRdave
10
jawaban ini berbahaya dan mungkin akan rusak pada sistem non-ascii. yaitu nama file arab akan hancur oleh hack ini.
Stephen
9
Jawaban ini berguna jika Anda mengabaikan nuansa tubuh pertanyaan dan fokus pada judul pertanyaan, yang membawa saya ke sini dari Google. Seperti, judul pertanyaan itu sangat menyesatkan dan harus diubah untuk mencerminkan pertanyaan sebenarnya yang diajukan
Anne Quinn
3
Ini hanya berfungsi untuk karakter ASCII 7-bit. Untuk latin1, ini hanya berfungsi jika char dikonfigurasikan sebagai unsigned. Jika tipe char ditandatangani (yang biasanya terjadi), karakter> 127 akan memberikan hasil yang salah.
huyc
32

Pertanyaan Anda kurang spesifik. Sebenarnya, contoh itu adalah kesalahan sintaksis. Namun, std::mbstowcsmungkin itu yang Anda cari.

Ini adalah fungsi C-library dan beroperasi pada buffer, tapi di sini adalah idiom yang mudah digunakan, milik TBohne (sebelumnya 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.
Potatoswatter
sumber
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 = terdistorsi
Samir
1
@ Samir: Anda harus memastikan pengkodean runtime sama dengan pengkodean waktu kompilasi. Anda mungkin perlu setlocaleatau menyesuaikan flag compiler. Saya tidak tahu karena saya tidak menggunakan Windows, tetapi inilah mengapa ini bukan fitur yang umum. Pertimbangkan jawaban yang lain jika memungkinkan.
Potatoswatter
1
std::string ws(s.size()); ws.resize(mbstowcs(&ws[0], s.c_str(), s.size());RAII FTW
Mooing Duck
2
@WaffleSouffle Itu sudah ketinggalan zaman. Implementasi yang berdekatan telah diperlukan sejak 2011 dan implementasi berhenti trik seperti itu jauh sebelum itu.
Potatoswatter
1
dan beberapa lingkungan seperti mingw masih belum memiliki header codecvt sehingga beberapa solusi 'lebih baik' sebelumnya tidak berfungsi yang berarti masalah ini masih belum memiliki solusi yang baik di mingw bahkan pada Desember 2014
Brian Jack
18

Hanya Windows API, sebelum implementasi C ++ 11, jika seseorang membutuhkannya:

#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);
}
Alex Che
sumber
Anda bisa mengoptimalkannya. Tidak perlu melakukan duplikat string dengan menggunakan a vector. Cukup cadangan karakter dalam string dengan melakukan wstring strW(charsNeeded + 1);dan kemudian menggunakannya sebagai penyangga untuk konversi: &strW[0]. Terakhir memastikan nol terakhir ada setelah konversi dengan melakukanstrW[charsNeeded] = 0;
c00000fd
1
@ c00000fd, sejauh yang saya tahu, std :: basic_string buffer internal harus kontinu hanya karena standar C ++ 11. Kode saya adalah pra C ++ 11, sebagaimana tercantum di bagian atas posting. Karenanya, kode & strW [0] tidak sesuai standar dan mungkin mogok secara sah saat runtime.
Alex Che
13

Jika Anda menggunakan Windows / Visual Studio dan perlu mengonversi string ke wstring Anda bisa menggunakan:

#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());

Prosedur yang sama untuk mengonversi wstring ke string (kadang-kadang Anda harus menentukan codepage ):

#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());

Anda dapat menentukan codepage dan bahkan UTF8 (itu cukup bagus ketika bekerja dengan JNI / Java ). Cara standar untuk mengubah string std :: wstring ke utf8 std :: ditunjukkan dalam jawaban ini .

// 
// 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);
}

Jika Anda ingin tahu lebih banyak tentang codepages ada artikel menarik tentang Joel tentang Perangkat Lunak: Minimum Yang Mutlak Setiap Pengembang Perangkat Lunak Sepenuhnya, Pasti Harus Tahu Tentang Unicode dan Set Karakter .

Makro CA2W (Konversi Ansi ke Lebar = unicode) ini adalah bagian dari Makro Konversi String ATL dan MFC , termasuk sampel.

Kadang-kadang Anda harus menonaktifkan peringatan keamanan # 4995 ', saya tidak tahu solusi lain (bagi saya itu terjadi ketika saya dikompilasi untuk Windows XP di VS2012).

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

Sunting: Yah, menurut artikel ini artikel oleh Joel tampaknya: "sambil menghibur, itu cukup ringan pada detail teknis yang sebenarnya". Artikel: Apa Yang Harus Semua Programmer Sepenuhnya Ingin Tahu Tentang Pengkodean dan Set Karakter Untuk Bekerja dengan Teks .

lmiguelmh
sumber
Maaf saya bukan penutur asli bahasa Inggris. Harap edit sesuai keinginan Anda.
lmiguelmh
Ada apa dengan downvoter? Apa yang salah dengan jawabannya?
lmiguelmh
Mungkin fakta bahwa itu mempromosikan kode non-portabel.
Pavel Minaev
Ya, itu sebabnya saya menyatakan bahwa ini hanya berfungsi di Windows / Visual Studio. Tapi setidaknya solusi ini benar, dan bukan yang ini:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
lmiguelmh
Catatan tambahan: CA2W berada di bawah namespace ATL. (ATL :: CA2W)
Val
12

Inilah cara menggabungkan string, wstringdan menggabungkan konstanta string ke wstring. Gunakan wstringstreamkelas.

Ini TIDAK berfungsi untuk pengkodean karakter multi-byte. Ini hanya cara bodoh membuang keamanan tipe dan memperluas karakter 7 bit dari std :: string ke dalam 7 bit yang lebih rendah dari setiap karakter std: wstring. Ini hanya berguna jika Anda memiliki string ASCII 7-bit dan Anda perlu memanggil API yang membutuhkan string luas.

#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();
Mark Lakata
sumber
Jawabannya sepertinya menarik. Bisakah Anda jelaskan sedikit: apakah ini akan berfungsi untuk pengkodean multi-byte, dan mengapa / bagaimana?
wh1t3cat1k
skema pengkodean adalah ortogonal ke kelas penyimpanan. stringmenyimpan 1 byte karakter dan wstringmenyimpan 2 byte karakter. sesuatu seperti utf8 menyimpan karakter mulitbyte sebagai rangkaian nilai 1 byte, yaitu dalam a string. kelas string tidak membantu pengodean. Saya bukan ahli pengkodean kelas di c ++.
Mark Lakata
2
Adakah alasan mengapa jawaban ini bukan jawaban terbaik, mengingat betapa singkat dan sederhananya itu? Adakah kasus yang tidak dicakup?
Ryuu
@ Markakata, saya membaca jawaban Anda untuk komentar pertama tetapi saya masih tidak yakin. Apakah ini akan berfungsi untuk karakter multi-byte? Dengan kata lain, apakah itu tidak rentan terhadap jebakan yang sama dengan jawaban ini ?
Marc.2377
@ Marc.2377 Ini TIDAK berfungsi untuk pengkodean karakter multi-byte. Ini hanya cara bodoh membuang jenis keselamatan dan memperluas karakter 7 bit dari std::stringke 7 bit yang lebih rendah dari masing-masing karakter std:wstring. Ini hanya berguna jika Anda memiliki string ASCII 7-bit dan Anda perlu memanggil API yang membutuhkan string luas. Lihatlah stackoverflow.com/a/8969776/3258851 jika Anda membutuhkan sesuatu yang lebih canggih.
Mark Lakata
11

Dari char* ke wstring:

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

Dari stringkewstring:

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

Perhatikan ini hanya berfungsi dengan baik jika string yang dikonversi hanya berisi karakter ASCII.

Ghominejad
sumber
7
Karena ini hanya berfungsi jika penyandiannya adalah Windows-1252, yang bahkan tidak dapat menahan huruf dalam pertanyaan.
Mooing Duck
3
ini adalah cara paling rawan kesalahan dalam melakukannya, ketika Anda tahu Anda berurusan dengan ASCII. Yang merupakan usecase yang menonjol saat porting aplikasi ke api yang lebih baru.
Sid Sarasvati
Ini bukan jalannya. Jika Anda menggunakan Visual Studio, Anda harus menggunakan atlconv.h. Periksa jawaban lainnya.
lmiguelmh
7

menggunakan Boost.Locale:

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

Varian ini adalah favorit saya di kehidupan nyata. Itu mengkonversi input, jika itu valid UTF-8, untuk masing-masing wstring. Jika input rusak, wstringitu dibangun dari byte tunggal. Ini sangat membantu jika Anda tidak benar-benar yakin tentang kualitas data input Anda.

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;
    }
}
Matthias Ronge
sumber
1
Saya baru saja meluncurkan pertanyaan ini berdasarkan jawaban Anda stackoverflow.com/questions/49669048/… Anda dapat melihatnya
MistyD
2

Jika Anda memiliki QT dan jika Anda malas menerapkan fungsi dan hal-hal yang dapat Anda gunakan

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

Kadir Erdem Demir
sumber
Hampir, tetapi Anda harus memulainya dengan a QString, karena QStringkonstruktor tidak dapat menerima string karena suatu alasan.
bobsbeenjamin
1
Anda dapat menggunakan doc.qt.io/qt-5/qstring.html#fromStdString
Kadir Erdem Demir
Ini bagus. Anda juga dapat menggunakan .c_str () untuk membiarkan QString menerima string Anda di konstruktor.
miep
1

Metode s2ws bekerja dengan baik. Semoga bisa membantu.

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;
}
hahakubile
sumber
6
Apa dengan semua jawaban ini yang mengalokasikan memori dinamis dengan cara yang tidak aman, dan kemudian menyalin data dari buffer ke string? Mengapa tidak ada yang menyingkirkan tengkulak tidak aman?
Mooing Duck
hahakubile, bisakah Anda membantu dengan sesuatu yang serupa untuk ws2s?
cristian
1

Berdasarkan pengujian saya sendiri (Pada windows 8, vs2010) mbstowcs sebenarnya dapat merusak string asli, ia hanya bekerja dengan halaman kode ANSI. Jika MultiByteToWideChar / WideCharToMultiByte juga dapat menyebabkan korupsi string - tetapi mereka cenderung mengganti karakter yang tidak mereka kenal dengan '?' tanda tanya, tetapi mbstowc cenderung berhenti ketika bertemu dengan karakter yang tidak dikenal dan memotong string pada saat itu. (Saya telah menguji karakter Vietnam di windows Finlandia).

Jadi lebih suka Multi * -windows fungsi api daripada fungsi ansi C analog.

Juga apa yang saya perhatikan cara terpendek untuk menyandikan string dari satu codepage ke yang lain tidak menggunakan MultiByteToWideChar / WideCharToMultiByte panggilan fungsi api tetapi analog ATL makro mereka: W2A / A2W.

Jadi fungsi analog seperti yang disebutkan di atas akan terdengar seperti:

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

_acp dideklarasikan dalam makro USES_CONVERSION.

Atau juga fungsi yang sering saya lewatkan ketika melakukan konversi data lama ke yang baru:

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

   _acp = CP_UTF8;
   return W2A( pw );
}

Tetapi harap perhatikan bahwa makro itu sangat banyak menggunakan stack - jangan gunakan untuk loop atau loop rekursif untuk fungsi yang sama - setelah menggunakan makro W2A atau A2W - lebih baik untuk mengembalikan ASAP, jadi stack akan dibebaskan dari konversi sementara.

TarmoPikaro
sumber
1

String to wstring

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 ke 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);
}
Isma Rekathakusuma
sumber
1
Str2Wstr ini memiliki masalah dengan 0 terminasi. Tidak mungkin untuk menyatukan wstrings yang dihasilkan lagi melalui "+" (seperti di wstring s3 = s1 + s2). Saya akan mengirim jawaban segera menyelesaikan masalah ini. Harus melakukan beberapa pengujian untuk kebocoran memori terlebih dahulu.
thewhiteambit
-2

string s = "おはよう"; adalah kesalahan.

Anda harus menggunakan wstring secara langsung:

wstring ws = L"おはよう";
Thomas Bonini
sumber
1
Itu juga tidak akan berhasil. Anda harus mengonversi karakter non-BMP ke urutan pelarian C.
Dave Van den Eynde
3
@ Dave: ini berfungsi jika kompiler Anda mendukung unicode dalam file sumber, dan semua yang dalam dekade terakhir melakukannya (visual studio, gcc, ...)
Thomas Bonini
Hai, terlepas dari pengkodean sistem default (saya mungkin memiliki bahasa Arab sebagai pengkodean sistem default saya misalnya), apa yang seharusnya pengkodean file kode sumber untuk L "お は よ う" berfungsi? haruskah dalam UTF-16, atau bisakah saya memiliki UTF-8 tanpa BOM untuk pengkodean file .cpp?
Afriza N. Arief
2
@afriza: tidak masalah asalkan kompilasi Anda mendukungnya
Thomas Bonini
2
Itu bukan kesalahan; karakter diperluas dalam string "sempit" didefinisikan untuk memetakan ke urutan multibyte. Kompiler harus mendukungnya selama OS melakukannya, yang paling tidak bisa Anda tanyakan.
Potatoswatter
-2

gunakan kode ini untuk mengonversi string Anda menjadi 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;
}
jaguar
sumber
3
Perhatikan bahwa pertanyaan tidak menyebutkan Windows dan jawaban ini hanya untuk Windows.
Johann Gerell
CP_ACPtentu saja argumen yang salah. Tiba-tiba, keadaan lingkungan thread yang mengeksekusi memiliki efek pada perilaku kode. Tidak disarankan. Tentukan penyandian karakter tetap dalam konversi Anda. (Dan pertimbangkan menangani kesalahan.)
IInspectable