std :: endsが原因で文字列比較が失敗するのはなぜですか?
質問
昨日、コードでこの問題を修正しようとして約4時間を費やしました。問題を次の例に単純化しました。
アイデアは、std :: endsで終わる文字列を文字列に格納し、後で取得して元の文字列と比較することです。
#include <sstream> #include <iostream> #include <string> int main( int argc, char** argv ) { const std::string HELLO( "hello" ); std::stringstream testStream; testStream << HELLO << std::ends; std::string hi = testStream.str(); if( HELLO == hi ) { std::cout << HELLO << "==" << hi << std::endl; } return 0; }
ご想像のとおり、上記のコードを実行しても何も出力されません。
出力するか、デバッガー(VS2005)で見ると、HELLOとhiは同じように見えますが、実際には.length()は1だけ異なります。それが&quot; ==&quot;の原因です失敗する演算子。
私の質問は理由です。 std :: endsが文字列hiに追加された目に見えない文字である理由がわかりません。同じ内容であってもhiとHELLOの長さが異なります。さらに、この不可視の文字は、ブーストトリムではトリミングされません。ただし、strcmpを使用して2つの文字列の.c_str()を比較する場合、比較は正しく機能します。
最初にstd :: endsを使用した理由は、過去に文字列ストリームがストリームの最後にガベージデータを保持するという問題があったためです。 std :: endsは私のためにそれを解決しました。
解決
std :: ends
は、null文字をストリームに挿入します。コンテンツを std :: string
として取得すると、そのヌル文字が保持され、それぞれの位置にヌル文字を含む文字列が作成されます。
実際、std :: stringにはヌル文字を埋め込むことができます。次のstd :: stringの内容は 異なります:
ABC
ABC\0
バイナリゼロは空白ではありません。ただし、印刷もできないため、表示されません(端末が特別に表示しない限り)。
strcmp
を使用して比較すると、 .c_str()
を渡すと、 std :: string
の内容がC文字列として解釈されます。
うーん、最初の
\ 0
(ヌル文字の終端)の前の文字は ABC なので、文字列は ABC
したがって、上記の2つの間に違いは見られません。おそらくこの問題が発生しています:
std::stringstream s;
s << "hello";
s.seekp(0);
s << "b";
assert(s.str() == "b"); // will fail!
stringstreamが使用するシーケンスはまだ「hello」を含む古いシーケンスであるため、アサートは失敗します。あなたがしたことは、最初の文字を上書きすることです。これを行いたい:
std::stringstream s;
s << "hello";
s.str(""); // reset the sequence
s << "b";
assert(s.str() == "b"); // will succeed!
こちらの回答もお読みください: ostringstreamを再利用する方法
他のヒント
std :: ends
は単なるヌル文字です。伝統的に、CおよびC ++の文字列はnull(ascii 0)文字で終了しますが、 std :: string
は実際にはこのことを必要としないことがわかります。とにかく、コードをステップごとにステップスルーするために、いくつかの興味深いことが起こっています:
int main( int argc, char** argv )
{
文字列リテラル&quot; hello&quot;
は、従来のゼロで終了する文字列定数です。その全体を std :: string
HELLOにコピーします。
const std::string HELLO( "hello" );
std::stringstream testStream;
string
HELLO(末尾の0を含む)を stream
に挿入し、その後に stdの呼び出しによって2番目のnullを挿入します:: ends
。
testStream << HELLO << std::ends;
stream
に入れたもののコピー(リテラル文字列&quot; hello&quot;と2つのヌルターミネータ)を抽出します。
std::string hi = testStream.str();
次に、 std :: string
クラスの operator ==
を使用して2つの文字列を比較します。この演算子は、(おそらく) string
オブジェクトの長さを比較します-末尾のヌル文字の数を含めます。 std :: string
クラスは、基礎となる文字配列が末尾のnullで終わることを必要としないことに注意してください。文字列 hi
の一部として扱われます。
2つの文字列の末尾のヌルの数が異なるため、比較は失敗します。
if( HELLO == hi )
{
std::cout << HELLO << "==" << hi << std::endl;
}
return 0;
}
ただし、印刷または見た場合 デバッガー(VS2005)で、こんにちは、こんにちは 同一に見える、それらの.length() 事実は1だけ異なります。それが私です 推測が&quot; ==&quot;を引き起こしているオペレーター 失敗します。
理由は、長さが末尾のヌル文字1つだけ異なるためです。
私の質問は理由です。私はしません なぜstd :: endsが 目に見えない文字が文字列に追加されました こんにちは、こんにちはとこんにちはを異なるものにする 彼らが持っているにもかかわらず、長さ 同じコンテンツ。また、これ 目に見えないキャラクターは取得しません ブーストトリムでトリミング。ただし、 strcmpを使用して.c_str()を比較します 2つの文字列、比較は機能します 正しく。
strcmp
は std :: string
とは異なります-文字列がnullで終了した初期の頃から書き込まれているため、最初に達すると hi
の末尾にnullが表示されなくなります。
std :: endsを使用した理由は 最初の場所は私が問題を抱えていたからです 過去にstringstreamで ゴミデータを最後に保持する ストリーム。 std :: endsはそれを解決しました 私。
基礎となる表現を理解することをお勧めします。
std :: endsを使用して、HELLOにNULL文字を追加しています。 str()でhiを初期化すると、NULL文字が削除されます。文字列は異なります。 strcmpはstd :: stringsを比較せず、char *(C関数)を比較します。
std :: endsは、ヌルターミネータ(char) '\ 0'を追加します。非推奨のstrstreamクラスで使用して、nullターミネーターを追加します。
stringstreamでは必要ありません。実際には、nullターミネーターは「文字列を終了する特別なnullターミネーター」ではないため、事態を台無しにします。文字列ストリームに、文字列ストリームにそれはちょうど別の文字、ゼロ文字です。 stringstreamはそれを追加するだけで、文字数が(この場合)7に増え、「hello」と比較されます;失敗します。
文字列を比較する良い方法があると思うのは、 std :: find
メソッドを使用することです。 Cメソッドと std :: string ones
!