火曜日, 1月 15, 2008

C++解説2(ストリーム)

1.streamのイメージを掴む(ストリーム)
C++ではstreamという考えた方が基本である。
streamとはかなり高機能の入出力クラスです。
おなじみにプログラム「Hello World!」を書いてみます
---------------------------------------------------------------------
#include

using namespace std;

int main(void) {
cout << "Hello World!\n"; return 0; } --------------------------------------------------------------------- 「using namespace std;」はおまじないだと思ってここでは無視して下さい。 いずれ解説をします。 何処が違うかは分かったと思います。 「cout」ってのが「printf」に相当するものであるって理解で十分です。 これだけだと何で変更する必要があるのかって事が今一ですよね。 「cout」の最大の特徴は「printf」のように「%d,%s,%c」などと言った 出力する型の指定を行わなくて良いところです。 どんな型であれ、「<<」と使用する事でストリームに出力されます。 --------------------------------------------------------------------- char aName[32]; printf("What your first name?\n"); scanf("%s", aName); printf("My name is %s", aName); --------------------------------------------------------------------- だったり --------------------------------------------------------------------- char c; printf("What do you like alphabet?\n"); scanf("%c", c); printf("I like spell %c, too", c); --------------------------------------------------------------------- します。 ここで何が言いたいかと言えば、printfは出力する変数の型を 指定する必要があるという事です。 当たり前のようでいて、とても頂けない文法だと思いませんか? その点、C++のストリームは何も考えずに出力できます。 2.何でも流し込め!(ストリーム) ストリームに流しこむイメージは掴めたと思います。 こうなると何でもかんでも流し込みたいのが人間の嵯峨というものです。 ※私だけでしょうか? という事でいろいろと流し込んでみます。 最初に思いつくのは、ファイルでしょうね。 C言語には「fprintf」という関数があります。 ファイルポインタを指定して、出力先を指定すれば 後は「printf」と何一つ違いがない関数です。 この話の流れはファイルに出力する方法も同じって事です。 C言語のプログラムとC++のプログラムを比較しながら見てみます。 --------------------------------------------------------------------- C言語 --------------------------------------------------------------------- #include

int main(void) {
FILE *fp;

char c1 = 'a';
char c2 = 'b';
char a1[] = { "cdefg" };
char a2[] = { "hijkl" };
int i = 1;
long l = 2;

fp = fopen("sample.txt", "w");
if (fp == NULL) {
printf("File Open Error!\n");
return -1;
}

fprintf(fp, "%c", c1);
fputc(c2, fp);

fprintf(fp, "%s", a1);
fputs(a2, fp);

fprintf(fp, "%d", i);

fprintf(fp, "%ld", l);

fclose(fp);

return 0;
}

---------------------------------------------------------------------
C++
---------------------------------------------------------------------

#include

using namespace std;

