std :: wstring VS std :: dize


741

Ben arasındaki farkı anlamak mümkün değilim std::stringve std::wstring. wstringUnicode karakterler gibi geniş karakterleri desteklediğini biliyorum . Aşağıdaki sorularım var:

  1. Ne zaman kullanmalıyım std::wstringüzerinde std::string?
  2. Yapabilmek std::string özel karakterler dahil tüm ASCII karakter kümesi, tutun?
  3. Dır-dir std::wstring tüm popüler C ++ derleyici tarafından desteklenen?
  4. Tam olarak " geniş karakter " nedir?

10
ASCII karakter seti çok sayıda "özel" karaktere sahip değil, en egzotik muhtemelen `` (arka tırnak). std :: string tüm Unicode karakterlerin yaklaşık%
0.025'ini tutabilir

3
Geniş karakterler ve hangi türün kullanılacağı hakkında iyi bilgiler burada bulunabilir: programmers.stackexchange.com/questions/102205/…
Yariv

14
Ve 2012'de olduğumuzdan beri utf8everywhere.org yazılmıştır. C ++ / Windows ile haklar ve yanlışlar hakkındaki tüm soruları hemen hemen cevaplıyor.
Pavel Radzivilovsky

42
@MSalters: std :: string, CHAR_BIT 8 olsa bile tüm Unicode karakterlerin% 100'ünü tutabilir. Sistem düzeyinde UTF-8 olabilecek std :: string kodlamasına bağlıdır (pencereler hariç hemen hemen her yerde olduğu gibi) ) veya uygulama düzeyinizde. Yerel dar kodlama Unicode'u desteklemiyor mu? Sorun değil, sadece kullanmayın, bunun yerine UTF-8 kullanın.
Yakov Galka

8
Bu konuda harika okuma: utf8everywhere.org
Timothy Shields

Yanıtlar:


992

string? wstring?

std::stringBir olan basic_stringbir üzerine şablonu charve std::wstringbir üzerindewchar_t .

char vs. wchar_t

chargenellikle 8 bitlik bir karakter içermelidir.
wchar_tgeniş bir karaktere sahip olması gerekiyordu ve sonra işler zorlaşıyor:
Linux'ta,wchar_t 4 bayt, Windows'ta ise 2 bayt.

Unicode ne olacak? ?

Sorun şu ki, ne ne charnewchar_t unicode'a doğrudan bağlı olmasıdır.

Linux'ta mı?

Bir Linux işletim sistemi alalım: Ubuntu sistemim zaten unicode farkında. Bir karakter dizisi ile çalıştığımda, yerel olarak UTF-8 (yani karakterlerin Unicode dizesi) olarak kodlanır . Aşağıdaki kod:

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;


   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(ordinals)  :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(text[i])
                          );
   }

   std::cout << std::endl << std::endl ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(ordinals) :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

aşağıdaki metni çıktılar:

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(ordinals)  : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(ordinals) : 111 108 233

"Olé" metninin chargerçekten dört karakterden oluştuğunu göreceksiniz : 110, 108, 195 ve 169 (sondaki sıfır sayılmıyor ). (Çalışmanıza izin vereceğimwchar_t Kodu bir alıştırma olarak )

Bu nedenle, charLinux'ta bir ile çalışırken , genellikle bilmeden bile Unicode'u kullanmalısınız. Ve std::stringile işler char, bu nedenle std::stringzaten unicode-hazırdır.

Bunu not et std::string C dize API gibi, "olé" dizesini ele alacağız, 4 karakter değil, üç olması. Bu nedenle, unicode karakterlerle keserken / oynatırken dikkatli olmalısınız çünkü UTF-8'de bazı karakter kombinasyonları yasaktır.

Windows üzerinde mi?

Windows'da bu biraz farklı. Win32 char, farklı karakter kümeleri / kod sayfalarıyla ve üzerinde çalışan birçok uygulamayı desteklemek zorundaydı , Unicode'un ortaya çıkmasından önce tüm dünyada üretilen .

Bu yüzden onların çözümü ilginçti: Bir uygulama ile çalışırsa char, karakter dizileri makinedeki yerel karakter / kod sayfasını kullanarak GUI etiketlerinde kodlanır / yazdırılır / gösterilir. Örneğin, "olé" Fransızca yerelleştirilmiş bir Windows'ta "olé" olur, ancak Kiril yerelleştirilmiş bir Windows'da farklı bir şey olur ( Windows-1251 kullanıyorsanız "olй" ). Bu nedenle, "tarihsel uygulamalar" genellikle aynı şekilde çalışır.

Unicode tabanlı uygulamalar için, Windows wchar_t2 bayt genişliğinde ve UTF-16'da kodlanmış olan kullanır. 2 baytlık karakterlerde (veya en azından en çok uyumlu UCS-2) kodlanan Unicode olan kodlanan kullanır. aynı şey IIRC).

