次のような行で std::string を作成したい場合:
std::string my_string("a\0b");
結果の文字列 (a、null、b) に 3 つの文字を含めたい場合、1 つだけ取得します。適切な構文は何ですか?
リテラルを作成することができました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";
}
問題は、入力が C 文字列でstd::string
あると想定するコンストラクタです。const char*
C 文字列は\0
終了するため、文字に到達すると解析が停止します\0
。
これを補うには、(C-String ではなく) char 配列から文字列を作成するコンストラクターを使用する必要があります。これは、配列へのポインターと長さの 2 つのパラメーターを取ります。
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.
注: C++std::string
は終了していません \0
(他の投稿で示唆されているように)。ただし、メソッドを使用して、C-String を含む内部バッファーへのポインターを抽出できますc_str()
。
a の使用については、以下の Doug T の回答もご覧くださいvector<char>
。
C++14 ソリューションのRiaDもチェックしてください。
C スタイルの文字列 (文字の配列) のように操作する場合は、次の使用を検討してください。
std::vector<char>
c-string を扱う場合と同じように、配列のように扱う自由度が高くなります。copy() を使用して文字列にコピーできます。
std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());
そして、c-strings を使用できる多くの同じ場所で使用できます。
printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';
ただし、当然のことながら、c-string と同じ問題に悩まされます。ヌル端末を忘れたり、割り当てられたスペースを超えて書き込んだりする可能性があります。
ユーザー定義リテラルは、C++ にどのような新しい機能を追加しますか? エレガントな答えを示します: 定義します
std::string operator "" _s(const char* str, size_t n)
{
return std::string(str, n);
}
次に、次の方法で文字列を作成できます。
std::string my_string("a\0b"_s);
またはそうでも:
auto my_string = "a\0b"_s;
「古いスタイル」の方法があります:
#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string
その後、定義できます
std::string my_string(S("a\0b"));
なぜそのようなことをしたいのかわかりませんが、これを試してください:
std::string my_string("a\0b", 3);
以下は動作します...
std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');
これには注意が必要です。'b' を任意の数字に置き換えると、ほとんどの方法を使用して間違った文字列を暗黙のうちに作成します。参照: C++ 文字列リテラルのルール エスケープ文字.
たとえば、私はこの無害に見えるスニペットをプログラムの途中にドロップしました
// 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";
このプログラムの出力は次のとおりです。
Entering loop.
Entering loop.
vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
これは私の最初の print ステートメントであり、印刷されない文字がいくつかあり、その後に改行があり、その後に上書きした内部メモリ内の何かが続きました (そして、上書きされたことを示すために印刷されました)。最悪なことに、gcc の詳細な警告を表示してこれをコンパイルしても、何か問題があることを示す兆候はなく、valgrind を介してプログラムを実行しても、不適切なメモリ アクセス パターンについて文句を言うことはありませんでした。つまり、最新のツールでは完全に検出できません。
これと同じ問題は、はるかに単純なstd::string("0", 100);
で発生しますが、上記の例は少しトリッキーであるため、何が問題なのかを確認するのが難しくなります。
幸いなことに、C++11 では、イニシャライザ リスト構文を使用して問題を解決できます。これにより、文字数を指定する必要がなくなり (上で示したように、間違って指定する可能性があります)、エスケープされた数字の組み合わせを回避できます。およびサイズstd::string str({'a', '\0', 'b'})
の配列を取るバージョンとは異なり、任意の文字列コンテンツに対して安全です。char
C++14 では、リテラルを使用できるようになりました
using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
この質問が教育目的だけではない場合は、 std::vector<char> を使用することをお勧めします。
この質問が聞かれるのは久しぶりです。しかし、同様の問題を抱えている人は、次のコードに興味があるかもしれません。
CComBSTR(20,"mystring1\0mystring2\0")
std::strings のほとんどすべての実装は null で終了するため、おそらくこれを行うべきではありません。自動 null ターミネータ (a、null、b、null) のため、「a\0b」は実際には 4 文字の長さであることに注意してください。本当にこれを実行して std::string の契約を破りたい場合は、次のようにすることができます。
std::string s("aab");
s.at(1) = '\0';
しかし、そうすると、すべての友達があなたを笑ってしまい、本当の幸せを見つけることはできません.