int main(void) {
ofstream fout;

char c1 = 'a';
char c2 = 'b';
char a1[] = { "cdefg" };
char a2[] = { "hijkl" };
int i = 1;
long l = 2;

fout.open("sample.txt");
if (!fout) {
printf("File Open Error!\n");
return -1;
}

fout <<>というヘッダをincludeする必要があります。
以下にマニピュレータの一覧を示します。(抜けがあるかもしれません)

マニピュレータ  概要
endl 改行文字を出力し、ストリームをフラッシュする
ends ヌルを出力
flush バッファをフラッシュする
ws 先頭から連続しているspaceを破棄する
setiosflags(fmtflags) 引数に指定したフラグを全て有効とする
resetiosflags(fmtflags) 引数に指定したフラグを全て無効とする
setw(int) 次に出力値について出力幅の最低サイズを指定する
setfill(char) 出力幅に満たない場合、不足分を指定した文字で補う
setbase(int) 数値型を出力する場合、基数を同時に出力する。
設定できる値は「8」「10」「16」の何れかとなる。
setprecision(int) 実数で定義されている変数に対して精度の桁数を指定
boolalpha bool値の入出力にtrue、falseの使用を有効にする
showbase 数値型を出力する場合、基数を同時に出力する事を有効にする
showpoint 実数の出力時に小数点の出力を有効とする
showpos 数値出力時に先頭に符号出力を有効とする
skipws stream入力時の先頭から連続しているspaceを破棄するのを有効にする
unitbuf 挿入操作の度にバッファをflushするのを有効にする
uppercase 基数の文字xを大文字出力するのを有効にする
dec 10進数出力
hex 16進数出力
oct 8進数出力
fixed 実数を通常表記で出力する
scientific 実数を科学技術表記で出力する
internal 出力幅が満たされるまで数値の追加が行われる
right 出力を右寄せにする
left 出力を左寄せにする
noboolalpha bool値の入出力にtrue、falseの使用を無効にする
noshowbase 数値型を出力する場合、基数を同時に出力する事を無効にする
noshowpoint 実数の出力時に小数点の出力を無効とする
noshowpos 数値出力時に先頭に符号出力を無効とする
noskipws stream入力時の先頭から連続しているspaceを破棄するのを無効にする
nounitbuf 挿入操作の度にバッファをflushするのを無効にする
nouppercase 基数の文字xを大文字出力するのを無効にする


C++のマニピュレータを使用するとどのように書かれるのか例を示します。

---------------------------------------------------------------------
プログラム:setw(int)
---------------------------------------------------------------------
char a1[] = { "abcde" };
cout << "<" <<>" <<>
---------------------------------------------------------------------

---------------------------------------------------------------------
プログラム:setfill(char)
---------------------------------------------------------------------
char a1[] = { "abcde" };
cout << "<" <<>" <<>
---------------------------------------------------------------------

---------------------------------------------------------------------
プログラム:setbase(int)
---------------------------------------------------------------------
int n = 10;
cout << d1 =" 3.14159;" d2 =" 3.14;">\n";
cout << "<" <<>\n";
cout << "<" <<>\n";

---------------------------------------------------------------------
出力結果
---------------------------------------------------------------------
<3.1,3.1>
<3.142,3.14>
<3.14159,3.14>
---------------------------------------------------------------------

---------------------------------------------------------------------
プログラム:internal/right/left
---------------------------------------------------------------------
int n = 10;
cout << "<" <<>" <<>" <<>" <<>
<10>
<>
---------------------------------------------------------------------

---------------------------------------------------------------------
プログラム:endl
---------------------------------------------------------------------
cout << "abcde" << n =" 11;" b =" true;" b=" << boolalpha << b << endl; cout << " b=" << noboolalpha << b << endl; --------------------------------------------------------------------- 出力結果 --------------------------------------------------------------------- b=true; b=1; --------------------------------------------------------------------- --------------------------------------------------------------------- プログラム:showbase/noshowbase --------------------------------------------------------------------- int n = 1; cout << showbase << hex << n << " d1 =" 3.14159;" d2 =" 1;" i =" 1;" l =" 2;" f =" 3.1;" d =" 4.2;">> skipws >> c1 >> c2 >> c3;
cout <<>> noskipws >> c1 >> c2 >> c3;
cout << c1 << ":" c2 << ":" c3 << endl;

---------------------------------------------------------------------
出力結果
---------------------------------------------------------------------
a:b:c
: :a
---------------------------------------------------------------------

---------------------------------------------------------------------
プログラム:unitbuf/nounitbuf
---------------------------------------------------------------------
ofstream outfile ("test.txt");
outfile << unitbuf << "test";
outfile.close();

---------------------------------------------------------------------
補足
---------------------------------------------------------------------
これは出力結果がnounitbufでも何も変わりません
fflushを駆使してプログラムを組んだ事があると
使用局面が分かると思いますが、私なりの考えを補足します。
ファイルを例に取って説明をします。
ファイルにデータを出力する場合「<<」という演算子を使用して
出力を行うが、厳密に言うとその命令文が完了しても
実際にはファイルに内容の出力は行われていない。
その度にファイルに出力していては、パフォーマンス劣化が激しいため
バッファに蓄えれている。
ファイルがclose、フラッシュ、バッファ使い切りなどのイベントが
発生すると実際にファイルへ出力されている。
今回説明している「unitbuf」というマニピュレーションは
毎回フラッシュするように定義する操作となる。
これは基本的にはパフォーマンス劣化に繋がるため
「nounitbuf」としておくのが通常である。
---------------------------------------------------------------------

---------------------------------------------------------------------
プログラム:uppercase/nouppercase
---------------------------------------------------------------------
int n = 11;

cout << showbase << hex << uppercase << n << endl;
cout << showbase << hex << nouppercase << n << endl;

---------------------------------------------------------------------
実行結果
---------------------------------------------------------------------
0XB
0xb
---------------------------------------------------------------------



最後に「setiosflags」と「resetiosflags」について記述します。
実は今まで書いてきた例はフラグを直接操作するもので
ほとんどの内容は「setiosflags」で行えます。

例えば以下の例を考えてみます。
---------------------------------------------------------------------
例1)1~9までの数値を同じ幅で符号付きで出力する
---------------------------------------------------------------------
// 直接フラグ操作
cout << "直接フラグ操作" << endl;
cout << showpos << internal;
for(int n = 1; n != 10; n++) {
cout << setw(4) << n << endl;
}

// setiosflagsを使用
cout << "setiosflagsを使用" << endl;
cout << setiosflags(ios::showpos | ios::internal);
for(int n = 1; n != 10; n++) {
cout << setw(4) << n << endl;
}
---------------------------------------------------------------------
実行結果
---------------------------------------------------------------------
直接フラグ操作
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
setiosflagsを使用
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
---------------------------------------------------------------------

---------------------------------------------------------------------
例2)1~9までの実数を同じ幅で符号付きで出力する
---------------------------------------------------------------------
// 直接フラグ操作
cout << "直接フラグ操作" << endl;
cout << showpos << internal << showpoint;
for(float f = 1.0; f != 10.0; f++) {
cout << setprecision(2) << setw(4) << f << endl;
}

// setiosflagsを使用
cout << "setiosflagsを使用" << endl;
cout << setiosflags(ios::showpos | ios::internal | ios::showpoint);
for(float f = 1.0; f != 10.0; f++) {
cout << setprecision(2) << setw(4) << f << endl;
}
---------------------------------------------------------------------
実行結果
---------------------------------------------------------------------
// 直接フラグ操作
cout << "直接フラグ操作" << endl;
cout << showpos << internal << showpoint;
for(float f = 1.0; f != 10.0; f++) {
cout << setprecision(2) << setw(4) << f << endl;
}

// setiosflagsを使用
cout << "setiosflagsを使用" << endl;
cout << setiosflags(ios::showpos | ios::internal | ios::showpoint);
for(float f = 1.0; f != 10.0; f++) {
cout << setprecision(2) << setw(4) << f << endl;
}
---------------------------------------------------------------------

まったく同じ結果が得られます。
どちらが良いという事はないので、自分の好みに合う方を使って下さい。

0 件のコメント: