Soru C ++ 11'de ikili veriler için std :: string'i güvenle kullanabilir miyim?


İnternette kullanmanız gerektiğini öneren birkaç mesaj var. std::vector<unsigned char> veya ikili veriler için benzer bir şey.

Ama daha çok tercih ederim std::basic_string Bunun için varyant, birçok kullanışlı dize manipülasyon fonksiyonları sağlar. Ve AFAIK, C ++ 11'den beri, standart bilinen her C ++ 03 uygulamasının ne yaptığını garanti ediyor: std::basic_string içeriğini bellekte sürekli olarak saklar.

İlk bakışta o zaman, std::basic_string<unsigned char> iyi bir seçim olabilir.

Kullanmak istemiyorum std::basic_string<unsigned char>Ancak, neredeyse tüm işletim sistemi işlevleri kabul ettiği için char*, açık bir döküm yapmak gerekli. Ayrıca, dize değişmezleri const char*bu yüzden açık bir oyuncuya ihtiyacım var const unsigned char* her defasında, dizgi dizgisi için bir dizgi hazırladım, ki bu da kaçınmak isterim. Ayrıca, dosyalara veya ağ arabelleklerine okuma ve yazma işlevleri de benzer şekilde kabul edilir. char* ve const char* işaretçileri.

Bu yapraklar std::string, temelde bir yazım için std::basic_string<char>.

Kullanarak kalan tek potansiyel sorun (görebiliyorum) std::string ikili veri için std::string kullanımları char (imzalı olabilir).

char, signed char, ve unsigned char üç farklı tip ve char imzasız veya imzalı olabilir.

Yani, gerçek bir bayt değeri 11111111b dan döndü std::string:operator[] char olarak ve değerini kontrol etmek istiyorsanız, değeri ya da 255 (Eğer char imzasız) veya "olumsuz bir şey" olabilir (eğer char numara gösterime bağlı olarak imzalanır).

Benzer şekilde, gerçek bayt değerini açıkça eklemek istiyorsanız 11111111b bir std::string, sadece ekleme (char) (255) Uygulama tanımlanmış olabilir (ve hatta bir sinyal yükseltmek) char imzalandı ve int için char konuşma bir taşma ile sonuçlanır.

Yani, bu etrafında güvenli bir yol var, bu yapar std::string tekrar ikili güvenli mi?

§3.10 / 15 eyaletleri:

Bir program, bir nesnenin kayıtlı değerine bir glvalue Aşağıdaki türlerden biri dışında davranış tanımlanmamıştır:

  • [...]
  • nesnenin dinamik tipine karşılık gelen imzalı veya işaretsiz tipte bir tür,
  • [...]
  • char veya imzasız char türü.

Hangi doğru anladım, bir unsigned char* bir bir içeriğe erişmek ve değiştirmek için işaretçi std::string ve bunu da yapar iyi tanımlanmış. Bu sadece yeniden yorumluyor bit kalıbı unsigned charherhangi bir değişiklik veya bilgi kaybı olmadan, ikincisi, çünkü char, signed char, ve unsigned char Değer gösterimi için kullanılmalıdır.

Bunu daha sonra kullanabilirdim unsigned char* içeriğinin yorumlanması std::string bayt değerlerine erişmek ve değiştirmek için bir araç olarak [0, 255] , iyi tanımlanmış ve taşınabilir bir şekilde, ne olursa olsun char kendisi.

Bu, potansiyel olarak imzalanmış herhangi bir problemi çözmelidir char.

Varsayımlarım ve vardıklarım doğru mu?

Ayrıca, unsigned char* aynı bit deseninin yorumlanması (ör. 11111111b veya 10101010b) tüm uygulamalarda aynı olması garantilidir? Başka bir deyişle, standart "gözün unsigned char", aynı bit desen her zaman aynı sayısal değere yol açar (bir bayttaki bitlerin sayısı aynı mıdır)?

Böylece güvenli bir şekilde (yani, hiç yok tanımlanmamış veya uygulama tanımlı davranış) std::string C ++ 11'de ikili verileri saklamak ve değiştirmek için?


17
2017-11-03 20:22


Menşei


Sadece bir vektör kullan. Bir ip ile yapabileceğin her şeyi yapabilirsin. - jrok
İkili verilerde ne tür "kullanışlı dizi düzenleme işlevleri" kullanmak istersiniz? Bu nasıl bir anlam kazanır ki? - jalf
Verilerinizde boş baytınız olduğunda çoğu string işlemi başarısız olur. - Frank Osterfeld
@jalf: Benim durumumda, mantıklıdır, çünkü veriler ikili olsa da, genellikle belirli yerlerde UTF-8 dizeleri içerir. Ayrıca substr () kullanmak istiyorum. - JohnCand
Senin sorunun "Ben bir manipüle edebilir miyim char bir nesne aracılığıyla unsigned char* ve tersi? " - dyp


Cevaplar:


Dönüşüm static_cast<char>(uc) nerede uc türdür unsigned char her zaman geçerlidir: 3.9.1'e göre [basic.fundamental] temsili char, signed char, ve unsigned char aynıdır char diğer iki türden biriyle aynı olmak:

