C言語関係掲示板

過去ログ

No.1179 int型の値を出力ファイル名に含めたい。(C++)

[戻る] [ホームページ]
No.15417

int型の値を出力ファイル名に含めたい。
投稿者---dai(2004/07/11 13:26:10)


c++で学習プログラムを書いています。
学習の途中結果を制御用の変数を用いてファイル出力させたいです。
具体的には、「中間結果 i(制御用の変数)回目.txt」
のようなファイル名にして何個かの途中結果を得たいです。

int i; //制御用の変数
char *fn = "中間結果1000回目.txt";
fout.open(fn);

↑このようにはできるのですが、
char *m
m = (char*)i;
  fout.open("中間結果"m"回目.txt");

↑このようにはできないです。
どのようにしたらいいか教えてください。
よろしくお願いします。


No.15418

Re:int型の値を出力ファイル名に含めたい。
投稿者---YuO(2004/07/11 13:54:26)


【掲示板ご利用上の注意】は読みましたか?
>※マルチポスト(多重投稿)は謹んで!
ref) Computer Planetのプログラミング掲示板。


>学習の途中結果を制御用の変数を用いてファイル出力させたいです。
>具体的には、「中間結果 i(制御用の変数)回目.txt」
>のようなファイル名にして何個かの途中結果を得たいです。

ostringstreamを使う。


>char *fn = "中間結果1000回目.txt";

C++においてchar *を文字列リテラルで初期化したり,
char *に文字列リテラルを初期化する処理は非推奨だったか廃要素だったかになっています。


> fout.open(fn);

foutって何ですか?


>char *m
>m = (char*)i;

型変換をちゃんと理解していますか?

個人的には,キャスト記法による明示的な型変換ではなく,
4つキャスト演算子を使って型変換を行った方が,
目的が少しは明示できて良いと考えています。


>  fout.open("中間結果"m"回目.txt");
>↑このようにはできないです。

当たり前です。
演算子無しで連結できるのは,文字列リテラルだけですから。



No.15421

Re:int型の値を出力ファイル名に含めたい。
投稿者---dai(2004/07/11 16:00:41)


>【掲示板ご利用上の注意】は読みましたか?
>>※マルチポスト(多重投稿)は謹んで!
>ref) Computer Planetのプログラミング掲示板。
>
すみません、解決をあせってしまい多重投稿してしまいました。
以後気をつけます。



>C++においてchar *を文字列リテラルで初期化したり,
>char *に文字列リテラルを初期化する処理は非推奨だったか廃要素だったかになっています。
>
string fn="中間結果1000回目.txt";

のようにすると良いということですか?




No.15426

Re:int型の値を出力ファイル名に含めたい。
投稿者---YuO(2004/07/11 20:50:59)


>>C++においてchar *を文字列リテラルで初期化したり,
>>char *に文字列リテラルを初期化する処理は非推奨だったか廃要素だったかになっています。
>string fn="中間結果1000回目.txt";
>のようにすると良いということですか?

違います。
const char * fn = "中間結果1000回目.txt";
のように,文字列リテラルはconst charへのポインタ型で受けるのが正しいです。
#文字列リテラルはconst charの配列だから。



No.15419

Re:int型の値を出力ファイル名に含めたい。
投稿者---かずま(2004/07/11 14:44:16)


次のどちらのほうがお好みですか?
----------------------------------------------------------------------
#include <cstdio>

    char name[32];
    std::sprintf(name, "中間結果%d回目.txt", i);
    std::ofstream fout;
    fout.open(name);

----------------------------------------------------------------------
#include <sstream>

    std::ostringstream name;
    name << "中間結果" << i << "回目.txt";
    std::ofstream fout;
    fout.open(name.str().c_str());

----------------------------------------------------------------------



No.15420

Re:int型の値を出力ファイル名に含めたい。
投稿者---ぽへぇ(2004/07/11 14:49:26)


>char *m
>m = (char*)i;
>  fout.open("中間結果"m"回目.txt");
>どのようにしたらいいか教えてください。
>よろしくお願いします。

ファイル名用のchar配列をとって、そこに sprintf




No.15422

かいけつしました。ありがとうございます。
投稿者---dai(2004/07/11 16:12:32)


ostringstream name;
ofstream fout;
name << "中間結果" << i << "回目.txt";
fout.open(name.str().c_str());
このように解決することができました。
YuOさんの書き込みをヒントに

fout.open(name.str());

と、までは行ったのですが。
coutではname.str()でだいじょぶだったのですが、
この場合はなぜname.str().c_str()のようにしなければならないのですか?
文法的な意味など
後学のために教えていただければ幸いです。
経験・知識共に乏しい者です。

皆さんありがとうございました。



No.15427

