C ++ Dizeyi (veya char *) wstring'e (veya wchar_t *) dönüştür


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

S'nin içeriğini ws'ye nasıl atayabilirim?

Google'da arama yaptı ve bazı teknikler kullandı ancak tam içeriği atayamazlar. İçerik bozuk.


7
Bence strings8 bit karakter kabul ediyorum . UTF-8'de zaten kodlanmış mı?
kennytm

3
Sisteminiz "おはよう", sistem tarafından kodlanmış bir dizeyi oluşturacağını kodlayan nedir ?
sbi

MSVC'nin bunu kabul edeceğini ve bazı çokbaytlı kodlama yapacağını düşünüyorum, belki UTF-8.
Potatoswatter

1
@Potatoswatter: MSVC, ANYTHING için varsayılan olarak UTF-8 kullanmaz. Bu karakterleri girerseniz, dosyayı hangi kodlamaya dönüştüreceğini sorar ve varsayılan olarak 1252 kod sayfasına
gider.

2
@Samir: dosyanın kodlaması ne kadar önemli ? Bu dizeyi dosyanın başına taşıyabilir ve o parçanın onaltılı dökümünü gösterebilir misiniz? Muhtemelen bundan ayırt edebiliriz.
Mooing Duck

Yanıtlar:


239

Örneğinizdeki giriş dizesinin (お は よ う) bir UTF-8 kodlu olduğunu varsayarsak (görünüşe göre değil, ancak bu açıklamanın uğruna olduğunu varsayalım :-)) bir Unicode dizesinin temsili için olduğunu varsayalım ilginizi çekerse, sorununuz yalnızca standart kitaplık (C ++ 11 ve daha yeni) ile tamamen çözülebilir.

TL; DR versiyonu:

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

Daha uzun çevrimiçi derlenebilir ve çalıştırılabilir örnek:

(Hepsi aynı örneği gösteriyor. Fazlalık için çok fazla var ...)

Not (eski) :

Yorumlarda belirtildiği ve https://stackoverflow.com/a/17106065/6345 adresinde açıklandığı gibi, UTF-8 ve UTF-16 arasında dönüştürme yapmak için standart kitaplığı kullanırken farklı platformlardaki sonuçlarda beklenmedik farklılıklar gösterebilecek durumlar var . Daha iyi bir dönüşüm std::codecvt_utf8için http://en.cppreference.com/w/cpp/locale/codecvt_utf8 adresinde açıklandığı gibi düşünün .

Not (yeni) :

Yana codecvtbaşlık C ++ 17 önerilmiyor, bu cevabında sunulan çözümü hakkında bazı endişe dile getirildi. Ancak, C ++ standartları komitesi önemli bir açıklama eklendi http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html diyerek

bu kütüphane bileşeni, uygun bir değiştirme standart hale gelinceye kadar yan boyunca Ek D'ye bırakılmalıdır.

Öngörülebilir gelecekte, codecvtbu cevabın çözümü güvenli ve taşınabilir.


2
VS dosyalarını hangi kodlamayla kaydettiğinizi kontrol edin
Johann Gerell

9
Bunun sadece C ++ 11 olduğunu unutmayın!
bk138

1
MinGW'de (gcc / g ++ 4.8.1 ve -std = c ++ 11) codecvt üstbilgisi yoktur. Bir alternatif var mı?
Brian Jack

1
std::codecvt_utf8Yeni başlayanlar için bir örnek
verebilir misiniz

15
<codecvt>C ++ 17'den beri kullanımdan kaldırıldığını lütfen unutmayın .
tambre

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

    ws = wsTmp;

    return 0;
}

93
Bu, yalnızca tüm karakterler tek bayt, yani ASCII veya ISO-8859-1 ise işe yarar . UTF-8 dahil olmak üzere çok baytlık herhangi bir şey sefil bir şekilde başarısız olacaktır. Soru açıkça çok baytlı karakterler içeriyor.
Mark Ransom

28
Bu cevap açıkça yetersiz ve dar karakterleri geniş karakterlere kopyalamaktan başka bir şey yapmıyor. Çok baytlı veya utf8 kodlu bir dizeden utf16 wstring'e nasıl düzgün bir şekilde geçileceği için diğer cevaplara, özellikle Johann Gerell tarafından verilen yanıtlara bakın.
DLRdave

10
bu cevap tehlikelidir ve muhtemelen ascii olmayan sistemde kırılacaktır. yani bir arapça dosya adı bu kesmekle karışacak.
Stephen