Kullanarak uygulamalar charsöylenir "baytlı" (her glif bir veya birden fazla oluşur, çünkü charkullanan uygulamalar ise, ler) wchar_tsöylenir "widechar" (her glif bir ya da iki oluşur çünkü wchar_t. Bkz MultiByteToWideChar ve WideCharToMultiByte fazla bilgi için Win32 dönüşüm API.

Bu nedenle, Windows üzerinde çalışıyorsanız, kötü bir şekilde kullanmak istersinizwchar_t ( GTK + veya QT gibi bunu gizleyen bir çerçeve kullanmazsanız ). Gerçek şu ki, perde arkasında, Windows wchar_tdizelerle çalışır , bu nedenle tarihsel uygulamalar bile API gibi kullanılırken chardizelerini dönüştürür (Win32 GUI'de etiketi ayarlamak için düşük düzey API işlevi).wchar_tSetWindowText()

Bellek sorunları?

UTF-32, karakter başına 4 bayttır, bu nedenle yalnızca UTF-8 metni ve UTF-16 metni UTF-32 metninden her zaman daha az veya aynı miktarda bellek kullanırsa (ve genellikle daha az ).

Bellek sorunu varsa, çoğu batı dilinden daha fazla bilmeniz gerekir, UTF-8 metni aynı UTF-16'dan daha az bellek kullanır.

Yine de, diğer diller (çince, japonca, vb.) İçin, kullanılan bellek UTF-8 için UTF-8 ile aynı veya biraz daha büyük olacaktır.

Sonuç olarak, UTF-16 çoğunlukla karakter başına 2 ve nadiren 4 bayt kullanacaktır (bir çeşit ezoterik dil glifiyle (Klingon? Elf?) İlgilenmiyorsanız, UTF-8 1 ila 4 bayt harcayacaktır.

Daha fazla bilgi için http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 adresine bakın .

Sonuç

  1. Ne zaman std :: wstring over std :: string kullanmalıyım?

    Linux'ta mı? Neredeyse hiç (§).
    Windows üzerinde mi? Neredeyse her zaman (§).
    Platformlar arası kodda mı? Araç setinize bağlıdır ...

    (§): aksini belirten bir araç takımı / çerçeve kullanmıyorsanız

  2. Can std::stringtüm özel karakterler dahil ASCII karakter kümesi tutun?

    Uyarı: A std::string, bir 'ikili' tamponu tutmak için uygundur;std::wstring !

    Linux'ta mı? Evet.
    Windows üzerinde mi? Yalnızca Windows kullanıcısının geçerli yerel ayarı için özel karakterler kullanılabilir.

    Düzenle ( Johann Gerell'den bir yorumdan sonra ):
    a std::string, tüm chartabanlı dizeleri işlemek için yeterli olacaktır (her charbiri 0 ile 255 arasında bir sayıdır). Fakat:

    1. ASCII'nin 0'dan 127'ye çıkması beklenir. Yüksek chars ASCII DEĞİLDİR.
    2. char0 ile 127 arasındaki a doğru şekilde tutulur
    3. Bir char128 dan 255 Şifrelemenizden (unicode olmayan unicode, vs.) bağlı bir anlama sahiptir, ancak bunlar UTF-8 olarak kodlanmıştır olduğunca boyu Unicode gliflere tutmak mümkün olacak.
  3. Is std::wstringneredeyse tüm popüler C ++ derleyici tarafından desteklenen?

    Çoğunlukla, Windows'a taşınan GCC tabanlı derleyiciler hariç.
    (Linux altında) benim g ++ 4.3.2 üzerinde çalışır ve Visual C ++ 6 beri Win32 üzerinde Unicode API kullandım.

  4. Tam olarak geniş bir karakter nedir?

    C / C ++ 'da, wchar_tbasit charkarakter türünden daha büyük yazılmış bir karakter türüdür. Endeksleri (Unicode glifleri gibi) 255'ten büyük (veya bağlı olarak 127 ...) olan karakterlerin içine koymak için kullanılır.


4
@gnud: Belki de wchar_t'ın UTF-16'nın ortaya çıkmasından önce tüm UCS-2 karakterlerini (çoğu UTF-16 karakter) işlemek için yeterli olması gerekiyordu ... Veya belki de Microsoft'un Unicode'a kolay erişim vermek gibi POSIX'ten başka öncelikleri vardı Win32 üzerinde char kodlu kullanımını değiştirmeden.
Paercebal

4
@Sorin Sbarnea: UTF-8 1-6 bayt alabilir, ancak görünüşe göre standart 1-4 ile sınırlar. Daha fazla bilgi için en.wikipedia.org/wiki/UTF8#Tanım bölümüne bakın .
paercebal

8
Bu örnekler Linux ve Windows üzerinde farklı sonuçlar üretse de, C ++ programı olèUTF-8 olarak kodlanıp kodlanmadığı konusunda uygulama tarafından tanımlanan davranışlar içerir . Dahası, sen olamaz nedeni doğal akışı wchar_t *için std::couttürleri bir kötü şekillendirilmiş programda çıkan uyumsuz ve kodlamaların kullanımı ile ilgisi yoktur çünkü. Özellikle kodunuzun taşınabilir olmasını istiyorsanız, platformdan ziyade kendi kodlama tercihinizi kullanıp kullanmadığınıza std::stringveya belirtmenize std::wstringbağlı olduğunuzu belirtmek gerekir.
John Leidegren

14
Windows aslında UTF-16 kullanıyor ve bir süredir var, Windows'un eski sürümleri UCS-2 kullandı, ancak artık böyle değil. Burada tek sorunum std::wstring, yanlış olduğunu düşündüğüm Unicode Windows API için daha uygun olduğu için Windows'ta kullanılması gereken sonuç . Tek endişeniz Unicode Windows API'sını çağırıyor ve dizeleri sıralamıyorsa, o zaman eminim ama bunu genel durum olarak almıyorum.
John Leidegren

15
@ John Leidegren:: If your only concern was calling into the Unicode Windows API and not marshalling strings then sureO zaman katılıyoruz. JavaScript değil, C ++ kodlama. Derleme zamanında gerçekleştirilebildiğinde, çalışma zamanında işe yaramaz marshalling veya diğer potansiyel olarak maliyetli işlemlerden kaçınmak, o dilin kalbidir. WinAPI ve kodlama karşı kodlama std::stringsadece haksız bir israf çalışma zamanı kaynaklarıdır. Onu yanıltıcı buluyorsunuz ve bu sizin bakış açınız olduğu için sorun değil. Benimkisi, Linux tarafında daha iyi göründüğü için Windows'ta kötümser kod yazmam.
paercebal

71

std::wstringArayüz tarafından istenmesi dışında veya Windows API çağrılarının ve ilgili kodlama dönüşümlerinin sözdizimsel şeker olarak kullanılması dışında, Windows'da veya başka yerlerde kaçınmanızı öneririm .

Benim görüşüm, ortak yazarı olduğum http://utf8everywhere.org ' da özetlenmiştir .

Uygulamanız API çağrısı merkezli değilse, örneğin esas olarak UI uygulaması değilse, öneri Unicode dizelerini std :: string'de saklamak ve UTF-8'de kodlanmış olarak API çağrılarının yakınında dönüşüm gerçekleştirmektir. Makalede özetlenen faydalar, özellikle karmaşık uygulamalarda, dönüşümün belirgin sıkıntısına ağır basmaktadır. Bu, çoklu platform ve kütüphane geliştirme için iki katına çıkar.

Ve şimdi, sorularınızı cevaplayın:

  1. Birkaç zayıf neden. Widechar'ların Unicode'u desteklemenin doğru yolu olduğuna inanılan tarihsel nedenlerden dolayı vardır. Artık UTF-16 dizelerini tercih eden API'leri arayüzlemek için kullanılıyor. Bunları yalnızca bu tür API çağrılarının doğrudan yakınında kullanıyorum.
  2. Bunun std :: string ile ilgisi yoktur. İçine koyduğunuz kodlamayı tutabilir. Tek soru nasıl Sen içeriğini davranın. Benim tavsiyem UTF-8, bu yüzden tüm Unicode karakterleri doğru bir şekilde tutabilecektir. Linux'ta yaygın bir uygulamadır, ancak bence Windows programları da bunu yapmalıdır.
  3. Hayır.
  4. Geniş karakter kafa karıştırıcı bir isim. Unicode'un ilk günlerinde, bir karakterin iki baytta, dolayısıyla adı kodlanabileceğine dair bir inanç vardı. Bugün "karakterin iki bayt uzunluğundaki herhangi bir bölümünü" temsil ediyor. UTF-16, bu bayt çiftlerinin (Geniş karakterler olarak da bilinir) bir dizisi olarak görülür. UTF-16'daki bir karakter bir veya iki çift alır.

37

Yani, şimdi buradaki her okuyucu gerçekler, durum hakkında net bir anlayışa sahip olmalıdır. Değilse, paercebal'ın olağanüstü kapsamlı cevabını okumalısınız [btw: teşekkürler!].

Benim pragmatik sonucum şok edici derecede basit: C ++ (ve STL) "karakter kodlama" şeyleri büyük ölçüde kırık ve işe yaramaz. Microsoft'a suçlayın ya da değil, bu yine de yardımcı olmaz.

Çözümüm, derinlemesine araştırmadan sonra, çok fazla hayal kırıklığı ve sonuçta ortaya çıkan deneyimler:

  1. kodlama ve dönüştürme işlemlerinden kendiniz sorumlu olmanız gerektiğini kabul edin (ve çoğunun oldukça önemsiz olduğunu göreceksiniz)

  2. UTF-8 kodlu dizeler için std :: string kullanın (sadece a typedef std::string UTF8String)

  3. böyle bir UTF8String nesnesinin sadece aptal, ama ucuz bir kap olduğunu kabul edin. Asla içindeki karakterlere asla doğrudan erişmeyin ve / veya bunları değiştirmeyin (arama, değiştirme vb.). Yapabilirsin, ama gerçekten sadece, gerçekten çok baytlık dizeler için metin manipülasyon algoritmaları yazarak zaman kaybetmek istemiyorsun! Diğer insanlar zaten bu kadar aptalca şeyler yapmış olsalar bile, bunu yapma! Varsın olsun! (Pekala, bunun mantıklı olduğu senaryolar var ... sadece bunlar için yoğun bakım kütüphanesini kullanın).

  4. UCS-2 kodlu dizeler için std :: wstring kullanın ( typedef std::wstring UCS2String) - bu bir uzlaşma ve WIN32 API'nin getirdiği karmaşadan ödün verilmesidir). UCS-2 çoğumuz için yeterlidir (daha fazlası ...).

  5. karakter karakter erişim gerektiğinde (okuma, değiştirme vb.) UCS2String örneklerini kullanın. Herhangi bir karakter tabanlı işlem NON-multibyte gösterimi ile yapılmalıdır. Basit, hızlı, kolaydır.

  6. UTF-8 ve UCS-2 arasında ileri geri dönüştürme için iki yardımcı program işlevi ekleyin:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );

Dönüşümler basit, google burada yardımcı olmalı ...

Bu kadar. Belleğin değerli olduğu yerlerde ve tüm UTF-8 I / O için UTF8String kullanın. Dizenin ayrıştırılması ve / veya işlenmesi gereken her yerde UCS2String kullanın. Bu iki gösterim arasında istediğiniz zaman dönüştürebilirsiniz.

Alternatifler ve Geliştirmeler

  • tek baytlık karakter kodlamalarından (örn. ISO-8859-1) const wchar_t tt_iso88951[256] = {0,1,2,...};dönüşümler , düz çeviri tabloları, örneğin UCS2'ye ve UCS2'den dönüşüm için uygun kod yardımı ile gerçekleştirilebilir .

  • UCS-2 yeterli değilse, UCS-4'e geçmek yerine ( typedef std::basic_string<uint32_t> UCS2String)

YBÜ veya diğer unicode kütüphaneleri?

Gelişmiş şeyler için.


Dang, yerel Unicode desteğinin orada olmadığını bilmek iyi değil.
Mihai Danila

@Frunsi, Glib :: ustring'i denediğinizi ve merak ediyorsanız, düşünceleriniz neler?
Caroline Beltran

@CarolineBeltran: Glib'i tanıyorum, ama hiç kullanmadım ve muhtemelen asla kullanmam, çünkü oldukça spesifik olmayan bir hedef platformla (unixoid sistemler ...) sınırlıdır. Windows bağlantı noktası harici win2unix katmanına dayanıyor ve IMHO hiç OSX uyumluluk katmanı yok. Tüm bu şeyler en azından kodum için açıkça yanlış bir yöne yönlendiriyor (bu kemer seviyesinde ...) ;-) Yani, Glib bir seçenek değil
Frunsi

9
Arama, değiştirme ve benzeri UTF-8 dizeleri üzerinde iyi çalışır (bir karakteri temsil eden bayt dizisinin bir kısmı asla başka bir karakter olarak yanlış yorumlanamaz). Aslında, UTF-16 ve UTF-32 bunu hiç kolaylaştırmaz: Üç kodlama da pratikte çok baytlı kodlamalardır, çünkü kullanıcı tarafından algılanan bir karakter (grafik kümesi) herhangi bir sayıda unicode kod noktası uzunluğunda olabilir! Pragmatik çözüm, her şey için UTF-8 kullanmak ve sadece Windows API ile uğraşırken UTF-16'ya dönüştürmektir.
Daniel

5
@Frunsi: Arama ve değiştirme, UTF-8 ile olduğu kadar UTF-8 ile de iyi çalışır. Tam olarak Unicode bilinçli metin işlemenin çok kodlu 'karakterlerle' ilgilenmesi gerektiği için, UTF-8 gibi değişken uzunluklu bir kodlama kullanmak dize işlemeyi daha karmaşık hale getirmez. Bu yüzden her yerde UTF-8 kullanın. Normal C string işlevleri UTF-8'de iyi çalışır (ve Unicode dizesindeki sıralı karşılaştırmalara karşılık gelir) ve daha fazla dil farkında bir şeye ihtiyacınız varsa, yine de bir Unicode kütüphanesine çağırmanız gerekir, UTF-16/32 seni bundan kurtaramaz.
Daniel

25
  1. Dizenizde geniş karakterler depolamak istediğinizde. wideuygulamaya bağlıdır. Visual C ++, doğru hatırlıyorsam varsayılan olarak 16 bit, GCC varsayılan olarak hedefe bağlı olarak. Burada 32 bit uzunluğunda. Lütfen wchar_t (geniş karakter türü) unicode ile bir ilgisi olmadığını unutmayın. Sadece uygulamanın yerelleri tarafından desteklediği en büyük karakter kümesinin tüm üyelerini ve en azından char kadar saklayabileceği garanti edilir. Kodlamayı kullanarak unicode dizelerini de saklayabilirsiniz . Ancak unicode kod noktalarının anlamını anlamaz. Yanistd::stringutf-8str.size() sizin dizede size mantıksal karakter miktarını vermeyecektir, ancak char veya bu dize / wstring saklanan wchar_t'den elemanların sadece miktarı. Bu nedenle, gtk / glib C ++ sarmalayıcı millet Glib::ustringutf-8'i işleyebilen bir sınıf geliştirdi .

    Eğer sizin wchar_t'den uzun 32 bit, daha sonra kullanabileceğiniz utf-32bir unicode kodlama olarak ve saklayabilir ve sabit (utf-32 sabittir uzunluğu) kodlama kullanarak kolu unicode dizeleri. Bu, wstring s.size()işlevinizin daha sonra doğru miktarda wchar_t öğesi ve mantıksal karakter döndüreceği anlamına gelir .

  2. Evet, char her zaman en az 8 bit uzunluğundadır, yani tüm ASCII değerlerini saklayabilir.
  3. Evet, tüm büyük derleyiciler bunu destekliyor.

# 2'yi merak ediyorum. 7 bitin teknik olarak da geçerli olacağını düşündüm. Yoksa 7 bitlik ASCII karakterlerinden daha fazlasını saklamak gerekiyor mu?
jalf

1
evet, jalf. c89, limit.h (imzasız karakter, 0..255 dakikadır) dokümantasyonunda temel türler için minimum aralıklar ve tamsayı türleri için saf bir ikili sistem belirtir. char, işaretsiz char ve işaretli char 8 bit uzunluğuna sahiptir. c ++ bu kuralları devralır.
Johannes Schaub - litb

15
"Bu, wstring'in s.size () işlevinin doğru miktarda wchar_t öğesi ve mantıksal karakter döndüreceği anlamına gelir." Bu, Unicode için bile tamamen doğru değildir. Kod noktasını söylemek "mantıksal karakter" den daha doğrudur, UTF-32'de bile belirli bir karakter birden fazla kod noktasından oluşabilir.
Logan Capaldo

Aslında siz C ++ 'nın Unicode karakter kümesi için yerel desteğe sahip olmadığını mı söylüyorsunuz?
Mihai Danila

1
"Ama unicode kod noktalarının anlamını anlamayacak." Pencerelerde de öyle std::wstring.
Tekilleştirici

5

Sık sık herhangi bir sorun olmadan utf-8 karakterleri tutmak için std :: string kullanın. Yürekten bunu yerel dize türü olarak utf-8 kullanan API'lerle arabirim yaparken yapmanızı öneririz.

Örneğin, kodumu Tcl yorumlayıcısıyla arayüzlerken utf-8 kullanıyorum.

Büyük uyarı std :: string'in uzunluğudur, artık dizedeki karakter sayısı değildir.


1
Juan: Yani std :: string tüm unicode karakterleri tutabilir, ancak uzunluk yanlış rapor verecek mi? Yanlış uzunluk bildirmesinin bir nedeni var mı?

3
Utf-8 kodlamasını kullanırken, tek bir unicode karakter birden çok bayttan oluşabilir. Bu nedenle, standart ascii setinden çoğunlukla karakterler kullanılırken utf-8 kodlaması daha küçüktür. Unicode karakter sayısını ölçmek için özel işlevler kullanmanız (veya kendinizinkini döndürmeniz) gerekir.

2
(Windows'a özgü) Çoğu işlev bayt kullanan bir dizenin ASCII ve 2 bayt olan Unicode, eski sürümler MBCS olmasını bekler. Bu, standart bir windows işlevini çağırmak için 16 bit unicode'a dönüştürmeniz gereken 8 bit unicode saklıyorsanız (yalnızca ASCII bölümünü kullanmıyorsanız) anlamına gelir.
Greg Domjan

2
Bir std :: string sadece uzunluğu yanlış bildirmekle kalmaz, aynı zamanda yanlış dize de verir. Bazı Unicode karakter UTF-8'de birden çok bayt olarak temsil edilirse, std :: string kendi karakterleri olarak düşünürse, tipik olarak std :: string manipülasyon rutinleriniz muhtemelen birinin yanlış yorumlanmasından kaynaklanan birkaç garip karakteri çıkarır doğru karakter.
Mihai Danila

2
Cevabı dizelerin yalnızca bayt konteynerleri olarak düşünülmesi gerektiğini belirtmek için değiştirmeyi öneririm ve baytlar bazı Unicode kodlamasıysa (UTF-8, UTF-16, ...), o zaman anlayan belirli kütüphaneleri kullanmalısınız. söyledi. Standart dize tabanlı API'lerin (uzunluk, altstr, vb.) Hepsi çok baytlı karakterlerle sefil bir şekilde başarısız olur. Bu güncelleme yapılırsa, aşağı oyumu kaldıracağım.
Mihai Danila

4
  1. 'Geniş' (Unicode) karakterleri saklamak istediğinizde.
  2. Evet: 255 tanesi (0 hariç).
  3. Evet.
  4. İşte size tanıtıcı bir makale: http://www.joelonsoftware.com/articles/Unicode.html

11
std :: string 0 iyi kalabilir (sadece c_str () yöntemini çağırırsanız dikkatli olun)
Bay Fooz

3
Ve kesinlikle, bir karakterin 8 bit olduğu garanti edilmez. :) # 4'teki bağlantınız mutlaka okunmalı, ancak soruyu cevapladığını sanmıyorum. Geniş bir karakter kesinlikle unicode ile ilgisi yoktur. Sadece daha geniş bir karakter. (Ne kadar geniş işletim sistemine bağlıdır, ancak genellikle 16 veya 32 bit)
jalf

2
  1. yalnızca ascii değil, Unicode dizelerini kullanmak istediğinizde, uluslararasılaşma için yararlıdır
  2. evet, ama 0 ile iyi oynamıyor
  3. olmayanların farkında değil
  4. geniş karakter, bir unicode karakterin sabit uzunluklu temsilini işlemenin derleyiciye özel yoludur, MSVC için 2 baytlık bir karakterdir, gcc için 4 bayt olduğunu anlıyorum. ve http://www.joelonsoftware.com/articles/Unicode.html için +1

1
2. Bir std :: string bir NULL karakteri gayet iyi tutabilir. Ayrıca utf-8 ve geniş karakterleri de tutabilir.

@Juan: Bu beni tekrar karışıklığa soktu. Std :: string unicode karakterleri tutabilirse, std :: wstring ile özel olan nedir?

1
@Appu: std :: string UTF-8 unicode karakterlerini tutabilir. Farklı karakter genişliklerini hedef alan bir dizi unicode standardı vardır. UTf8 8 bit genişliğindedir. Ayrıca sırasıyla 16 ve 32 bit genişliğinde UTF-16 ve UTF-32 vardır
Greg D

Bir std :: wstring ile. Sabit uzunluklu kodlamalar kullanılırken her unicode karakter bir wchar_t olabilir. Örneğin, Greg'in bağlandığı gibi joel'i yazılım yaklaşımında kullanmayı seçerseniz. Daha sonra, wstring'in uzunluğu tam olarak dizedeki unicode karakterlerin sayısıdır. Ancak daha fazla yer kaplıyor

0 '\ 0' tutamayacağını söylemedim ve ne demek istediğim iyi oynamıyorsa, bazı yöntemler size wstring'in tüm verilerini içeren beklenen bir sonuç vermeyebilir. Aşağı oylarda çok sert.
Greg Domjan

2

Yalnızca 256 farklı karakterden memnun olmayan uygulamalar, ya geniş karakterleri (8 bitten fazla) ya da UTF-8 gibi değişken uzunluklu kodlamayı (C ++ terminolojisinde çok baytlı kodlama) kullanma seçeneklerine sahiptir. Geniş karakterler genellikle değişken uzunluklu kodlamadan daha fazla alan gerektirir, ancak işlenmesi daha hızlıdır. Çok miktarda metin işleyen çok dilli uygulamalar genellikle metni işlerken geniş karakterler kullanır, ancak diske kaydederken UTF-8'e dönüştürür.

A stringve a arasındaki tek fark wstring, sakladıkları karakterlerin veri türüdür. Bir dize char, boyutu en az 8 bit olduğu garanti edilen depoları saklar , böylece ASCII, ISO-8859-15 veya UTF-8 metni gibi dizeleri işlemek için kullanabilirsiniz. Standart, karakter kümesi veya kodlama hakkında hiçbir şey söylemez.

Pratik olarak her derleyici, ilk 128 karakteri ASCII'ye karşılık gelen bir karakter seti kullanır. Bu UTF-8 kodlaması kullanan derleyiciler için de geçerlidir. UTF-8 veya başka bir değişken uzunluklu kodlamada dizeler kullanırken dikkat edilmesi gereken önemli şey, indekslerin ve uzunlukların karakter değil bayt cinsinden ölçülmesidir.

Bir wstring'in veri tipi wchar_t, boyutu standartta tanımlanmamıştır, ancak en azından bir karakter kadar büyük olması gerekir, genellikle 16 bit veya 32 bit. wstring, uygulama tanımlı geniş karakter kodlamasındaki metni işlemek için kullanılabilir. Kodlama standartta tanımlanmadığından, dizeler ve wstrings arasında dönüştürme yapmak kolay değildir. Wstringlerin sabit uzunluklu bir kodlamaya sahip olduğu da kabul edilemez.

Çoklu dil desteğine ihtiyacınız yoksa, yalnızca normal dizeleri kullanmakta iyi olabilirsiniz. Öte yandan, grafik bir uygulama yazıyorsanız, API'nın yalnızca geniş karakterleri desteklemesi genellikle söz konusudur. O zaman muhtemelen metni işlerken aynı geniş karakterleri kullanmak istersiniz. UTF-16'nın değişken uzunlukta bir kodlama olduğunu, yani length()karakter sayısını geri döndüremeyeceğinizi unutmayın. API, UCS-2 gibi sabit uzunlukta bir kodlama kullanıyorsa, işlem kolaylaşır. Geniş karakterler ve UTF-8 arasında dönüştürme yapmak taşınabilir bir yolla yapmak zordur, ancak daha sonra, kullanıcı arayüzü API'niz muhtemelen dönüşümü destekler.


Bu nedenle, ilk paragrafı açıklama: 256 karakterden fazlasına ihtiyaç duyan uygulamanın çok baytlı veya maybe_multibyte kodlaması kullanması gerekir.
Deduplicator

Yine de, UCS-2 ve UCS-4 gibi 16 ve 32 bit kodlamalara çokbaytlı kodlama adı verilmez. C ++ standardı, çok baytlı kodlamalar ve geniş karakterler arasında ayrım yapar. Geniş karakter temsili karakter başına sabit sayı (genellikle 8'den fazla) bit kullanır. En yaygın karakterleri kodlamak için tek bir bayt ve karakter kümesinin geri kalanını kodlamak için birden fazla bayt kullanan kodlamalara çok baytlı kodlama denir.
Seppo Enarvi

Üzgünüm, özensiz yorum. Bahsedilen değişken uzunluklu kodlamaya sahip olmalıdır. UTF-16, tıpkı UTF-8 gibi bir değişken uzunluklu kodlamadır. Değil gibi yapmak kötü bir fikir.
Deduplicator

İyi bir noktaya değindin. Wstring'lerin UTF-16'yı (UCS-2 yerine) depolamak için kullanılamamasının bir nedeni yoktur, ancak daha sonra sabit uzunluklu bir kodlamanın kolaylığı kaybolur.
Seppo Enarvi

2

Güzel bir soru! VERİ KODLAMA (bazen bir CHARSET de dahil), bir dosyaya veri kaydetmek veya bir ağ üzerinden veri aktarmak için bir HAFIZA İFADESİ MEKANİZMASI olduğunu düşünüyorum , bu yüzden bu soruyu şu şekilde cevaplıyorum:

1. Ne zaman std :: wstring over std :: string kullanmalıyım?

Programlama platformu veya API işlevi tek baytlıysa ve bazı Unicode verilerini işlemek veya ayrıştırmak istiyoruz, örneğin Windows'.REG dosyasından veya ağ 2 bayt akışından okuyun, std :: wstring değişkenini kolayca bildirmeliyiz onları işlemek. örneğin: wstring ws = L "中国 a" (6 sekizli bellek: 0x4E2D 0x56FD 0x0061), '中' karakteri almak için ws [0] ve '国' karakteri almak için ws [1] ve 'a' karakteri al vb.

2. std :: string, özel karakterler de dahil olmak üzere tüm ASCII karakter kümesini tutabilir mi?

Evet. Ancak dikkat: Amerikan ASCII, her 0x00 ~ 0xFF sekizlisinin "123abc & * _ &" gibi yazdırılabilir metinler de dahil olmak üzere bir karakteri temsil ettiği anlamına gelir ve özel bir karakter söylediniz, çoğunlukla ''. ' editörleri veya terminalleri karıştırmayın. Ve diğer bazı ülkeler kendi "ASCII" karakter kümesini genişletiyor, örneğin Çince, bir karakteri temsil etmek için 2 oktet kullanıyor.

Std :: wstring tüm popüler C ++ derleyicileri tarafından destekleniyor mu?

Belki ya da çoğunlukla. Ben kullandım: VC ++ 6 ve GCC 3.3, YES

4. Tam olarak "geniş karakter" nedir?

geniş karakter çoğunlukla tüm ülkelerin karakterlerini tutmak için 2 sekizli veya 4 sekizli kullandığını gösterir. 2 sekizli UCS2 temsili bir örnektir ve ayrıca örneğin İngilizce 'a', belleği 2 sekizlik 0x0061'dir (vs ASCII 'a'nın belleği 1 sekizli 0x61'dir)


0

Burada çok iyi cevaplar var, ancak Windows / Visual Studio ile ilgili ekleyebileceğim birkaç şey olduğunu düşünüyorum. Tis, VS2015 ile olan deneyimime dayanıyor. Linux'ta temel olarak cevap std::stringher yerde kodlanmış UTF-8 kullanmaktır . Windows / VS'de daha karmaşık hale gelir. İşte nedeni. Windows, chars kullanılarak saklanan dizelerin yerel ayar kod sayfası kullanılarak kodlanmasını bekler . Bu hemen hemen her zaman ASCII karakter kümesini ve ardından bulunduğunuz yere bağlı olarak 128 diğer özel karakterdir. Bunu sadece Windows API'sini kullanırken değil, bu dizelerin standart C ++ ile etkileşime girdiği diğer üç önemli yer olduğunu da belirteyim. Bunlar dizgi değişmezleri, dosya adını std::coutkullanmaya <<ve aktarmaya çıktıdır std::fstream.

Burada, bir dil uzmanı değil, bir programcı olduğumun başında olacağım. USC2 ve UTF-16'nın aynı olmadığını takdir ediyorum, ancak benim amacım için değiştirilebilir olacak kadar yakınlar ve onları burada böyle kullanıyorum. Aslında hangi Windows'un kullandığından emin değilim, ama genellikle de bilmeme gerek yok. Bu cevapta UCS2'yi belirttim, eğer bu konuyla ilgili bilgisizliğimden birini rahatsız edersem şimdiden özür dilerim ve yanlış bir şeyim varsa değiştirmek için mutluyum.

Dize değişmez değerleri

Yalnızca kod sayfanız tarafından temsil edilebilecek karakterler içeren dize değişmezleri girerseniz, VS bunları kod sayfanıza göre karakter kodlaması başına 1 bayt ile dosyanızda depolar. Kod sayfanızı değiştirirseniz veya kaynağınızı farklı bir kod sayfası kullanarak başka bir geliştiriciye verirseniz, karakterin farklı olacağını düşünüyorum (ancak test etmedim). Kodunuzu farklı bir kod sayfası kullanarak bir bilgisayarda çalıştırırsanız, karakterin de değişip değişmeyeceğinden emin değilim.

Kod sayfanız tarafından temsil edilemeyen herhangi bir dize değişmezi girerseniz, VS dosyayı Unicode olarak kaydetmenizi ister. Dosya daha sonra UTF-8 olarak kodlanacaktır. Bu, tüm ASCII olmayan karakterlerin (kod sayfanızda bulunanlar dahil) 2 veya daha fazla baytla temsil edileceği anlamına gelir. Bu, kaynağınızı başka birine verirseniz, kaynak aynı görüneceği anlamına gelir. Ancak, kaynağı derleyiciye geçirmeden önce VS, UTF-8 kodlu metni kod sayfası kodlu metne dönüştürür ve kod sayfasında eksik olan tüm karakterlerin yerini alır ?.

VS'de bir Unicode dizgi değişmezini doğru şekilde temsil etmenin tek yolu, dizgi değişmezinden önce Lgeniş bir dizgi değişmezi yapmaktır. Bu durumda VS, UTF-8 kodlu metni dosyadan UCS2'ye dönüştürür. Daha sonra bu dize hazır std::wstringbilgisini bir kurucuya geçirmeniz veya utf-8'e dönüştürmeniz ve bir std::string. İsterseniz, Windows API işlevlerini kullanarak kod sayfanızı kullanarak kodlamak için kullanabilirsiniz std::string, ancak geniş bir dize hazır bilgisi kullanmamış olabilirsiniz.

std :: cout

Kullanarak konsola çıkarılırken <<sadece kullanabilirsiniz std::stringdeğil, std::wstringve metin yerelleştirme Codepage kullanarak kodlanmalıdır. Eğer bir std::wstringWindows API işlevlerinden birini kullanarak dönüştürmek gerekir ve kod sayfanızda olmayan herhangi bir karakter ile değiştirilir ?(belki karakteri değiştirebilirsiniz, hatırlayamıyorum).

std :: fstream dosya adları

Windows işletim sistemi, dosya adları için UCS2 / UTF-16 kullanır, böylece kod sayfanız ne olursa olsun, herhangi bir Unicode karakteri olan dosyalarınız olabilir. Ancak bu, kod sayfanızda olmayan karakterlere sahip dosyalara erişmek veya dosya oluşturmak için kullanmanız gerektiği anlamına gelir std::wstring. Başka yolu yok. Bu, std::fstreamdiğer sistemlerde derlenmeyeceği için Microsoft'a özgü bir uzantıdır . Std :: string kullanıyorsanız, yalnızca kod sayfanızda yalnızca karakter içeren dosya adlarını kullanabilirsiniz.

Seçenekleriniz

Sadece Linux üzerinde çalışıyorsanız, muhtemelen bu kadar uzağa gitmediniz. Sadece std::stringher yerde UTF-8 kullanın .

Sadece Windows üzerinde çalışıyorsanız, std::wstringher yerde UCS2 kullanın . Bazı saflar UTF8'i kullandıktan sonra gerektiğinde dönüştürebilir, ancak neden güçlükle uğraşabilirsiniz.

Çapraz platform iseniz o zaman dürüst olmak için bir karışıklık. UTF-8'i Windows'ta her yerde kullanmaya çalışırsanız, dize değişmezlerine ve konsola çıktıya gerçekten dikkat etmelisiniz. Orada dizelerinizi kolayca bozabilirsiniz. std::wstringLinux'ta her yerde kullanırsanız , geniş sürümüne erişemeyebilirsiniz std::fstream, bu yüzden dönüşümü yapmanız gerekir, ancak yolsuzluk riski yoktur. Kişisel olarak bunun daha iyi bir seçenek olduğunu düşünüyorum. Birçoğu katılmıyorum, ama yalnız değilim - örneğin wxWidgets tarafından alınan yol.

Başka bir seçenek Linux ve Windows'ta unicodestringolduğu gibi typedef yazmak std::stringve std::wstringWindows'ta L ön eki ve Linux'ta hiçbir şey önermeyen UNI () adında bir makroya sahip olmak olabilir.

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
    std::string result;
    //Call WideCharToMultiByte to do the conversion
    return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
    return str;
}
#endif

int main()
{

    unicodestring fileName(UNI("fileName"));
    std::ofstream fout;
    fout.open(fileName);
    std::cout << formatForConsole(fileName) << std::endl;
    return 0;
}

her iki platformda da iyi olacağını düşünüyorum.

Yanıtlar

Yani sorularınızı cevaplamak için

1) Windows için programlıyorsanız, Windows'ta olası yolsuzluk sorunlarıyla uğraşmak veya #ifdefsfarklılıklar üzerinde çalışmak için platformla belirli bir kod yazmak istemiyorsanız, her zaman, çapraz platform varsa, belki de her zaman Linux o zaman asla.

2) Evet. Ayrıca Linux'ta tüm Unicode için de kullanabilirsiniz. Windows'da bunu tüm unicode için yalnızca UTF-8 kullanarak manuel olarak kodlamayı seçerseniz kullanabilirsiniz. Ancak Windows API ve standart C ++ sınıfları std::string, yerel ayar kod sayfası kullanılarak kodlanmasını bekler . Bu, tüm ASCII artı bilgisayarınızın kullanmak üzere ayarladığı kod sayfasına bağlı olarak değişen 128 karakter daha içerir.

