Aşağıdaki gibi bir satır içeren bir std :: string oluşturmak istersem:
std::string my_string("a\0b");
Elde edilen dizede üç karakter olmasını istediğimde (a, null, b), yalnızca bir tane alırım. Doğru sözdizimi nedir?
Aşağıdaki gibi bir satır içeren bir std :: string oluşturmak istersem:
std::string my_string("a\0b");
Elde edilen dizede üç karakter olmasını istediğimde (a, null, b), yalnızca bir tane alırım. Doğru sözdizimi nedir?
Yanıtlar:
gerçek anlamda yaratabildik std::string
#include <iostream>
#include <string>
int main()
{
using namespace std::string_literals;
std::string s = "pl-\0-op"s; // <- Notice the "s" at the end
// This is a std::string literal not
// a C-String literal.
std::cout << s << "\n";
}
Sorun, girdinin bir C-dizesi olduğunu varsayan std::string
yapıcıdır const char*
. C-dizeleri \0
sonlandırılır ve dolayısıyla \0
karaktere ulaştığında ayrıştırma durur .
Bunu telafi etmek için, dizeyi bir char dizisinden (bir C-String değil) oluşturan yapıcıyı kullanmanız gerekir. Bu, iki parametre alır - diziye bir işaretçi ve bir uzunluk:
std::string x("pq\0rs"); // Two characters because input assumed to be C-String
std::string x("pq\0rs",5); // 5 Characters as the input is now a char array with 5 characters.
Not: C ++ std::string
edilir DEĞİL \0
(diğer mesajlar önerildiği gibi) nihayetti. Ancak, yöntemle bir C-String içeren bir dahili tampona bir işaretçi ayıklayabilirsiniz c_str()
.
Ayrıca Doug T'nin a vector<char>
.
Ayrıca bir C ++ 14 çözümü için RiaD'ye bakın.
Eğer bir c-stili dizgeyle (karakter dizisi) yaptığınız gibi manipülasyon yapıyorsanız, kullanmayı düşünün
std::vector<char>
Bir c-dizgesine davrandığınız gibi onu bir dizi gibi ele almak için daha fazla özgürlüğünüz var. Bir dizeye kopyalamak için copy () kullanabilirsiniz:
std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());
ve c-dizelerini kullanabileceğiniz birçok yerde kullanabilirsiniz.
printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';
Doğal olarak, yine de, c-dizgileriyle aynı sorunlardan muzdaripsiniz. Boş uçbiriminizi unutabilir veya ayrılan alanın ötesine yazabilirsiniz.
byte *bytes = new byte[dataSize]; std::memcpy(bytes, image.data, dataSize * sizeof(byte)); std::string test(reinterpret_cast<char *>(bytes)); std::cout << "Encoded String length " << test.length() << std::endl;
Neden böyle bir şey yapmak istediğiniz hakkında hiçbir fikrim yok, ama şunu deneyin:
std::string my_string("a\0b", 3);
vector<unsigned char>
ya unsigned char *
da icat edildi.
std::string
Verilerin düz metin olarak değerlendirilmesi gerektiğini belirtmek için kullanıyorum , ancak bazı hash işlemleri yapıyorum ve her şeyin hala boş karakterlerle çalıştığından emin olmak istiyorum. Bu, gömülü bir boş karaktere sahip bir dizgenin geçerli bir kullanımı gibi görünüyor.
\0
UTF-8 dizesindeki bir bayt yalnızca NUL olabilir. Çok baytlı kodlanmış bir karakter asla - \0
ya da bu konuda başka bir ASCII karakteri içermez .
Kullanıcı tanımlı değişmez değerler C ++ 'ya hangi yeni yetenekler ekler? zarif bir cevap sunar: Tanımla
std::string operator "" _s(const char* str, size_t n)
{
return std::string(str, n);
}
o zaman dizenizi şu şekilde oluşturabilirsiniz:
std::string my_string("a\0b"_s);
hatta öyle:
auto my_string = "a\0b"_s;
"Eski tarz" bir yol var:
#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string
o zaman tanımlayabilirsin
std::string my_string(S("a\0b"));
Aşağıdakiler işe yarayacak ...
std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');
Bu konuda dikkatli olmalısın. 'B'yi herhangi bir sayısal karakterle değiştirirseniz, çoğu yöntemi kullanarak sessizce yanlış dizeyi oluşturursunuz. Bakınız: C ++ dize değişmezleri için kurallar kaçış karakteri .
Örneğin, bu masum görünümlü parçacığı bir programın ortasına düşürdüm
// Create '\0' followed by '0' 40 times ;)
std::string str("\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", 80);
std::cerr << "Entering loop.\n";
for (char & c : str) {
std::cerr << c;
// 'Q' is way cooler than '\0' or '0'
c = 'Q';
}
std::cerr << "\n";
for (char & c : str) {
std::cerr << c;
}
std::cerr << "\n";
İşte bu programın bana çıkardığı şey:
Entering loop.
Entering loop.
vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
Bu, iki kez ilk yazdırma ifademdi, birkaç basılmayan karakter, ardından bir satırsonu, ardından dahili bellekte üzerine yazdığım bir şey geldi (ve sonra üzerine yazıldığını göstererek yazdırdım). Hepsinden kötüsü, bunu kapsamlı ve ayrıntılı gcc uyarılarıyla derlemek bile bana bir şeyin yanlış olduğuna dair hiçbir gösterge vermedi ve programı valgrind aracılığıyla çalıştırmak, herhangi bir uygunsuz bellek erişim modelinden şikayet etmedi. Başka bir deyişle, modern araçlar tarafından tamamen tespit edilemez.
Aynı sorunu çok daha basit olanla da alabilirsiniz std::string("0", 100);
, ancak yukarıdaki örnek biraz daha karmaşıktır ve bu nedenle neyin yanlış olduğunu görmek daha zordur.
Neyse ki, C ++ 11 bize başlatıcı listesi sözdizimini kullanarak soruna iyi bir çözüm sunar. Bu sizi karakter sayısını (yukarıda gösterdiğim gibi yanlış yapabilirsiniz) belirtme zorunluluğundan kurtarır ve kaçan sayıları birleştirmekten kaçınır. std::string str({'a', '\0', 'b'})
dizi char
ve boyut alan sürümlerin aksine tüm dizgi içeriği için güvenlidir .
C ++ 14'te artık değişmez değerleri kullanabilirsiniz
using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
auto s{"a\0b"s};
Bu soru sadece eğitim amaçlı değilse std :: vector <char> kullanmak daha iyidir.
anonimin cevabı mükemmel, ancak C ++ 98'de de makro olmayan bir çözüm var:
template <size_t N>
std::string RawString(const char (&ch)[N])
{
return std::string(ch, N-1); // Again, exclude trailing `null`
}
Bu işlevle RawString(/* literal */)
aynı dizeyi şu şekilde üretir S(/* literal */)
:
std::string my_string_t(RawString("a\0b"));
std::string my_string_m(S("a\0b"));
std::cout << "Using template: " << my_string_t << std::endl;
std::cout << "Using macro: " << my_string_m << std::endl;
Ek olarak, makro ile ilgili bir sorun var: ifade aslında std::string
yazıldığı gibi değil ve bu nedenle örneğin basit atama-başlatma için kullanılamaz:
std::string s = S("a\0b"); // ERROR!
... bu nedenle kullanılması tercih edilebilir:
#define std::string(s, sizeof s - 1)
Açıkçası, projenizde yalnızca birini veya diğerini kullanmalı ve uygun olduğunu düşündüğünüz şekilde adlandırmalısınız.
Bu sorunun uzun zamandır sorulduğunu biliyorum. Ancak benzer bir sorunu olan herkes aşağıdaki kodla ilgilenebilir.
CComBSTR(20,"mystring1\0mystring2\0")
Std :: strings'in neredeyse tüm gerçeklenimleri boş sonlandırılmıştır, bu yüzden muhtemelen bunu yapmamalısınız. Otomatik boş sonlandırıcı (a, null, b, null) nedeniyle "a \ 0b" nin aslında dört karakter uzunluğunda olduğuna dikkat edin. Bunu gerçekten yapmak ve std :: string'in sözleşmesini bozmak istiyorsanız, şunları yapabilirsiniz:
std::string s("aab");
s.at(1) = '\0';
ama yaparsan, bütün arkadaşların sana gülecek, gerçek mutluluğu asla bulamayacaksın.