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

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

ただいまの
回答率

90.52%

  • C++

    3435questions

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

  • JSON

    1146questions

    JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

C++でファイルを保存せずに閉じる

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 653

jnst

score 26

実現したいこと

あるオブジェクトを読み込んで内容をJSONファイルに出力する処理を書いています。
ある条件になったときはファイルに保存せずに終了したいのですがどのようなやり方があるでしょうか。

該当のソースコード

FILE* fp;
char buf[512];

fp = fopen(path.c_str(), "wb");
FileWriteStream ws(fp, buf, sizeof(buf));
PrettyWriter<FileWriteStream> writer(ws);

for (Object o : objects)
{
  // 書き込む処理
}

fclose(fp);

試したこと

現状は fopen する前にあらかじめ for 文で objects をすべて走査し、該当する条件になったときは return してしまうコードにしています。
このやり方だと objects を2回走査しなければならないためスマートではありません。
他には該当する条件になったときはファイルを保存した後に削除してしまうという方法もあると思いますが、これも余計なことをしていますよね。
そもそもファイルを保存したくないのです。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+6

こんにちは。

std::stringstreamへ出力しておいて、必要な時にファイルへ書き出すのではどうでしょう?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/03/01 13:45

    FileWriteStreamを最初から使うのではなく、std::stringstream で一度扱い、最後に必要だったら FileWriterStream に変換してファイル保存を行う、というアプローチが本来やりたかったことのように思います。
    ストリーム処理は経験が少なく調べても使えそうなのが見つからないため、サンプルコードを示していただけないでしょうか?

    キャンセル

  • 2017/03/01 14:06

    FileWriteStreamを使ったことがないので分からないです。
    C++対応のライブラリなら、std::ostreamへの出力をサポートしていることが一般的と思うのですが。
    もし、お使いのライブラリがRapidJSONでしたら、std::ostreamをサポートしてなさそうですね。

    http://qiita.com/k2ymg/items/eef3b15eaa27a89353ab
    を見ると、メモリへの出力としてStringBufferStreamをサポートしていると言うことですので、そこへ出力後、その内容を文字列として取り出してファイルへ書き込めば良いと思います。

    キャンセル

checkベストアンサー

0

現状の作りで正しいと思います。
取りうる選択肢は以下の2つだけです。
・現状の形を維持する
・正常なデータのみが書き込み処理に到達するようにする

1回チェックしてから書き込むので普通ですから、物凄くピーキーな処理以外ではあまり気にしない方が良いと思います。物理的に書き込む時間とインメモリをチェックする時間には大きな開きがありますから。

fopenとか生書きで直後にobjectを走査するコードがあるとしたら、走査回数を気にするよりよほど問題だと思います。

質問の要件で、僕の場合のC++における理想のコードは以下のような形です。
(以下Embarcadero社製のC++Builderを使っているコードなので違和感があるかもしれませんが)

void WriteObjectsToJson(String path, const std::vector<Object> &objects)
{
    auto result = Validator::Validate(objects);
    if (!result.Success) return;

    std::unique_ptr<Stream> stream(new TFileStream(path, fsCreate));
    JsonSerializer::Serialize(objects, stream);
}

この際のValidationするクラスや、シリアライズするクラスは適当です。
クラスじゃなくてメソッドでもいい。ただこういう呼び出しの組み合わせで機能を実現する、ということです。

ここで説明したいのは、この時、「2回全体の走査が行われる」とかを一切気にしないということです。
されるのが当たり前だと考えているのもあるし、移譲された処理の先で何が起きているか考えること自体が少し違うかなと考えているからです。
それに上記の処理には再利用性があります。
例えば、TCP:IP通信でJson返したいんだよね、とか、WebのレスポンスにJson入れたいんだよね、という時には、ファイルストリームではなく、そちらも大体Stream関連のクラスが用意されているもので、わずかな手間で結果の出力先を変えることができます。
Validation関連の処理は書き込み前にも使えますが、フロントエンドでUI上で簡易なエラー検査処理を行う際にも使えます。(サービスとクライアントが分かれている場合、検査が2回行われるのはありえる。)

