質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

4回答

1674閲覧

大きなstringデータをコストをかけずに連結できるのか?

退会済みユーザー

退会済みユーザー

総合スコア0

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

2クリップ

投稿2022/10/04 22:54

編集2022/10/04 23:12

大きな画像データを以下のように読み込んでBMPの文字列に変換して、
別の文字列に連結したのですが。
そうすると、連結後も大きなメモリが確保されると思います。
これをあまりコピーを発生させずに、メモリを余計に消費せずに行いたいのですが、
自分では、これ以上は難しいです。
何かアドバイスあればいただきたいです。

環境はVisualStudio 2019、c++17

※画像データは、strを作成した以降は他では使用しません。

C++

1cv::Mat img = imread("sample.jpg"); // 大きな画像 2vector<uchar> data; 3cv::imencode(".bmp", img, data); 4string str_data(data.begin(), data.end()); 5 6std;;string str = "文字列" + str_data + "文字列";

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2022/10/04 23:05

> これをあまりコピーを発生させずに、メモリを余計に消費せずに行いたいのですが そこまでシビアにメモリ容量を考えるということは、組み込みやモバイルなどリソースの限られた環境なのでしょうか。
退会済みユーザー

退会済みユーザー

2022/10/04 23:09

コメントありがとうございます。 パソコンですが、非同期で複数枚画像を同じようにコピーするので、なるべく省エネで行いたいのです。
guest

回答4

0

ベストアンサー

※画像データは、strを作成した以降は他では使用しません。

でしたら、strappendしていってはいかがでしょうか。イテレーターもappendの引数にできます。

C++

1std::string str = "文字列" 2str.append(data.begin(), data.end()); 3str.append("文字列");

投稿2022/10/04 23:08

編集2022/11/04 15:32
maisumakun

総合スコア145184

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2022/10/04 23:15

あとは、str_dataの宣言と使う部分だけブロックにくくって、ブロック終了時点で変数破棄、のような手段も考えられます。
退会済みユーザー

退会済みユーザー

2022/10/04 23:36

appendをあまり使用したことがなかったのですが、これは、元の文字列に追加で、 普通に+連結すると新しい文字列を作成するという認識であっていますか? イテレータをそのまま引数にできるのは大きいです。ありがとうございます!
maisumakun

2022/10/04 23:46

> appendをあまり使用したことがなかったのですが、これは、元の文字列に追加で、 普通に+連結すると新しい文字列を作成するという認識であっていますか? そうですね、a+bとした場合、aともbとも違うインスタンスが作成されますが、a.append(b)ではaを破壊的に書き換えます。
退会済みユーザー

退会済みユーザー

2022/10/05 09:16

ありがとうございます。appendだと、上書き?破棄後に作成されるというkとですね。
yominet

2022/10/06 01:59

appendにブレイクポイントをおき、タスクマネージャーでメモリ、ワーキングセット、ピークワーキングセットあたりの増減を確認するのが良いと思います。
guest

0

maisumakunさんの回答と、ほぼ同じなのですが、あらかじめreserveで内部のバッファを確保しておかないと、appendの時にメモリを再確保してコピーしなおしてしまう可能性があります。

C++

1std::string str; 2str.reserve(最初の文字列の長さ + data.size() + 二つ目の文字列の長さ); 3str.append("文字列"); 4str.append(data.begin(), data.end()); 5str.append("文字列");

投稿2022/10/05 02:54

Bearded-Ockham

総合スコア430

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/10/05 09:17

ありがとうございます!参考になります。
guest

0

文字列連結をしてしまうとコピーは避けられないので、文字列のリストとして保持しておいて出力するときにループするのはいかがでしょうか。

c++