9
Bu cevap, sorunun kaynağının nüansını göz ardı ederseniz ve beni Google'dan buraya getiren soru başlığına odaklanırsanız faydalıdır. Olduğu gibi, sorunun başlığı son derece yanıltıcıdır ve sorulan gerçek soruyu yansıtacak şekilde değiştirilmelidir
Anne Quinn

3
Bu yalnızca 7 bit ASCII karakterler için geçerlidir. Latin1 için, yalnızca char imzasız olarak yapılandırılmışsa çalışır. Char türü imzalanırsa (çoğu zaman büyük harftir),> 127 karakterleri yanlış sonuçlar verir.
huyc

32

Sorunuz yetersiz. Kesinlikle, bu örnek bir sözdizimi hatasıdır. Ancak, std::mbstowcsmuhtemelen aradığınız şey budur.

Bu bir C-kütüphane fonksiyonudur ve tamponlar üzerinde çalışır, ancak TBohne (eski adıyla Mooing Duck) sayesinde kullanımı kolay bir deyim:

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 = yeni wchar_t [s.size ()]; size_t num_chars = mbstowcs (buf, s.c_str (), s.size ()); wstring ws (buf, num_chars); // ws = bozuk
Samir

1
@Samir: Çalışma zamanı kodlamasının derleme zamanı kodlaması ile aynı olduğundan emin olmalısınız. setlocaleDerleyici bayraklarını ayarlamanız veya ayarlamanız gerekebilir . Bilmiyorum çünkü Windows'u kullanmıyorum, ama bu yüzden ortak bir özellik değil. Mümkünse diğer cevabı düşünün.
Potatoswatter

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

2
@WaffleSouffle Bu güncel değil. 2011'den beri bitişik uygulamalar gerekiyor ve uygulamalar bu tür hilelerden çok önce ayrılıyor.
Potatoswatter

1
mingw gibi bazı ortamlar hala codecvt başlığına sahip değildir, bu yüzden daha önce 'daha iyi' çözümlerden bazıları işe yaramaz, yani bu sorunun mingw'de Aralık 2014'ten beri bile iyi çözümleri yoktur
Brian Jack

18

Birinin ihtiyaç duyması durumunda yalnızca Windows API, C ++ 11 öncesi uygulaması:

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

Optimize edebilirsiniz. A kullanarak dizenin iki kopyasını almanıza gerek yoktur vector. Basitçe yaparak karakter dizisini ayırın wstring strW(charsNeeded + 1);ve dönüşüm için tampon olarak kullanın &strW[0]. Son olarak dönüşümden sonra son null değerinin mevcut olduğundan emin olunstrW[charsNeeded] = 0;
c00000fd

1
@ c00000fd, bildiğim kadarıyla std :: basic_string iç arabelleğinin yalnızca C ++ 11 standardından beri sürekli olması gerekiyor. Kodum, yazının üstünde belirtildiği gibi C ++ 11 öncesi. Bu nedenle, & strW [0] kodu standart uyumlu değildir ve çalışma zamanında yasal olarak kilitlenebilir.
Alex Che

13

Eğer kullanıyorsanız , Windows / Visual Studio kullanmak olabilir wstring bir dize dönüştürmek için ve ihtiyacı:

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

Wstring'i dizeye dönüştürmek için aynı prosedür (bazen bir kod sayfası belirtmeniz gerekir ):

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

Bir kod sayfası ve hatta UTF8 ( JNI / Java ile çalışırken oldukça güzel) belirtebilirsiniz . Bir std :: wstring'i utf8 std :: string'e dönüştürmenin standart bir yolu bu cevapta gösterilmiştir .

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

Kod sayfaları hakkında daha fazla bilgi edinmek istiyorsanız , Yazılım üzerinde Joel hakkında ilginç bir makale var: Mutlak Minimum Her Yazılım Geliştiricisi Kesinlikle, Olumlu Unicode ve Karakter Setleri Hakkında Bilmelisiniz .

Bu CA2W (Ansi'yi Geniş = Unicode'a Dönüştür) makroları, ATL ve MFC Dize Dönüşüm Makrolarının bir parçasıdır , örnekler dahil.

Bazen # 4995 güvenlik uyarısını devre dışı bırakmanız gerekir, diğer geçici çözümü bilmiyorum (bana VSX1212'de WindowsXp için derlediğimde olur).

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