Karakter (char) olarak bildirilen nesneler, uygulamanın temel karakter kümesinin herhangi bir üyesini depolayacak kadar büyük olmalıdır. Bu setteki bir karakter bir karakter nesnesinde saklanırsa, o karakter nesnesinin integral değeri, o karakterin tek karakterli tamsayı formunun değerine eşittir. Bir char nesnesinin negatif değerler tutabildiğini uygulayarak tanımlanır. Karakterler açık bir şekilde imzasız veya imzalı olarak ilan edilebilir. Düz char, imzalı char ve imzasız char, toplu olarak dar karakter tipleri olarak adlandırılan üç ayrı tiptir. Bir char, imzalı bir char ve imzasız bir char aynı miktarda depoyu işgal eder ve aynı hizalama şartlarına sahiptir (3.11); Yani, aynı nesne temsiline sahipler. Dar karakter türleri için, nesne temsilinin tüm bitleri, değer temsiline katılır. İmzasız dar karakter tipleri için, değer temsilinin tüm olası bit modelleri sayıları temsil eder. Bu şartlar diğer türler için geçerli değildir. Herhangi bir özel uygulamada, bir düz char nesnesi aynı şeyi üstlenebilir   imzalı bir char veya imzasız bir char olarak değerler; hangisi uygulama tanımlıdır.

Değerleri aralık dışında dönüştürme unsigned char için char Elbette, sorunlu olacak ve tanımlanmamış davranışlara neden olabilir. Yani, komik değerleri std::string iyi olursun Bit kalıpları ile ilgili olarak, güvenebilirsiniz n2'ye tercüme etmek için birazn. İkili verileri depolamak için bir sorun olmamalı. std::string dikkatli bir şekilde işlendiğinde.

Bu, ben senin öncül içine satın almayın dedi: ikili veri işleme çoğunlukla kullanarak en iyi manipüle byte ile uğraşmak gerektirir unsigned değerler. Aralarında dönüşüm yapmanız gereken birkaç durum char* ve unsigned char* kullanımı sırasında karmaşık bir şekilde tedavi edilmediğinde uygun hatalar yaratın char yanlışlıkla sessiz olacak! Yani, ile uğraşmak unsigned char hataları önler. Ayrıca tüm bu güzel dize işlevlerini aldığınızı önermezim: birincisi, yine de algoritmaları kullanmaktan daha iyidir, ancak ikili veri de değil dize verileri. Özetle: öneri std::vector<unsigned char>sadece ince havadan değil! Tasarımın içine tuzakları bulmak için sıkı bir şekilde bina yapmaktan kaçınmak kasıtlıdır!

Kullanmanın lehine olan tek hafif argüman char Dize değişmezleri hakkında bir tane olabilir ama C ++ 11'e tanıtılan kullanıcı tanımlı dize değişmezleriyle su tutmuyor bile:

#include <cstddef>
unsigned char const* operator""_u (char const* s, size_t) 
{
    return reinterpret_cast<unsigned char const*>(s);
}

unsigned char const* hello = "hello"_u;

17
2017-11-03 21:01



Detaylı cevabınız için teşekkür ederiz. Argümanlarınız oldukça ikna edici. Henüz alıntılanan paragrafı tamamen anladığımdan emin değilim, bu yüzden lütfen sormamla çıplak olun: 1) 127'den büyük kod noktalarını içeren au "" UTF-8 dizesiyle birlikte sağladığınız kullanıcı tanımlı dize değişmezini kullanırsam, Örneğin u8 "â" _u, bayt dizisi 0xC3 0xA2 olan ve benim char imzalanacak olur, bu, bu dizenin her iki chars dönüşüm önce negatif olmasıyla sonuçlanır. Standart, dönüştürücü editörünüzün her zaman tam olarak aynı imzasız sayısal sayısal değerlerle sonuçlanmasını garanti eder mi? - JohnCand
... (Yani, her zaman gerçekten 0xC3 0xA2 geri sayısal değerleri alacağım)? 2) Sadece char * veya const char * 'yı kabul eden işletim sistemi işlevleri ile nasıl doğru bir şekilde bağlantı kurabilirim? Muhtemelen cevabınızdaki her iki soruya da zaten değindiniz, ama bu noktalar hala bana tamamen açık değil. - JohnCand
@ JohnCaC2: 1. Temsili signed char ve unsigned char bitleri değiştirmeden birbiri arasında dökülebilir. Sonuç şu ki bu olumlu signed char değerler ile aynı değere sahip olacak unsigned char olanlar; Negatif değerlerin nasıl dönüştüğü unsigned char ya da başka bir şekilde belirtilmemiş, ancak bit deseni hala değişmez. Bu dönüştürüyor signed char için unsigned char ve geri kimlik fonksiyonu (aynı şekilde diğer şekilde). 2. reinterpret_cast<char*>(...). Asıl nokta, derleyicinin gerekli olduğu yerde yakalanması. - Dietmar Kühl


Evet, varsayımların doğru. İkili verileri std :: dizesinde imzasız char dizisi olarak depolayın.


1
2017-11-03 20:46



Reddetmekten kaçıracağım. Ancak kanonik cevap kelimeleri içermelidir unsigned ve vector İşte - sehe
Sanırım bu düşüşün nedenini açıklamak ve bunun neden bu kadar kötü olduğu konusunda bazı nedenler açıklamak faydalı olacaktır. - Venemo


Microsoft Visual Studio'da ikili verileri işlemek için std :: string kullanarak sorun yaşadım. Dizeleri açıklanamayacak şekilde kesildiğini gördüm, bu yüzden standart belgelerin söylediğine bakılmaksızın bunu yapmayacağım.


-1
2018-01-05 00:37



"Standart belgelerin ne dediğine bakmaksızın" diyen şey satanizmdir: D - Géza Török