std :: endsが原因で文字列比較が失敗するのはなぜですか?

StackOverflow https://stackoverflow.com/questions/804826

  •  03-07-2019
  •  | 
  •  

質問

昨日、コードでこの問題を修正しようとして約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

を混在させないでください。
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top