Jika saya ingin membuat std :: string dengan garis seperti:
std::string my_string("a\0b");
Di mana saya ingin memiliki tiga karakter dalam string yang dihasilkan (a, null, b), saya hanya mendapatkan satu. Apa sintaks yang tepat?
Jika saya ingin membuat std :: string dengan garis seperti:
std::string my_string("a\0b");
Di mana saya ingin memiliki tiga karakter dalam string yang dihasilkan (a, null, b), saya hanya mendapatkan satu. Apa sintaks yang tepat?
Jawaban:
kami telah mampu menciptakan literal 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";
}
Masalahnya adalah std::string
konstruktor yang const char*
menganggap input adalah string-C. String C \0
diakhiri dan penguraian berhenti saat mencapai \0
karakter.
Untuk mengimbangi ini, Anda perlu menggunakan konstruktor yang membangun string dari array karakter (bukan C-String). Ini membutuhkan dua parameter - pointer ke array dan panjang:
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.
Catatan: C ++ std::string
adalah TIDAK \0
-terminated (seperti yang disarankan dalam posting lain). Namun, Anda dapat mengekstrak pointer ke buffer internal yang berisi C-String dengan metode tersebut c_str()
.
Lihat juga jawaban Doug T di bawah ini tentang penggunaan a vector<char>
.
Lihat juga RiaD untuk solusi C ++ 14.
Jika Anda melakukan manipulasi seperti yang Anda lakukan dengan string gaya-c (larik karakter) pertimbangkan untuk menggunakan
std::vector<char>
Anda memiliki lebih banyak kebebasan untuk memperlakukannya seperti array dengan cara yang sama seperti Anda memperlakukan c-string. Anda dapat menggunakan copy () untuk menyalin ke dalam string:
std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());
dan Anda bisa menggunakannya di banyak tempat yang sama Anda bisa menggunakan c-string
printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';
Secara alami, bagaimanapun, Anda mengalami masalah yang sama seperti c-string. Anda mungkin lupa terminal null Anda atau menulis melewati ruang yang dialokasikan.
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;
Saya tidak tahu mengapa Anda ingin melakukan hal seperti itu, tetapi coba ini:
std::string my_string("a\0b", 3);
vector<unsigned char>
atau unsigned char *
diciptakan.
std::string
untuk menunjukkan bahwa data harus dianggap sebagai teks biasa, tetapi saya melakukan beberapa pekerjaan hashing dan saya ingin memastikan semuanya masih berfungsi dengan karakter nol yang terlibat. Itu sepertinya penggunaan literal string yang valid dengan karakter null yang disematkan.
\0
byte dalam UTF-8 string hanya dapat NUL. Karakter yang dienkode multi-byte tidak akan pernah berisi \0
--atau karakter ASCII lainnya dalam hal ini.
Kemampuan baru apa yang ditambahkan literal yang ditentukan pengguna ke C ++? menyajikan jawaban yang elegan: Tentukan
std::string operator "" _s(const char* str, size_t n)
{
return std::string(str, n);
}
maka Anda dapat membuat string Anda dengan cara ini:
std::string my_string("a\0b"_s);
atau bahkan lebih:
auto my_string = "a\0b"_s;
Ada cara "gaya lama":
#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string
lalu Anda bisa mendefinisikan
std::string my_string(S("a\0b"));
Berikut ini akan bekerja ...
std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');
Anda harus berhati-hati dengan ini. Jika Anda mengganti 'b' dengan karakter numerik apa pun, Anda akan membuat string yang salah secara diam-diam menggunakan sebagian besar metode. Lihat: Aturan untuk karakter pelarian literal string C ++ .
Misalnya, saya menjatuhkan potongan yang tampak tidak bersalah ini di tengah-tengah program
// 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";
Inilah hasil program ini untuk saya:
Entering loop.
Entering loop.
vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
Itu adalah pernyataan cetak pertama saya dua kali, beberapa karakter non-cetak, diikuti oleh baris baru, diikuti oleh sesuatu di memori internal, yang baru saja saya timpa (dan kemudian dicetak, menunjukkan bahwa itu telah ditimpa). Yang terburuk dari semuanya, bahkan mengkompilasi ini dengan peringatan gcc yang menyeluruh dan bertele-tele tidak memberi saya indikasi ada sesuatu yang salah, dan menjalankan program melalui valgrind tidak mengeluh tentang pola akses memori yang tidak tepat. Dengan kata lain, itu sama sekali tidak terdeteksi oleh alat modern.
Anda bisa mendapatkan masalah yang sama dengan yang lebih sederhana std::string("0", 100);
, tetapi contoh di atas sedikit lebih rumit, dan karenanya lebih sulit untuk melihat apa yang salah.
Untungnya, C ++ 11 memberi kami solusi yang baik untuk masalah menggunakan sintaks daftar penginisialisasi. Ini menyelamatkan Anda dari keharusan untuk menentukan jumlah karakter (yang, seperti yang saya tunjukkan di atas, Anda dapat melakukannya dengan tidak benar), dan menghindari penggabungan nomor yang lolos. std::string str({'a', '\0', 'b'})
aman untuk konten string apa pun, tidak seperti versi yang menggunakan larik char
dan ukuran.
Di C ++ 14 Anda sekarang dapat menggunakan literal
using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
auto s{"a\0b"s};
Lebih baik menggunakan std :: vector <char> jika pertanyaan ini bukan hanya untuk tujuan pendidikan.
jawaban anonym sangat bagus, tetapi ada solusi non-makro di C ++ 98 juga:
template <size_t N>
std::string RawString(const char (&ch)[N])
{
return std::string(ch, N-1); // Again, exclude trailing `null`
}
Dengan fungsi ini, RawString(/* literal */)
akan menghasilkan string yang sama seperti 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;
Selain itu, ada masalah dengan makro: ekspresi sebenarnya tidak std::string
seperti yang tertulis, dan oleh karena itu tidak dapat digunakan misalnya untuk inisialisasi-tugas sederhana:
std::string s = S("a\0b"); // ERROR!
... jadi mungkin lebih baik menggunakan:
#define std::string(s, sizeof s - 1)
Jelas Anda hanya boleh menggunakan satu atau solusi lain dalam proyek Anda dan menyebutnya apa pun yang menurut Anda sesuai.
Saya tahu sudah lama pertanyaan ini ditanyakan. Tetapi bagi siapa saja yang mengalami masalah serupa mungkin tertarik dengan kode berikut.
CComBSTR(20,"mystring1\0mystring2\0")
Hampir semua implementasi std :: strings dihentikan oleh null, jadi Anda sebaiknya tidak melakukan ini. Perhatikan bahwa "a \ 0b" sebenarnya terdiri dari empat karakter karena terminator null otomatis (a, null, b, null). Jika Anda benar-benar ingin melakukan ini dan memutuskan kontrak std :: string, Anda dapat melakukan:
std::string s("aab");
s.at(1) = '\0';
tetapi jika Anda melakukannya, semua teman Anda akan menertawakan Anda, Anda tidak akan pernah menemukan kebahagiaan sejati.