nullが埋め込まれたstd :: stringをどのように構築しますか?


88

次のような行でstd :: stringを作成したい場合:

std::string my_string("a\0b");

結果の文字列(a、null、b)に3文字を含めたい場合、1つしか取得できません。適切な構文は何ですか?


4
これには注意する必要があります。'b'を任意の数字に置き換えると、間違った文字列が黙って作成されます。参照:stackoverflow.com/questions/10220401/...
デヴィッド・ストーン

回答:


128

C ++ 14以降

リテラルを作成することができました 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 ++ 14より前

問題は、入力がC文字列でstd::stringあるconst char*と想定するコンストラクターです。C文字列は\0終了するため、\0文字に到達すると解析が停止します。

これを補うには、(C文字列ではなく)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文字列を含む内部バッファへのポインタを抽出できますc_str()

の使用に関する以下のDougTの回答も確認してくださいvector<char>

また、C ++ 14ソリューションについてはRiaDを確認してください。


6
更新:c ++ 11以降、文字列はnullで終了します。そうは言っても、ロキの投稿は引き続き有効です。
matthewaveryusa 2014

14
@mna:ストレージに関してはnullで終了ますが、意味のあるnull終了(つまり、文字列の長さを定義するセマンティクス)でnullで終了するという意味ではありません。これは、この用語の通常の意味です。
軌道上でのライトネスレース2015年

よく説明されています。ありがとうございました。
Joma 2018年

22

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文字列を使用できるのと同じ場所の多くでそれを使用できます

printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';

ただし、当然、c文字列と同じ問題が発生します。nullターミナルを忘れたり、割り当てられたスペースを超えて書き込んだりする可能性があります。


バイトを文字列にエンコードしようとしている場合(grpcバイトは文字列として格納されます)、回答で指定されているベクトルメソッドを使用します。文字列全体を構築しない通常の方法(以下を参照)ではありません 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;
AlexPunnen18年

13

私は考えていない、なぜあなたは、このようなAの事をしたいと思いますが、これを試してみてください。

std::string my_string("a\0b", 3);

1
これを行うためのあなたの懸念は何ですか?「a \ 0b」を保存する必要性に疑問を持っていますか?またはそのようなストレージのためのstd :: stringの使用に疑問を投げかけますか?後者の場合、代替案として何を提案しますか?
アンソニークランプ

3
@Constantinの場合、バイナリデータを文字列として保存していると、何か問題が発生します。それが何のために、vector<unsigned char>またはunsigned char *発明されたのかです。
Mahmoud Al-Qudsi 2012年

2
文字列のセキュリティについてもっと学ぼうとしているときに、これに出くわしました。コードをテストして、ファイル/ネットワークからテキストデータと見なされるものを読み込んでいるときにヌル文字を読み込んだ場合でも、コードが機能することを確認したかったのです。私std::stringはデータをプレーンテキストと見なす必要があることを示すために使用しますが、ハッシュ作業を行っており、すべてがヌル文字を使用して機能することを確認したいと思います。これは、ヌル文字が埋め込まれた文字列リテラルの有効な使用法のようです。
デビッドストーン

3
@DuckMaestroいいえ、そうではありません。\0UTF-8文字列内のバイトは、唯一のNULすることができます。マルチバイトでエンコードされた文字には、-\0やその他のASCII文字が含まれることはありません。
John Kugelman 2013

1
テストケースでアルゴリズムを誘発しようとしたときに、これに遭遇しました。したがって、正当な理由があります。少数ですが。
namezero 2014年

12

ユーザー定義リテラルは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"));


5

これには注意する必要があります。'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

それは私の最初の2回の印刷ステートメントであり、いくつかの非印刷文字、改行、内部メモリ内の何かが続き、それを上書きしました(そして印刷して、上書きされたことを示します)。何よりも悪いことに、これを徹底的で冗長なgcc警告でコンパイルしても、何かが間違っていることを示すものはなく、valgrindを介してプログラムを実行しても、不適切なメモリアクセスパターンについて文句を言うことはありませんでした。言い換えれば、それは最新のツールでは完全に検出できません。

これと同じ問題ははるかに単純なものstd::string("0", 100);でも発生しますが、上記の例は少しトリッキーであるため、何が問題なのかを確認するのが困難です。

幸い、C ++ 11は、初期化リスト構文を使用して問題を解決するのに適しています。これにより、文字数を指定する必要がなくなり(上記で示したように、間違って行う可能性があります)、エスケープされた数字を組み合わせる必要がなくなります。std::string str({'a', '\0', 'b'})charサイズの配列をとるバージョンとは異なり、文字列コンテンツに対して安全です。


2
この投稿の準備の一環として、これを少し安全にするための警告が追加されることを期待して、バグレポートをgccに送信しました:gcc.gnu.org/bugzilla/show_bug.cgi?id
David Stone

4

C ++ 14では、リテラルを使用できるようになりました

using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3

1
そして2行目は、代わりに、よりうまくとして、私見、書き込むことができるauto s{"a\0b"s};
underscore_d

いい答えありがとう。
Joma 2018年


1

匿名の答えは素晴らしいですが、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)

もちろん、プロジェクトではどちらか一方のソリューションのみを使用し、適切と思われるソリューションと呼ぶ必要があります。


-5

この質問がなされるのは久しぶりです。しかし、同様の問題を抱えている人は、次のコードに興味があるかもしれません。

CComBSTR(20,"mystring1\0mystring2\0")

この回答はMicrosoftプラットフォームに固有のものであり、元の質問(std :: stringについて尋ねたもの)には対応していません。
6月ロードス

-8

std :: stringのほとんどすべての実装はnullで終了するため、おそらくこれを行うべきではありません。自動ヌルターミネータ(a、null、b、null)があるため、「a \ 0b」は実際には4文字の長さであることに注意してください。本当にこれを実行してstd :: stringのコントラクトを解除したい場合は、次のことができます。

std::string s("aab");
s.at(1) = '\0';

しかし、そうすると、友達全員があなたを笑い、本当の幸せを見つけることはできません。


1
std :: stringはNULLで終了する必要はありません。
マーティンヨーク

2
必須ではありませんが、ほとんどすべての実装では、おそらくc_str()アクセサーがnullで終了する同等のものを提供する必要があるためです。
ジャーニー2008年

2
効率を上げるために、データバッファの背面にヌル文字保持することできます。ただし、文字列に対する操作(つまり、メソッド)はいずれもこの知識を使用せず、NULL文字を含む文字列の影響を受けません。NULL文字は、他の文字とまったく同じ方法で操作されます。
マーティンヨーク

これが、文字列がstd ::-の動作がどのプラットフォームでも定義されていないほど面白い理由です。

user595447がまだここにいて、いったい何について話していると思っているのかを尋ねられるようにしたいと思います。
underscore_d
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.