3) Buna inanıyorum, ancak değilse, sadece 'std :: basic_string' wchar_tyerine basit bir typedefchar

4) Geniş karakter, 1 bayt standart chartürden daha büyük bir karakter türüdür. Windows'ta 2 bayt, Linux'ta 4 bayt.


1
"Ancak, kaynağı derleyiciye geçirmeden önce VS, UTF-8 kodlu metni kod sayfası kodlu metne dönüştürür ve kod sayfasında eksik olan tüm karakterler? İle değiştirilir." -> Derleyici UTF-8 kodlaması (kullanım /utf-8) kullandığında bunun doğru olduğunu düşünmüyorum .
Roi Danton

Bunun bir seçenek olarak farkında değildim. Bu bağlantıdan docs.microsoft.com/en-us/cpp/build/reference/… proje özelliklerinde seçilecek bir onay kutusu yok gibi görünüyor, ek bir komut satırı seçeneği olarak eklemelisiniz. İyi nokta!
Phil Rosenberg


-6

Ne zaman geniş karakterler KULLANMAMALISINIZ?

1990 yılından önce kod yazarken.

Açıkçası, ben dönüyorum, ama gerçekten, şimdi 21. yüzyıl. 127 karakter uzun zamandan beri yeterli değil. Evet, UTF8'i kullanabilirsiniz, ancak neden baş ağrılarıyla uğraşıyorsunuz?