Edit: Eh, bu makaleye göre Joel tarafından makale gibi görünüyor: "eğlenceli iken, gerçek teknik detaylar oldukça hafif". HABER: Her Programcı'nın Metinle Çalışmak için Kodlama ve Karakter Kümeleri Hakkında Kesinlikle, Olumlu Olarak Bilmesi Gerekenler .


Üzgünüm, ana dili İngilizce olan biri değilim. Lütfen uygun gördüğünüz şekilde düzenleyin.
lmiguelmh

Downvoter ile neler oluyor? Cevapta yanlış olan ne?
lmiguelmh

Muhtemelen taşınabilir olmayan kodu teşvik ettiği gerçeği.
Pavel Minaev

Evet, bu yüzden bunun sadece Windows / Visual Studio'da çalıştığını belirttim. Ama en azından bu çözüm doğru, ve bu değil:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
lmiguelmh

Ek not: CA2W, ATL'nin ad alanı altındadır. (ATL :: CA2W)
Val

12

İşte birleştirerek için bir yoldur string, wstringve karışık dize sabitleri wstring. wstringstreamSınıfı kullanın .

Bu, çok baytlı karakter kodlamaları için ÇALIŞMAZ. Bu, tür güvenliğini atmanın ve std :: string'den 7 bit karakterleri std: wstring'in her karakterinin alt 7 bitine genişletmenin aptalca bir yoludur. Bu, yalnızca 7 bit ASCII dizeleriniz varsa ve geniş dizeler gerektiren bir API'yi çağırmanız gerektiğinde kullanışlıdır.

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

Cevap ilginç görünüyor. Biraz açıklayabilir misiniz: bu çok baytlı kodlamalar için işe yarar mı ve neden / nasıl?
wh1t3cat1k

kodlama şemaları depolama sınıfına diktir. string1 baytlık karakterleri ve wstring2 baytlık karakterleri saklar. utf8 gibi bir şey, mulitbayt karakterleri 1 bayt değerinde bir dizi olarak depolar, yani a string. dize sınıfları kodlamaya yardımcı olmaz. Ben c ++ sınıfları kodlama konusunda uzman değilim.
Mark Lakata

2
Bunun ne kadar kısa ve basit olduğu göz önüne alındığında, bunun en iyi cevap olmaması için herhangi bir neden var mı? Kapsadığı herhangi bir durum var mı?
Ryuu

@ MarkLakata, ilk yoruma cevabını okudum ama hala emin değilim. Çok baytlık karakterler için çalışır mı? Başka bir deyişle, bu cevapla aynı tuzağa eğilimli değil mi?
Marc.2377

@ Marc.2377 Bu, çok baytlı karakter kodlamaları için ÇALIŞMAZ. Bu, tip güvenliği atmanın ve 7 bit karakterleri std::stringher karakterin alt 7 bitine genişletmenin sadece aptalca bir yoludur std:wstring. Bu, yalnızca 7 bit ASCII dizeleriniz varsa ve geniş dizeler gerektiren bir API'yi çağırmanız gerektiğinde kullanışlıdır. Daha sofistike bir şeye ihtiyacınız varsa stackoverflow.com/a/8969776/3258851 adresine bakın .
Mark Lakata

11

Gönderen char*için wstring:

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

Gönderen stringiçin wstring:

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

Bunun yalnızca dönüştürülmekte olan dize yalnızca ASCII karakterleri içeriyorsa işe yaradığını unutmayın.


7
Çünkü bu sadece kodlama, söz konusu harfleri tutamayan Windows-1252 ise işe yarar.
Mooing Duck

3
ASCII ile uğraştığınızı bildiğinizde, bu en az hataya meyilli bir yöntemdir. Bu, uygulamaları daha yeni api'lere taşırken önemli bir kullanıcı tabanıdır.
Sid Sarasvati

Bu yol değil . Visual Studio kullanıyorsanız kullanmalısınız atlconv.h. Diğer cevapları kontrol edin.
lmiguelmh

7

Boost.Locale kullanarak:

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

5

Bu varyantı gerçek hayatta benim favorim. Geçerli UTF-8 ise , girişi ilgili girişe dönüştürür wstring. Giriş bozuksa, wstringtek bayttan oluşturulur. Giriş verilerinizin kalitesinden gerçekten emin olamıyorsanız bu son derece yararlıdır.

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
Cevabınızı temel alarak bu soruyu yeni başlattım stackoverflow.com/questions/49669048/… nazikçe bir göz
atabilir miyim

2

QT'niz varsa ve bir işlev ve malzeme uygulamak için tembelseniz kullanabilirsiniz

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