fopenの前にやってるobjectsの走査処理と、書き込み操作処理を合わせるというのは、この完全に分割できる二つの処理を、なんとか混ぜあわせて、処理回数を削ろうとしているのに等しいです。
ストリームに一旦書き込めば1個のループに書き込みつつ、ファイルに書き込まないということはできます。しかし、それはつまり書き込む前に一旦メモリに全部書き出すということで今以上にリソースの観点からすればスマートではないやり方になると思います。ストリームがファイルストリームなら結局ファイルができてしまいますし。
ストリームにする最大の利点は、どのストリームに書き込むかを選択できることですからね。

現在言えることは、検査と書き込みを合体することはオススメできません。

いやいや、ものすごくピーキーなんだよ!データ数も物凄い量なんだよ!この処理は!
という時は、異常なデータが作られるのはどこなのか、どこでその間違いを防げるのか、そういうところを探し、そこで検査を行って、書き込み処理に来るデータを正常なものだけになるようにしましょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/03/01 13:43

    良いプロダクトコードにするためのアプローチとして回答内容は100%同意します。
    ですが、知りたかったことはまさに本当に物凄い量のデータ量だった場合でも効率良く書ける方法はないか、というところでした。
    現実問題として必要はないのですけどね。

    キャンセル

  • 2017/03/01 13:56

    ・文字列ストリーム(≒メモリストリーム)に一旦書くとメモリの使用量が書き込む分必要になること
    ・C++で普通にオブジェクトの配列を操作するんだったら、走査そのものが書き込む処理やシリアライズよりずっと早いこと

    から、改善する方向を現情報から示すのは難しいです。

    一括取得するとあまりにもオブジェクトが巨大である場合やメモリの圧迫が許されない場合には、オブジェクトを少しずつ受け取り、少しずつ処理して、一時ファイルに書き込み、最後にリネームします。エラーがある場合は削除します。

    キャンセル

  • 2017/03/01 13:57

    結局ディスクスペースとメモリスペースとCPUのコスト比なので。その際、オブジェクトの走査にかかるCPUコストはあんまり考えなくていいのではないか、そこがボトルネックってことはないんじゃないか、と思います。

    キャンセル

  • 2017/03/01 14:50

    仰る通りですね。効率的な、という観点だと一番最初にご教示いただいた内容が解になりそうです。

    キャンセル

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

  • ただいまの回答率 90.52%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    ファイルから読み込み集計する方法

    A:100 B:200 C:300 A:130 B:210 C:330 のようなファイルを読み込み合計を出力するにはどうしたらいいでしょうか? A:230 B:410 C

  • 解決済

    C言語について

    C言語についての質問があります。 C言語で scanf()がありますが、 char buf[32]; scanf("%s", buf); 通常は上記のように作ると思

  • 解決済

    文字列挿入

    いつもお世話になっています。プログC初心者です。文字列について質問させて頂きます。 [Wakamatsu] 0 Aizu [Aizu][Wakamatsu] 11 youn

  • 受付中

    c言語の課題に悩んでいます

    前提・実現したいこと 出力したファイル(a.txt)を読み込み、各行の合計値をファイル(b.txt)へ出力するプログラムを作成したいのですが上手くいきません。どなたか下記のプログ

  • 解決済

    [c]一つのデータでカラム数が変わるデータの読み込み

    質問失礼します。プログラム初心者です。 以下のような〜〜〜.datを fp1 = fopen(fname1,"r"); while((ret = fscanf(fp1,"%d %d

  • 解決済

    あと少しなんですがうまくいきません、、、

    C C言語で 各学生の学籍番号・物理の点数・化学の点数の組がスペース区切りで一行に記述されたファイルを読み込み,全学生の物理の平均点・最高点・最低点,および化学の平均点・最高点・最

  • 解決済

    Swift ラベルの座標を動的にずらす方法

    Swift3にて、UILabelを縦に並べて表示させた場合に、文字列が多いなどで改行させてラベルの高さを動的に可変させた場合に、それ以降に並んだラベルの座標位置を自動的にずらす方法

  • 解決済

    マルチバイト文字のchar*をvector<char*>に変換したい

     前提・実現したいこと マルチバイト文字が格納されているchar*型の文字列をvector<char*>に変換したいです。 char* String = "abcあいうえお"

同じタグがついた質問を見る

  • C++

    3435questions

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

  • JSON

    1146questions

    JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。