次のような行でstd :: stringを作成する場合:
std::string my_string("a\0b");
結果の文字列に3文字(a、null、b)が必要な場合、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ではなく)から文字列を構築するコンストラクタを使用する必要があります。これには、配列へのポインターと長さの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
_ is[〜#〜] not [〜#〜]_\0
_- terminated(他で提案されているように)投稿)。ただし、メソッドc_str()
を使用して、C-Stringを含む内部バッファーへのポインターを抽出できます。
_vector<char>
_の使用について Doug Tの答え も確認してください。
C++ 14ソリューションについては、 RiaD も確認してください。
Cスタイルの文字列(charsの配列)を使用する場合と同様に操作を行う場合は、
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ストリングを使用できるのと同じ場所の多くで使用できます。
printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';
ただし、当然ながら、Cストリングと同じ問題に悩まされます。 nullターミナルを忘れるか、割り当てられたスペースを超えて書き込むことがあります。
なぜこのようなことをしたいのですが、これを試してみてください:
std::string my_string("a\0b", 3);
ユーザー定義リテラルが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 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
_
それは私の最初の印刷文で、いくつかの非印刷文字、それに続く改行、それに続いて内部メモリ内の何かが続き、それを上書きしました(そして印刷され、上書きされたことを示しました)。最悪なのは、これを 徹底的かつ詳細な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>を使用することをお勧めします。
匿名の答えは優れていますが、C++ 98には非マクロソリューションもあります。
_template <size_t N>
std::string RawString(const char (&ch)[N])
{
return std::string(ch, N-1); // Again, exclude trailing `null`
}
_
この関数では、RawString(/* literal */)
は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;
_
さらに、マクロに問題があります:式は実際には_std::string
_ではないため、使用できません。単純な割り当て初期化の場合:
_std::string s = S("a\0b"); // ERROR!
_
...したがって、使用することが望ましい場合があります:
_#define std::string(s, sizeof s - 1)
_
明らかに、プロジェクトではどちらか一方のソリューションのみを使用し、適切と思われるものを呼び出す必要があります。