Re:かいけつしました。ありがとうございます。
投稿者---YuO(2004/07/11 21:01:56)


>fout.open(name.str());
>と、までは行ったのですが。
>coutではname.str()でだいじょぶだったのですが、
>この場合はなぜname.str().c_str()のようにしなければならないのですか?

ちゃんと調べましたか?
template <class charT, class traits, class Allocator>
    basic_string<charT, traits, Allocator> basic_ostringstream<charT, traits, Allocator>::str () const;
は上記のようにbasic_stringを返します。
そして,ヘッダ<string>には
template <class charT, class traits, class Allocator>
    operator<< (basic_ostream<charT, traits>& os,
                const basic_string<charT, traits, Allocator>& str);
が定義されているため,
std::cout << name.str();
は解決できますが,std::basic_ofstream::openは,
template <class charT, class traits>
    void basic_ofstream<charT, traits>::open (const char * s, ios::base::openmode mode);
のみです。
また,std::basic_string<charT>には,operator const T *operator T *は定義されていませんから,
fout.open(name.str());
は利用可能なメンバ関数が存在しません。



No.15434

Re:かいけつしました。ありがとうございます。
投稿者---aki(2004/07/12 00:54:58)


> std::basic_ofstream::openは,
>
> template <class charT, class traits>
> void basic_ofstream<charT, traits>::open (const char * s, ios::base::openmode mode);
> のみです。

この関数で、ファイル名を指定する時に std::string をそのまま渡すことができないのが不便に感じ
ます。なぜストリームをオープンする関数はファイル名として std::string ではなく char* を要求
するのでしょうか?


No.15436

Re:かいけつしました。ありがとうございます。
投稿者---YuO(2004/07/12 01:09:10)


>この関数で、ファイル名を指定する時に std::string をそのまま渡すことができないのが不便に感じ
>ます。なぜストリームをオープンする関数はファイル名として std::string ではなく char* を要求
>するのでしょうか?

とりあえず,std::basic_ofstream::openはstd::stringもchar *も要求していません。
const char *を要求しています。

でもって,なんでconst char *を要求するのかは,ISO/IEC JTC1/SC22/WG21の人間にでも聞いてください。
まぁ,
template <class charT, class traits> template <class traits2, class Allocator>
    void std::basic_ofstream<charT, traits>::open (const basic_string<char, traits2, Allocator>&, openmode mode);
なんてのを用意するくらいなら,最初からconst char *だけを受け付けてくれるのが有り難いですが。
#なにを要求するクラスか,一目ではわからなくなる。



No.15439

Re:かいけつしました。ありがとうございます。
投稿者---aki(2004/07/12 02:25:39)


> とりあえず,std::basic_ofstream::openはstd::stringもchar *も要求していません。
> const char *を要求しています。

そうですね。const char* でした。

> でもって,なんでconst char *を要求するのかは,ISO/IEC JTC1/SC22/WG21の人間にでも聞いてください。
>
> まぁ,template <class charT, class traits> template <class traits2, class Allocator>
> void std::basic_ofstream<charT, traits>::open (const basic_string<char, traits2, Allocator>&, openmode mode);
>
> なんてのを用意するくらいなら,最初からconst char *だけを受け付けてくれるのが有り難いですが。
> #なにを要求するクラスか,一目ではわからなくなる。

template <class charT, class traits>
void std::basic_ofstream<charT, traits>::open (const std::string& filename, openmode mode);

これよりも const char* だけを受け付ける方が有難いですか?



No.15443

Re:かいけつしました。ありがとうございます。
投稿者---YuO(2004/07/12 07:59:15)


>template <class charT, class traits>
> void std::basic_ofstream<charT, traits>::open (const std::string& filename, openmode mode);
>これよりも const char* だけを受け付ける方が有難いですか?

template <class charT, class traits>
    void std::basic_ofstream<charT, traits>::open (const basic_string<char, char_traits<char> , allocator<char>&, openmode mode);
ですか……。
わざわざ,特性をchar_traits<char>に,アロケータをallocator<char>に限定する理由はありますか?
特に,アロケータは何らかの理由で置き換えることはあり得ると思いますが。
#特性を置き換える,というのはあまり考えつかない。

相手は標準ライブラリであることをお忘れなく。
#本当に知りたいなら,作成者に聞くべきかと。


そうそう,内部でc_strとか使ったら,openに渡したbasic_stringの要素への,
  • リファレンス
  • ポインタ
  • 反復子
は全て無効になります。これも問題が大きいです。
#というか,こっちの方が問題か。



No.15450

Re:かいけつしました。ありがとうございます。
投稿者---aki(2004/07/12 19:08:23)


> わざわざ,特性をchar_traits<char>に,アロケータをallocator<char>に限定する理由はありますか?
> 特に,アロケータは何らかの理由で置き換えることはあり得ると思いますが。
> #特性を置き換える,というのはあまり考えつかない。

open 関数の仮引数の basic_string のアロケータが限定されるだけです。open 関数のユーザーは
好きなアロケータを使った basic_string にファイル名を格納し、c_str 関数を使って open 関数に
それを渡せます。

void f(const std::string& filename)
{
    std::ofstream out;
    out.open(filename);  // OK   .c_str()は不要(私の理想)
    // ...
}

typedef std::basic_string<char, std::char_traits<char>, My_allocator<char> > My_string;

void g(const My_string& filename)
{
    std::ofstream out;
    out.open(filename.c_str());  // OK   .c_str()は必要
    // ...
}

> 相手は標準ライブラリであることをお忘れなく。
> #本当に知りたいなら,作成者に聞くべきかと。

順番としてはこうだと思います。

1. 身近な掲示板などで質問。
2. 海外のニュースグループで質問。
3. ストラウストラップにメール。

> そうそう,内部でc_strとか使ったら,openに渡したbasic_stringの要素への,
>
>  ・リファレンス 
>  ・ポインタ 
>  ・反復子 
>
> は全て無効になります。これも問題が大きいです。
> #というか,こっちの方が問題か。

根拠プリーズ




No.15451

Re:かいけつしました。ありがとうございます。
投稿者---Ban(2004/07/12 19:33:49)


きっと、次期標準は boost::filesystem::fstream でリプレイスですよ、
などと言ってみるテスト。
# boost::filesystem::path 経由で渡して c_str() も不要だし、
# Boost.org だけに、あながち外してはいないかも(根拠なし)


No.15453

Re:かいけつしました。ありがとうございます。
投稿者---aki(2004/07/12 20:17:22)


> きっと、次期標準は boost::filesystem::fstream でリプレイスですよ、
> などと言ってみるテスト。
> # boost::filesystem::path 経由で渡して c_str() も不要だし、
> # Boost.org だけに、あながち外してはいないかも(根拠なし)

情報ありがとうございます。これなら満足です。

namespace boost
{
  namespace filesystem
  {
    class path
    {
    public:
      // constructors:
      path();
      path( const std::string & src );
      path( const char * src );

      // ...
    };

    // ...
  }
}

namespace boost
{
  namespace filesystem
  {
    // ...

    template < class charT, class traits = std::char_traits<charT> >
    class basic_ifstream : public std::basic_ifstream<charT,traits>
    {
    public:
      // ...
      explicit basic_ifstream( const path & file_ph, std::ios_base::openmode mode = std::ios_base::in );
      void open( const path & file_ph, std::ios_base::openmode mode = std::ios_base::in );
    };

    // ...

    template < class charT, class traits = std::char_traits<charT> >
    class basic_ofstream : public std::basic_ofstream<charT,traits>
    {
    public:
      // ...
      explicit basic_ofstream( const path & file_ph, std::ios_base::openmode mode = std::ios_base::out );
      void open( const path & file_ph, std::ios_base::openmode mode = std::ios_base::out );
    };

    // ...

    template < class charT, class traits = std::char_traits<charT> >
    class basic_fstream : public std::basic_fstream<charT,traits>
    {
    public:
      // ...
      explicit basic_fstream( const path & file_ph, std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out );
      void open( const path & file_ph, std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out );
    };
    // ...
  }
}

http://boost.cppll.jp/HEAD/libs/filesystem/doc/index.htm から




No.15455

Re:かいけつしました。ありがとうございます。
投稿者---YuO(2004/07/12 20:18:58)


> open 関数の仮引数の basic_string のアロケータが限定されるだけです。open 関数のユーザーは
> 好きなアロケータを使った basic_string にファイル名を格納し、c_str 関数を使って open 関数に
> それを渡せます。

std::basic_string<char, std::char_traits<char>, std::allocator<char> >のみを特別扱いする理由は何ですか?

標準化委員会は,恐らく特別扱いする必要性を見出さなかったのでしょう。
また,const char *std::stringに渡すと,std::bad_alloc例外が発生する恐れもあります。
それらの理由などから,const char *を受けるようにしたのだと推測します。


std::stringを受けるstd::basic_fstreamが欲しいなら,自分で作ればよいでしょう。


>> #本当に知りたいなら,作成者に聞くべきかと。
> 順番としてはこうだと思います。

規格書には一切理由は書いていません。
よって,委員会に所属していない人間に聞いたところで,
「〜〜ではないか」という答えしか返ってきません。


>> そうそう,内部でdataとかc_strとか使ったら,openに渡したbasic_stringの要素への,
>>  ・リファレンス
>>  ・ポインタ
>>  ・反復子
>> は全て無効になります。これも問題が大きいです。
>> #というか,こっちの方が問題か。
> 根拠プリーズ

ISO/IEC 14882:2003。21.3にちゃんと書いてあります。



No.15456

Re:かいけつしました。ありがとうございます。
投稿者---Ban(2004/07/12 20:38:50)



>ISO/IEC 14882:2003。21.3にちゃんと書いてあります。

すみません。それって、"21.3.6" の章の中に書いてあるんでしょうか。
それとも他の場所ですか。初耳だったもので探してみたんですが、
手持ちの版が古いせいか、それらしい記述が見つからないのですが.........
改版で追記されたものなんでしょうか。




No.15459

Re:かいけつしました。ありがとうございます。
投稿者---YuO(2004/07/12 21:07:41)


>>ISO/IEC 14882:2003。21.3にちゃんと書いてあります。
>すみません。それって、"21.3.6" の章の中に書いてあるんでしょうか。
>それとも他の場所ですか。初耳だったもので探してみたんですが、
>手持ちの版が古いせいか、それらしい記述が見つからないのですが.........

1998年版にしろ,2003年版にしろ,21.3のParagraph 5に,
basic_stringのリファレンス・ポインタ・反復子が無効になる条件が書いてあります。



No.15460

Re:かいけつしました。ありがとうございます。
投稿者---Ban(2004/07/12 21:22:39)


>1998年版にしろ,2003年版にしろ,21.3のParagraph 5に,
>basic_stringのリファレンス・ポインタ・反復子が無効になる条件が書いてあります。

- Calling date() and c_str() member functions.

確かにそちらの方に書いてありました。

参照時に内部要素を再確保しても許される(保証されない)仕様だとは
聞いていましたが、既存の要素への参照が破棄されるとまでは
認識していませんでした。

ありがとうございます。


No.15464

Re:かいけつしました。ありがとうございます。
投稿者---aki(2004/07/13 03:57:59)


> std::basic_string<char, std::char_traits<char>, std::allocator<char> >のみを特別扱いする理由は何ですか?
>
> 標準化委員会は,恐らく特別扱いする必要性を見出さなかったのでしょう。
> また,const char *std::stringに渡すと,std::bad_alloc例外が発生する恐れもあります。
> それらの理由などから,const char *を受けるようにしたのだと推測します。

std::basic_string<char, std::char_traits<char>, std::allocator<char> > は string という
typedef名が付けられて区別されているかなり特別な basic_string だと思っていたのですが... 

少し釈然としないので、もうしばらく考えてみます。

> 規格書には一切理由は書いていません。
> よって,委員会に所属していない人間に聞いたところで,
>「〜〜ではないか」という答えしか返ってきません。

委員会とは無関係な人でも、理由を知っている可能性があります。理由を委員会の人から聞
いたことがあるとか、理由が書かれてある文献(「プログラミング言語C++」など)を読ん
だことがあるといった場合です。

> ISO/IEC 14882:2003。21.3にちゃんと書いてあります。

確かにありました。これですね。失礼しました。

References, pointers, and iterators referring to the elements of a basic_string
sequence may be invalidated by the following uses of that basic_string object:

 // ...

 ― Calling data() and c_str() member functions.

 // ...



No.15474

Re:かいけつしました。ありがとうございます。
投稿者---Ban(2004/07/13 10:52:05)


> std::basic_string<char, std::char_traits<char>,
> std::allocator<char> > は string というtypedef名が
> 付けられて区別されているかなり特別な basic_string だと
> 思っていたのですが...
>少し釈然としないので、もうしばらく考えてみます。

機能的に string を特別視しているようなところは
標準内にはあまりないと思いますけど。
# STL からは単にコンテナもどきの認識で、
# stream 系も内部的には basic_string を扱う
# basic_stream が typedef されているだけですよね。

個別対応もきりがないでしょうし、上の流れで行くと
少なくとも wstring の立場がなさそうな気が...。


> 委員会とは無関係な人でも、理由を知っている可能性があります。
> 理由を委員会の人から聞いたことがあるとか、理由が書かれてある文献
> (「プログラミング言語C++」など)を読んだことがあるといった場合です。

確かに可能性はあるのでしょうが、限りなく低そうな気が....。

突っ込んだ背景が知りたいくらいの人は、そういった"バイブル的文献"は
既に自分で読んでそうですし、また読んだ方がいいと思います。
そこにも載っていないような情報が知りたいのなら後はもう、
YuO さんも書かれているように関係者に話を伺うくらいしかないのでは?
(回答があるかは別問題)

可能な限り間接情報よりは直接情報に接するのが真相に近づく最善手かと思います。