Nếu tôi muốn tạo một chuỗi std :: với một dòng như sau:
std::string my_string("a\0b");
Trong trường hợp tôi muốn có ba ký tự trong chuỗi kết quả (a, null, b), tôi chỉ nhận được một. Cú pháp thích hợp là gì?
Nếu tôi muốn tạo một chuỗi std :: với một dòng như sau:
std::string my_string("a\0b");
Trong trường hợp tôi muốn có ba ký tự trong chuỗi kết quả (a, null, b), tôi chỉ nhận được một. Cú pháp thích hợp là gì?
Câu trả lời:
chúng tôi đã có thể tạo ra 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";
}
Vấn đề là hàm std::string
tạo nhận một const char*
giả sử đầu vào là một chuỗi C. Chuỗi C được \0
kết thúc và do đó quá trình phân tích cú pháp dừng lại khi nó đến \0
ký tự.
Để bù đắp điều này, bạn cần sử dụng hàm tạo xây dựng chuỗi từ một mảng char (không phải chuỗi C). Điều này nhận hai tham số - một con trỏ đến mảng và một độ dài:
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.
Lưu ý: C ++ std::string
là KHÔNG \0
-terminated (như đề xuất trong bài viết khác). Tuy nhiên, bạn có thể trích xuất một con trỏ đến bộ đệm bên trong có chứa C-String bằng phương thức này c_str()
.
Ngoài ra, hãy xem câu trả lời của Doug T dưới đây về việc sử dụng a vector<char>
.
Ngoài ra, hãy xem RiaD để biết giải pháp C ++ 14.
Nếu bạn đang thực hiện thao tác như bạn làm với chuỗi kiểu c (mảng ký tự), hãy xem xét sử dụng
std::vector<char>
Bạn có nhiều quyền tự do hơn để xử lý nó giống như một mảng giống như cách bạn xử lý một chuỗi c. Bạn có thể sử dụng copy () để sao chép vào một chuỗi:
std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());
và bạn có thể sử dụng nó ở nhiều nơi giống nhau, bạn có thể sử dụng c-string
printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';
Tuy nhiên, đương nhiên, bạn gặp phải những vấn đề tương tự như c-string. Bạn có thể quên thiết bị đầu cuối rỗng của mình hoặc ghi quá không gian được cấp phát.
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;
Tôi không biết tại sao bạn muốn làm điều đó, nhưng hãy thử điều này:
std::string my_string("a\0b", 3);
vector<unsigned char>
hoặc unsigned char *
được phát minh ra để làm gì.
std::string
để chỉ ra rằng dữ liệu nên được coi là văn bản thuần túy, nhưng tôi đang thực hiện một số công việc băm và tôi muốn đảm bảo rằng mọi thứ vẫn hoạt động với các ký tự rỗng có liên quan. Điều đó có vẻ giống như việc sử dụng hợp lệ một chuỗi ký tự có ký tự null được nhúng.
\0
byte trong chuỗi UTF-8 chỉ có thể là NUL. Một ký tự được mã hóa nhiều byte sẽ không bao giờ chứa - \0
với bất kỳ ký tự ASCII nào khác cho vấn đề đó.
Những khả năng mới nào mà các ký tự do người dùng xác định thêm vào C ++? trình bày một câu trả lời thanh lịch: Xác định
std::string operator "" _s(const char* str, size_t n)
{
return std::string(str, n);
}
thì bạn có thể tạo chuỗi của mình theo cách này:
std::string my_string("a\0b"_s);
hoặc thậm chí như vậy:
auto my_string = "a\0b"_s;
Có một cách "kiểu cũ":
#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string
sau đó bạn có thể xác định
std::string my_string(S("a\0b"));
Sau đây sẽ hoạt động ...
std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');
Bạn sẽ phải cẩn thận với điều này. Nếu bạn thay thế 'b' bằng bất kỳ ký tự số nào, bạn sẽ âm thầm tạo chuỗi sai bằng cách sử dụng hầu hết các phương pháp. Hãy xem: Quy tắc cho ký tự thoát chuỗi ký tự C ++ .
Ví dụ: tôi đã bỏ đoạn mã trông có vẻ ngây thơ này vào giữa chương trình
// 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";
Đây là những gì chương trình này xuất ra cho tôi:
Entering loop.
Entering loop.
vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
Đó là câu lệnh in đầu tiên của tôi hai lần, một số ký tự không in được, tiếp theo là một dòng mới, tiếp theo là một thứ gì đó trong bộ nhớ trong mà tôi vừa ghi đè (và sau đó in ra, cho thấy rằng nó đã bị ghi đè). Tệ nhất, ngay cả việc biên dịch điều này với các cảnh báo gcc chi tiết và kỹ lưỡng cũng không cho tôi dấu hiệu nào cho thấy có điều gì đó không ổn và việc chạy chương trình thông qua valgrind cũng không phàn nàn về bất kỳ kiểu truy cập bộ nhớ không phù hợp nào. Nói cách khác, nó hoàn toàn không thể bị phát hiện bởi các công cụ hiện đại.
Bạn có thể gặp phải vấn đề tương tự với cách đơn giản hơn nhiều std::string("0", 100);
, nhưng ví dụ trên phức tạp hơn một chút, và do đó khó nhận ra điều gì sai.
May mắn thay, C ++ 11 cung cấp cho chúng ta một giải pháp tốt cho vấn đề bằng cách sử dụng cú pháp danh sách trình khởi tạo. Điều này giúp bạn không phải chỉ định số ký tự (như tôi đã trình bày ở trên, bạn có thể làm sai) và tránh kết hợp các số thoát. std::string str({'a', '\0', 'b'})
an toàn cho bất kỳ nội dung chuỗi nào, không giống như các phiên bản có một mảng char
và một kích thước.
Trong C ++ 14 bây giờ bạn có thể sử dụng các ký tự
using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
auto s{"a\0b"s};
Tốt hơn nên sử dụng std :: vector <char> nếu câu hỏi này không chỉ dành cho mục đích giáo dục.
Câu trả lời của người nặc danh là tuyệt vời, nhưng cũng có một giải pháp không phải macro trong C ++ 98:
template <size_t N>
std::string RawString(const char (&ch)[N])
{
return std::string(ch, N-1); // Again, exclude trailing `null`
}
Với hàm này, RawString(/* literal */)
sẽ tạo ra cùng một chuỗi như 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;
Ngoài ra, có một vấn đề với macro: biểu thức không thực sự là một std::string
như được viết, và do đó không thể được sử dụng, ví dụ như để gán-khởi tạo đơn giản:
std::string s = S("a\0b"); // ERROR!
... vì vậy nó có thể thích hợp hơn để sử dụng:
#define std::string(s, sizeof s - 1)
Rõ ràng là bạn chỉ nên sử dụng một hoặc giải pháp khác trong dự án của mình và gọi nó là bất cứ thứ gì bạn cho là phù hợp.
Tôi biết câu hỏi này đã được đặt ra từ rất lâu rồi. Nhưng đối với bất kỳ ai đang gặp vấn đề tương tự có thể quan tâm đến đoạn mã sau.
CComBSTR(20,"mystring1\0mystring2\0")
Hầu như tất cả các triển khai của std :: string đều được kết thúc bằng null, vì vậy bạn có thể không nên làm điều này. Lưu ý rằng "a \ 0b" thực sự dài bốn ký tự do dấu chấm hết null tự động (a, null, b, null). Nếu bạn thực sự muốn làm điều này và phá vỡ hợp đồng của std :: string, bạn có thể làm:
std::string s("aab");
s.at(1) = '\0';
nhưng nếu bạn làm vậy, tất cả bạn bè của bạn sẽ cười nhạo bạn, bạn sẽ không bao giờ tìm thấy hạnh phúc thực sự.