16
@dave: UTF-8'in hangi baş ağrısının Widechars (UTF-16) 'dan daha fazla yarattığını bilmiyorum. UTF-16'da çok karakterli karakterleriniz de vardır.
Pavel Radzivilovsky

Sorun şu ki, İngilizce konuşulan bir ülkeden başka bir yerdeyseniz, wchar_t kullanmanız gerekir. Bazı alfabelerin bir bayta sığabileceğinden çok daha fazla karakteri olduğunu belirtmiyoruz. DOS'ta oradaydık. Kod sayfası şizofreni, hayır, teşekkürler, daha fazla ..
Swift - Friday Pie

1
@Swift Sorun wchar_t, boyutunun ve anlamının işletim sistemine özgü olmasıdır. Sadece eski problemleri yenileriyle değiştirir. Bir Oysa charbir olduğunu char(en azından, benzer platformlarda) bakılmaksızın OS. Bu nedenle, UTF-8'i de kullanabiliriz, her şeyi chars dizilerine paketleyebiliriz ve C ++ 'ın bu diziler içinde herhangi bir standart ölçüm, indeksleme, bulma vb. Yöntemi olmadan bizi tamamen kendi başımıza nasıl bıraktığını ağıtlayabiliriz.
underscore_d

1
@Swift Tamamen geriye dönük gibi görünüyorsunuz. wchar_tsabit genişlikli bir veri türüdür, bu nedenle 10'luk bir dizi wchar_ther zaman sizeof(wchar_t) * 10platform baytlarını işgal eder . UTF-16, karakterlerin 1 veya 2 16 bit kod noktasından (ve UTF-8 için s / 16/8 / g) oluşturulabildiği değişken genişlikte bir kodlamadır.
underscore_d

1
@SteveHollasch wchar_t dizginin pencerelerde gösterimi, FFFF'den büyük karakterleri özel vekil çifti olarak kodlar, diğeri sadece bir wchar_t öğesi alır. Böylece bu gösterim, gnu derleyicisinin oluşturduğu temsil ile uyumlu olmayacaktır (FFFF'den küçük tüm karakterlerin önünde sıfır sözcük olacaktır). Wchar_t içinde saklanan şey, bir anlaşma ile değil, programcı ve derleyici tarafından belirlenir
Swift - Friday Pie
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.