1std::vector<std::string> str_list; 2str_list.push_back("文字列"); 3str_list.push_back(std::move(str_data)); 4str_list.push_back("文字列"); 5 6... 7 8for (const auto& str : str_list) { 9 // str をどこかに出力 10}

std::string 1つだけを受け取るような関数に渡したい場合は連結するしかないので、どうしようもないですね。

投稿2022/10/04 23:10

int32_t

総合スコア20884

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/10/04 23:42

おっしゃる通り最後に引数に渡すので、この方法は難しいのですが考え方として参考になりました。 ありがとうございます。
guest

0

C/C++であれば、ポインターが使えますので、連結する代わりに分割されている領域のアドレス(&data)を配列(かリスト)に追加しておいて、利用する際にその配列(リスト)からアドレスを参照するというのはいかがでしょう?

配列やリストを作ったとしても、アドレスを保持する程度のものなので、コストも性能的にも問題ないように思います。
データを引数に渡すとの事ですが、連結したデータを返す関数(メソッド)を定義して、その関数へのポインターを引数として渡すという方法もあるかと思います。
(Cのsort関数のパラメータなどが参考になります)

当然、アドレスを保持するデータ領域は、alloc()などで確保しておき、内容が変更されない事を保証する必要はあります。(使い終えたらfree()で解放も必要)

後、私が使っていた時代と今は異なる可能性があるので、無視して頂いて構わないのですが、
バイナリーデータを読み込んで格納する場合は、stringではなくchar(byte)なのでは?と。
string型はあくまでも「文字列」型のため、unicodeなどのマルチバイト文字を「1文字」として扱うように思います。一方画像データは1バイトずつのデータの塊です。
本来1バイトで済むデータをstring型に入れると、それだけ余剰がでてしまうのでは?と懸念します。
(stringが4バイト使うなら、本来5バイトのデータをstring型に格納すると、lengthは5になるが、使う領域は20バイトになる)
char型は最初から1バイトですから、5バイトで長さ5の配列であっても使う領域は5バイトのままかと。
(古い知識なので、間違っていたら申し訳ありません)

投稿2022/10/05 02:25

編集2022/10/05 03:00
San

総合スコア8

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

int32_t

2022/10/05 02:34 編集

> それだけ余剰がでてしまうのでは?と懸念します。 それは間違いです。std:string は単純にcharの列を保持していて、バイナリを入れてもとくに不要なメモリを食うことはありません。バイナリを扱うことにもよく使われます。 1バイトに入らない文字を扱うものは std::wstring、std::u16string、std::u32string などです。
San

2022/10/05 02:36

そうでしたか。 では、その部分は削除しておきますね。
退会済みユーザー

退会済みユーザー

2022/10/05 09:15

ありがとうございます! これは、ある領域をあらかじめ確保しておいて、連結したい場所のアドレスをstringで参照して文字を入れていく感じでしょうか?
San

2022/10/05 10:43

そうですね。 連結したいそれぞれのデータ(”文字列", data等)を、その大きさの領域をデータを読み込む度に確保し、 その先頭アドレス(&str, &data)をリストに格納しておくという考え方です。 使う(連結)する場合は、リストからそのアドレスを順次参照し、使う領域へコピーするなり、ストリーミングするなりしていく感じでしょうか。 どこかへwrite()するだけであれば、新たに領域を作らずに確保した領域のみが使われるので、節約可能と思います。 ただ、imencodeで得られるデータがどのような物かわからないのと、"文字列"というのが具体的に何なのかを把握していないので、見当はずれの事を言っているかもしれませんが…。 (BMPに変換した文字列というのは、printable:テキスト表示可能なもの?とか、その辺りの根本的な事を理解せず口を挟んでしまいました) ですが無駄な領域を作りたくないという事でしたので、自前で領域管理をする事で極力無駄は削げ落とせるはずです。 alloc(), free()を使えるのはC/C++の利点でもあるので、上手く活用すれば節約できると思います。 またC++であれば、それらのデータ(アドレスのリスト含む)と、結合する為のメソッドを持ったクラスを定義し、そのインスタンスを関数の引数とする事で、データとgetterがセットで渡せますので、何とかなるのでは?と思いました。
退会済みユーザー

退会済みユーザー

2022/10/05 23:18

一部の文字列が、連結直前までサイズが分からないため、あらかじめ領域を取るのが難しく、 こちらの方法が取れませんでしたが非常に勉強になりました。 ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問