C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[10]: 標準入出力をバイナリモードで扱う


(過去ログ 53 を表示中)

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■28777 / inTopicNo.1)  標準入出力をバイナリモードで扱う
  
□投稿者/ dogatana (19回)-(2008/11/30(Sun) 22:28:23)

分類:[C/C++] 

別スレッドの返答に書かれていたバイナリファイルを扱うプログラムで気になっ
たので整理も兼ねて調べてみたところ、標準入出力について、C言語ではできる
ものの、C++ではどうやってもできそうにないのですが、本質的に無理なので
しょうかね?

実際上、標準入出力をバイナリモードで使うかどうかは別としてライブリの使い
方としてどうなのかという疑問です。
実際のファイルであればios::binary指定でOKなのですが。

<試したファイル>
16進ダンプとして
 20 41 42 43 0D 0D 0A 0A 20 20 1A 41 0D 0A
というファイル。

狙いはテキストファイルの処理で出てくる
・1Aの処理・・EOFと扱われるのをスキップできるか
・0Aの処理・・出力で0D/0Aに変換されるか
・0Dの処理・・入力で無視されるか
などを確認すること。

このファイルをhogeIn.binとして
hoge < hogeIn.bin > hogeOut.bin
を実行しい、hogeOut.bin が hogeIn.bin と同じになるかどうか。

<C言語の場合>
setmode() を使用することで制御できる
int main(void)
{
    int c;
    setmode(0, O_BINARY);
    setmode(1, O_BINARY);
    while ((c = getchar()) != EOF) {
        putchar(c);
    }
    return 0;
}

<C++言語の場合>
次の3種類試したけれどいずれもうまく行かない。

プログラム1
int main()
{
    std::cin >> std::noskipws;
    std::copy(
        std::istream_iterator<char>(std::cin),
        std::istream_iterator<char>(),
        std::ostream_iterator<char>(std::cout));
}

プログラム2(以下mainの本体のみ)
    while (std::cin) {
        char    c;
        std::cin.get(c);
        std::cout.put(c);
    }
    
プログラム3
    while (std::cin) {
        char    c;
        std::cin.read(&c, 1);
        std::cout.write(&c, 1);
    }

setf()ではだめで、これ以上思いつかず・・・

引用返信 編集キー/
■28787 / inTopicNo.2)  Re[1]: 標準入出力をバイナリモードで扱う
□投稿者/ 774RR (243回)-(2008/12/01(Mon) 09:25:37)
言語規格書に (1998 版しか見てませんが) 標準入出力のモードについては記載がない
ことから、規格書的には標準入出力の動作はどっちでもいいんだと思います。

実際ウチの hpux では標準入出力は無変換(バイナリモード)ですし、この辺の事情は
Windows 系固有の話だと思われます。

ということで、どうせ処理系依存なわけで、俺はこんな風にしてます
#include "config.h"
int main() {
#if HAVE_SETMODE_AND_O_BINARY
setmode(fileno(stdin), O_BINARY);
setmode(fileno(stdout), O_BINARY);
#endif

1つ気になったんだけど istream_iterator ではまずくない?
noskipws だけでOKなんだっけ?他の変換はありえない、んだっけ?
istreambuf_iterator, ostreambuf_iterator のほうが良いと思いますです。
少なくともウチでためした範囲では圧倒的に buf_iterator のほうが高速でした。
引用返信 編集キー/
■28801 / inTopicNo.3)  Re[2]: 標準入出力をバイナリモードで扱う
□投稿者/ επιστημη (1396回)-(2008/12/01(Mon) 13:12:57)
επιστημη さんの Web サイト
> istreambuf_iterator, ostreambuf_iterator のほうが良いと思いますです。

[io]sterambuf_iteartor と copy でやってみたんだけど、CRLF⇔LF変換を除去できんかったです。

引用返信 編集キー/
■28802 / inTopicNo.4)  Re[3]: 標準入出力をバイナリモードで扱う
□投稿者/ 774RR (244回)-(2008/12/01(Mon) 13:41:32)
むう、俺は
・streambuf_iterator ではなされない変換が stream_iterator でなされる
かどうかを気にしているだけなので (特に skipws 以外の)
・streambuf_iterator で CRLF⇔LF変換 が発生するかどうか
は気にしていません。
というか Microsoft Visual C++ 6.0/2008 のライブラリ実装ソース見たので
CRLF⇔LF変換は発生して当然だと思っております。

streambuf_iterator を使えば無駄な変換チェックがスキップできるので高速になるでしょ、
ということで効率とか速度とかそっちを気にしております。

C++ 言語規格書のレベルを離れてよいのであれば、
Visual C++ の場合 setmode で回避できるよん、と言っております。
引用返信 編集キー/
■28823 / inTopicNo.5)  Re[4]: 標準入出力をバイナリモードで扱う
□投稿者/ dogatana (20回)-(2008/12/01(Mon) 20:12:47)
No28802 (774RR さん) に返信

> streambuf_iterator を使えば無駄な変換チェックがスキップできるので高速になるでしょ、
> ということで効率とか速度とかそっちを気にしております。