Neredeyse, ama sadece bir ile başlamalısınız QString, çünkü QStringkurucu bir nedenle bir dizeyi kabul edemez.
bobsbeenjamin


Bu güzel. Ayrıca, QString öğesinin yapıcıdaki dizenizi kabul etmesine izin vermek için .c_str () öğesini kullanabilirsiniz .
miep

1

s2ws yöntemi iyi çalışıyor. Umut yardımcı olur.

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
Tüm bu cevapların dinamik belleği güvensiz bir şekilde tahsis etmesi ve ardından arabellekten dizeye veri kopyalaması nedir? Neden kimse güvensiz aracılardan kurtulamıyor?
Mooing Duck

hahakubile, lütfen ws2s için benzer bir şey ile yardımcı olabilir misiniz?
cristian

1

Kendi testime dayanarak (Windows 8, vs2010'da) mbstowcs aslında orijinal dizeye zarar verebilir, sadece ANSI kod sayfası ile çalışır. MultiByteToWideChar / WideCharToMultiByte de dize bozulmasına neden olabilir - ancak bilmedikleri karakterleri '?' soru işaretleri, ancak mbstowcs bilinmeyen bir karakterle karşılaştığında durma eğilimindedir ve o noktada dize keser. (Fin pencerelerinde Vietnamca karakterleri test ettim).

Bu yüzden analog ansi C işlevleri yerine Multi * -windows api işlevini tercih edin.

Ayrıca bir kod sayfasından diğerine dize kodlamak için en kısa yol fark ne MultiByteToWideChar / WideCharToMultiByte api işlev çağrıları ama analog ATL makroları kullanmak değildir: W2A / A2W.

Yani yukarıda belirtildiği gibi analog fonksiyon şöyle görünür:

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

_acp, USES_CONVERSION makrosunda bildirildi.

Veya eski veri dönüşümünü yenisine dönüştürürken sıklıkla özlediğim işlev:

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

   _acp = CP_UTF8;
   return W2A( pw );
}

Ancak, bu makronun çok fazla yığın kullandığına dikkat edin - W2A veya A2W makrosunu kullandıktan sonra döngüler veya özyinelemeli döngüler için kullanmayın - ASAP'ye dönmek için daha iyi, bu nedenle yığın geçici dönüşümden kurtulur.


1

Wstring dizesi

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 için Dize

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
Bu Str2Wstr 0 sonlandırmayla ilgili bir sorun var. Üretilen wstring'leri artık "+" (wstring s3 = s1 + s2'deki gibi) ile birleştirmek mümkün değildir. Yakında bu sorunu çözecek bir cevap göndereceğim. Önce bellek sızıntıları için bazı testler yapmak zorundasınız.
thewhiteambit

-2

string s = "おはよう"; bir hatadır.

Doğrudan wstring kullanmalısınız:

wstring ws = L"おはよう";

1
Bu da işe yaramayacak. BMP olmayan karakterleri C kaçış dizilerine dönüştürmeniz gerekir.
Dave Van den Eynde

3
@Dave: Derleyiciniz kaynak dosyalarda unicode'u destekliyorsa ve son on yılda tümleri (görsel stüdyo, gcc, ...) destekliyorsa çalışır
Thomas Bonini

Merhaba, varsayılan sistem kodlaması ne olursa olsun (örneğin benim varsayılan sistem kodlama olarak Arapça olabilir), L "お は よ う" için kaynak kod dosyasının kodlaması ne olmalıdır? UTF-16 olmalı veya .cpp dosya kodlaması için BOM olmadan UTF-8 alabilir miyim?
Afriza N.Arief

2
@afriza: Derlemeniz desteklediği sürece gerçekten önemli değil
Thomas Bonini

2
Bu bir hata değildir; "dar" bir dizedeki genişletilmiş karakterler, çok baytlı dizilere eşlenecek şekilde tanımlanır. Derleyici, işletim sisteminin yaptığı sürece desteklemelidir, bu da en az sorabilmenizdir.
Potatoswatter

-2

dizenizi wstring'e dönüştürmek için bu kodu kullanın

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
Sorunun Windows'tan bahsetmediğini ve bu yanıtın yalnızca Windows olduğunu unutmayın.
Johann Gerell

CP_ACPkesinlikle yanlış bir argüman. Aniden, yürütme iş parçacığının ortam durumu kodun davranışı üzerinde bir etkisi vardır. Tavsiye edilmez. Dönüşümünüzde sabit bir karakter kodlaması belirtin. (Ve hataları ele
almayı
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.