Visual C++/CLRコンソール・アプリケーションの作成
テキストファイル入出力の説明は、このウェブサイトの後半で行う。
コンソール・アプリケーションは、図6のようなコンソールウィンドウ(図6はウィンドウの枠をドラッグして小さくしてある)にテキストを出力し、キーボードから入力を行うプログラムである。このタイプのプログラムを新規に作成するときは、「ファイル|新規作成|プロジェクト」メニュを選ぶ。
図1
図1の状態で「プロジェクト(P)」を選ぶと、図2のダイアログ・ウィンドウが表示される。
図2
「プロジェクトの種類」は「Visual C++|CLR」を選び、テンプレートは「CLRコンソール アプリケーション」を選ぶ。場所のフォルダは、右の「参照」ボタンをクリックして適当なフォルダを選ぶ。「プロジェクト名」に適当なプロジェクト名(C++プログラムコード内でも用いられる)を設定すると、同じ文字列が「ソリューション名」にも表示される。この状態で「OK」ボタンをクリックすると、指定した名前のプロジェクトが作成され、コンソール・アプリケーションのひな型が図3のように表示される。
図3
ConsoleクラスのWriteLine関数は、実引数である文字列をコンソールに出力して、カーソル位置を次行の先頭に移動するものである。しかし、図3のプログラムを「F5キー」を押してビルド実行すると、コンソール・ウィンドウは表示されてすぐに消え、プログラムの実行終了となる。表示されているはずの文字列「Hello World」を見るために、キーボードからの入力待ちの状態として、プログラムがすぐに終了しないようにする。Consoleクラス型のReadLine関数は、Enterキーが押されるまでのキー入力文字を返すものであるが、これを図4のように追加する。
図4
図4のプログラムはEnterキーが押されるまで終了しない。図4のプログラムを実行するために「F5キー」を押すと、図5のウィンドウが表示される。
図5
「はい」キーをクリックするとビルドが始まり、リンク後、プログラムが実行されて、コンソール・ウィンドウが表示される(図6)。
図6
図6の状態で、Enterキーの入力待ちとなっている。Enterキーを押すと、Console::ReadLine()の終了となってプログラムは終了する。
テキストファイルからの読み込みは、System::IOにある入力ストリームと呼ばれているファイル入力用クラス型であるStreamReaderを用いる。
StreamReader ^ fin = gcnew StreamReader(fin_nm);
fin_nmはファイル名が設定されているString型のハンドルであり、次のようにConsole::ReadLine( )によって設定することができる。
Console::Write("入力データファイル= ");
String ^ fin_nm = Console::ReadLine();
ファイル名は、パス名(フォルダ名)などが設定されていないときは、プログラムソースファイルと同じフォルダにあるものとみなされる。プログラムソースファイルの置かれているフォルダがカレントフォルダである。
ファイルからの読出しはConsoleからの読み込みと同じでありReadLine()関数などによって行い、String型のハンドル「String ^ s;」に読み込んだ文字列を設定することができる。ファイルの最後まで読み込まれている時は、sにはnullptrが設定される。
s = fin->ReadLine();
テキストファイルへの書き出しは、System::IOにあるファイル出力用ストリームStreamWriterによって行う。
StreamWriter ^ fout = gcnew StreamWriter(fout_nm);
ファイルへの書き出しはWriteLine( )関数によって
fout->WriteLine(文字列);
などと行うことができる。
ファイルは、入出力が終われば関数Close()を呼び出して閉じる。出力ファイルの場合、このクローズ処理を行わずにプログラムを終了すると、WriteLine()で書き出したものが途中のバッファにとどまった状態で出力ファイルに転送されずに実行終了となることがある。
以下に、StreamReader/StreamWriterによるプログラム例を示す。
Console::Write("入力データファイル= ");
String
^ fin_nm = Console::ReadLine();
Console::Write("出力ファイル= ");
String
^ fout_nm = Console::ReadLine();
using namespace
System::IO;
StreamReader
^ fin = gcnew StreamReader(fin_nm); // 入力データファイルのオープン
StreamWriter
^ fout = gcnew StreamWriter(fout_nm); // 出力データファイルのオープン
int i = 0;
String
^ s;
while(true){
if ((s = fin->ReadLine()) != nullptr){
fout->WriteLine((++i).ToString()
+ ":
" + s);
}else{
break;
}
}
fin->Close(); // 入力データファイルのクローズ
fout->Close(); // 出力データファイルのクローズ
Console::WriteLine("Enterキーを押して終了。");
Console::ReadLine();
return 0;
実行時のコンソール画面は図1のようになる。
図1
Data_eng.txtの内容は以下のようになっている。
========== Data_eng.txtの内容 ==========
12345
abcde
========== ここまで ==========
このとき、出力ファイルtemp.txtは次のようである。
========== temp.txtの内容 ==========
1: 12345
2: abcde
========== ここまで ==========
入出力文字列に日本語文字列が含まれている箇所は文字化けが起こる。例えば、次のファイルData_jp.txtでは日本語全角文字の部分に文字化けが発生する。
========== Data_jp.txtの内容 =========
12345
abcde
12345
ABCDE
========== ここまで =========
日本語文字を扱うときは、ストリームの生成のときのコンストラクタのパラメタに「GetEncoding("shift-jis")」を追加する。この関数はSystem::Text::Encodingにあるので、
System::Text::Encoding::GetEncoding("shift-jis")
というような書き方になる。
上のサンプルプログラムを、日本語が扱えるようにしたものを以下に示す。
Console::Write("入力データファイル= ");
String
^ fin_nm = Console::ReadLine();
Console::Write("出力ファイル= ");
String
^ fout_nm = Console::ReadLine();
using namespace
System::IO;
StreamReader
^ fin = gcnew StreamReader(fin_nm,
System::Text::Encoding::GetEncoding("shift-jis"));
StreamWriter
^ fout = gcnew StreamWriter(fout_nm, false, System::Text::Encoding::GetEncoding("shift-jis"));
int i = 0;
String
^ s;
while(true){
if ((s = fin->ReadLine()) != nullptr){
fout->WriteLine((++i).ToString()
+ ":
" + s);
}else{
break;
}
}
fin->Close();
fout->Close();
Console::WriteLine("Enterキーを押して終了。");
Console::ReadLine();
return 0;
コンストラクタStreamWriterの第2パラメタfalseは、既存のファイルと同じ名前のファイルに出力するときは、既存のファイル内容を捨てて、新しく書き直すことを意味する。
ReadLine( )は、テキストファイルを1行単位で読み込むので、1行に複数のデータが書かれているときはそれらのデータを1つ1つ取り出す必要がある。そのための関数としてSeekStrを用意した。使用例を以下のプログラムに示す。
//
// pos
番目の文字列の取り出し
//
String
^ SeekStr( String ^ str, int pos ){
String
^ vstr = "";
array<wchar_t>
^ separator = { ' ', '\t', ',',' ','/' };
array<String ^> ^ ary_str;
ary_str
= str->Split( separator );
int L = ary_str->Length;
int ckp = -1;
int tmpPos = 0;
do {
if (ary_str[tmpPos]->Length > 0) ckp++;
tmpPos++;
}
while ((ckp != pos) && (tmpPos < L)
);
if (ckp == pos) vstr = ary_str[tmpPos-1];
return vstr;
}
int main(array<System::String ^> ^args)
{
Console::Write("入力データファイル= ");
String
^ fin_nm = Console::ReadLine();
Console::Write("出力データ= ");
String
^ fout_nm = Console::ReadLine();
using namespace
System::IO;
StreamReader
^ fin = gcnew StreamReader(fin_nm,
System::Text::Encoding::GetEncoding("shift-jis"));
StreamWriter
^ fout = gcnew StreamWriter(fout_nm, false, System::Text::Encoding::GetEncoding("shift-jis"));
String
^ s;
while(true){
if ((s = fin->ReadLine()) != nullptr){;
double x = double::Parse(SeekStr(s,
0));
double y = double::Parse(SeekStr(s,
1));
fout->WriteLine(x.ToString()
+ " + " + y.ToString() + " = " + (x + y).ToString());
}else{
break;
}
}
fin->Close();
fout->Close();
return 0;
上のプログラムは入力データとして以下のような形式のファイルを想定している。
=========== 入力用データファイル例 Data.txt ===============
1 2
3 4
5 6
7 8
9 10
=========== ここまで ===============
このData.txtファイルを入力ファイルとして上のプログラムを実行すると出力ファイルは以下のようになる。
=========== 出力ファイル例 ====================
1 + 2 = 3
3 + 4 = 7
5 + 6 = 11
7 + 8 = 15
9 + 10 = 19
=========== ここまで ====================
Visual C++/CLRコンソール・アプリケーションの作成法についての詳しい説明を<岡本安晴「大学生のための心理学VC++プログラミング入門」勁草書房、第0章「はじめに」>で行っている。
Win32コンソールアプリケーション/Visual C++ 2010 の解説ウェブサイトを用意した。