これは知らなかったので、勉強になりました。
ifstream -> ofstream で5MBほどのファイルをcopy()でコピーしてやると、
おおよそ5倍程度早くなりました。

> C++ 言語規格書のレベルを離れてよいのであれば、
> Visual C++ の場合 setmode で回避できるよん、と言っております。

つまり、C言語のライブラリ関数のsetmode()がC++でも有効ということですね。
で、早速試したのですが、なぜか出力ファイルは1バイト大きくなります。
最後に読み込んだデータが1バイトだけ余計に読み出される模様。

Borland C++でも同じ結果になりました。

結局、cin/coutでのバイナリ入出力(厳密には入力?)は無理だと判断します。

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

解決済み
引用返信 編集キー/
■28828 / inTopicNo.6)  Re[5]: 標準入出力をバイナリモードで扱う
□投稿者/ 774RR (245回)-(2008/12/01(Mon) 22:17:49)
最初のC++サンプル1はバグっていないのだけど
サンプル2サンプル3はバグっていて1バイト大きい結果が得られるはず。
俺ちゃんの実験結果ではそうなる。
解決済みはずしておくよ
引用返信 編集キー/
■28831 / inTopicNo.7)  Re[6]: 標準入出力をバイナリモードで扱う
□投稿者/ dogatana (21回)-(2008/12/01(Mon) 22:32:58)
No28828 (774RR さん) に返信
> 最初のC++サンプル1はバグっていないのだけど
> サンプル2サンプル3はバグっていて1バイト大きい結果が得られるはず。

お恥ずかしい。
読み出しを行って初めてEOFかどうか分かるわけで、while(cin)がまずいですね。
get()はそのままストリームを返すので、
    char c;
    while (std::cin.get(c)) {
        std::cout.put(c);
    }
ということですかね。

引用返信 編集キー/
■28832 / inTopicNo.8)  Re[7]: 標準入出力をバイナリモードで扱う
□投稿者/ 774RR (246回)-(2008/12/01(Mon) 22:53:22)
> ということですかね。
御意。
実験してないけど、それでたぶん期待通りの結果になると思う。

ついでに蛇足モードで
ストリーム系の、というか ios_base の operator! および operator void* は
fail() であって、決して eof() ではないんだよね。
ただ「文字の読み込み」が fail するのは eof のときだけだから無問題っつことで。
# operator void* であって operator bool でないのがまた味噌で
引用返信 編集キー/
■29060 / inTopicNo.9)  Re[8]: 標準入出力をバイナリモードで扱う
□投稿者/ dogatana (22回)-(2008/12/03(Wed) 23:31:41)
No28832 (774RR さん) に返信
>>ということですかね。
> 御意。
> 実験してないけど、それでたぶん期待通りの結果になると思う。
>
> ついでに蛇足モードで
> ストリーム系の、というか ios_base の operator! および operator void* は
> fail() であって、決して eof() ではないんだよね。
> ただ「文字の読み込み」が fail するのは eof のときだけだから無問題っつことで。

fstreamのときは、読み込む前にチェックが必要ですが、cin/coutではfail()はEOFと考えて良いのでしょうね。

> # operator void* であって operator bool でないのがまた味噌で

このあたりは、Accelerated C++に理由も含め書いてありました。


ということで、再度結論をば。

cin/coutでのバイナリ入出力は
・C言語のライブラリであるsetmodeを使用することで可能
・これはBorlandC++でも同様

ということになります。


解決済み
引用返信 編集キー/
■29073 / inTopicNo.10)  Re[9]: 標準入出力をバイナリモードで扱う
□投稿者/ 774RR (249回)-(2008/12/04(Thu) 09:26:41)
> fstreamのときは、読み込む前にチェックが必要ですが、cin/coutではfail()はEOFと考えて良いのでしょうね。
なんか微妙に誤解というか語弊というか、あるかもしんない

俺は「文字の読み込み」と書いた。のであって cin から読み込みとは書いていない。
int x;
while (cin>>x) cout<<x;
は、入力が整数値表現として解釈できなくなったところ(=fail) で終了だよ

ifstream f("hoge.bin", in|binary); // があるとき
char c;
while (f>>c) cout<<c; // fail まで処理するが fail するのは eof のときのみなので
// 結局 eof まで処理すると考えても無問題
引用返信 編集キー/
■29782 / inTopicNo.11)  Re[10]: 標準入出力をバイナリモードで扱う
□投稿者/ dogatana (24回)-(2008/12/14(Sun) 19:34:23)
2008/12/14(Sun) 19:36:48 編集(投稿者)

No29073 (774RR さん) に返信
>>fstreamのときは、読み込む前にチェックが必要ですが、cin/coutではfail()はEOFと考えて良いのでしょうね。
> なんか微妙に誤解というか語弊というか、あるかもしんない
>
> 俺は「文字の読み込み」と書いた。のであって cin から読み込みとは書いていない。
> int x;
> while (cin>>x) cout<<x;
> は、入力が整数値表現として解釈できなくなったところ(=fail) で終了だよ

そうですね。
脳内ではバイト単位で扱う前提で、文字(char)しかありませんでした。

しばらく見られないうちに、既に彼方に飛んでますが、解決済みにしておきます ^^;
ありがとうございました。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -