.NET FrameworkではConsoleクラスのメソッドを使うことで自プロセスの標準入力(STDIN)や標準出力(STDOUT)などの標準ストリーム(standard streams)に対する入出力操作を行うことができるようになっています。 Console.WriteLine等のメソッドで標準出力への書き込みConsole.ReadLine等のメソッドで標準入力からの読み込みが行えます。 標準エラーへの出力にはConsole.Errorプロパティを使用します。

ファイルに対する入出力を行うStream派生クラスとしてFileStreamクラスが存在しますが、.NET Frameworkには標準ストリームに直接対応するStream派生クラスは公開されていません。 そのかわりに、Console.OpenStandardOutputなどのメソッドを使って標準ストリームに対応するStreamを取得できるため、ファイルと同様に標準ストリームを扱うこともできるようになっています。

また、標準ストリームをリダイレクトして入出力先をファイルなどに変更することもできます。

なお、子プロセスを起動してそのプロセスの標準ストリームを操作する方法については子プロセスの標準入出力で解説しています。

§1 標準ストリームに対する読み込み・書き込み

標準ストリームに対する読み込み・書き込みは、Consoleクラスに用意されている静的メソッドを呼び出すことで行うことができます。

§1.1 標準出力への書き込み

ConsoleクラスのメソッドWrite, WriteLineなどを使うことで標準出力への書き込みを行うことができます。 実際の出力先はコンソールウィンドウ、リダイレクトされたファイルやIDEの出力ペインなどとなり、アプリケーションの種類や実行環境によって変わります。

Console.Write・WriteLineメソッドで標準出力に文字列を書き込む
using System;

class Sample {
  static void Main()
  {
    Console.Write("Hello, ");    // 改行なしで標準出力に書き込む
    Console.WriteLine("world!"); // 改行付きで標準出力に書き込む
    Console.WriteLine("Hello, world!");
  }
}
実行結果
Hello, world!
Hello, world!

Write, WriteLineメソッドでは書式を指定した書き込みもできるようになっています。

Console.WriteLineメソッドで書式を指定して数値・日時などの値を標準出力に書き込む
using System;

class Sample {
  static void Main()
  {
    int intValue = 42;
    double doubleValue = 3.141592654;
    DateTime dateTimeValue = new DateTime(2013, 8, 24, 1, 2, 3);

    // int double, DateTimeの値をそれぞれ異なる書式で出力する
    Console.WriteLine("{0:D4} {1:N3} {2:hh:ss}", intValue, doubleValue, dateTimeValue);
  }
}
実行結果
0042 3.142 01:03

書式を指定した書き込みについては書式指定子で解説しています。

§1.2 標準入力からの読み込み

ReadLineメソッドを使うことで標準入力から1行分の文字列を読み込むことができます。 ReadLineメソッドの戻り値には改行文字は含まれないため、末尾の改行文字を取り除くchomp/chopのような操作は不要です。

Console.ReadLineメソッドで標準入力から文字列を読み込む
using System;

class Sample {
  static void Main()
  {
    Console.WriteLine("何か入力してください");

    // 標準入力から一行読み込む
    string text = Console.ReadLine();

    // 標準入力から読み込んだ文字列を標準出力に書き込む
    Console.WriteLine("'{0}'と入力されました", text);
  }
}
キー入力を使った場合の実行例
>sample.exe
何か入力してください
Hello, world!
'Hello, world!'と入力されました

>

コマンドプロンプトで実行した場合(標準入力がターミナルに接続されている場合)は、ReadLineメソッドはエンターキーが押されるまで待機します。 標準入力がパイプ等に接続されている場合は、その接続先から1行分読み込んだ時点で処理が戻ります。


ReadLineメソッドではCR/LF/CRLFのいずれかを改行として扱います。 リダイレクトパイプで接続された標準入力から読み込める文字列がなくなった場合や、コンソールアプリケーションにおいてCTRL+Z(LinuxではCTRL+D)によって入力を中断した場合、ReadLineメソッドはnull/Nothingを返します。

Console.ReadLineメソッドで標準入力から読み込める内容がなくなるまで読み込み、読み込んだ内容に行番号を付けて標準出力に書き込む
using System;

class Sample {
  static void Main()
  {
    int lineNumber = 0;

    while (true) { // 無限ループ
      // 標準入力から一行読み込む
      string line = Console.ReadLine();

      if (line == null) {
        // 読み込める文字列が無くなった
        break;
      }
      else {
        // 読み込んだ文字列に行番号を付けて標準出力に書き込む
        lineNumber++;
        Console.WriteLine("{0}: {1}", lineNumber, line);
      }
    }
  }
}
キー入力を使った場合の実行例
>sample.exe
line1
1: line1
line2
2: line2
line3
3: line3
^Z
パイプを使った場合の実行例
>type sample.txt
line1
line2
line3

>type sample.txt | sample.exe
1: line1
2: line2
3: line3

コンソールアプリケーションではCTRL+Cを押下することでアプリケーションが中断します。 CTRL+Cが押下されたときの動作を変更したい場合は、Console.CancelKeyPressイベントにイベントハンドラを設定することで動作を定義することができます。

コマンドラインでのリダイレクト・パイプと標準入出力との接続についてはMS-DOSコマンドプロンプトTips §.パイプとリダイレクトを参照してください。


Readメソッドを使うと標準入力から1文字ずつ読み込むことができます。 ReadメソッドはReadLineメソッドとは異なり、改行文字もそのまま読み込まれます。 Readメソッドは読み込んだ文字をchar/Char型の範囲の数値(int/Integer型)として返すため、文字として参照する場合は戻り値をChar型にキャストします。 標準入力から読み込める文字列がなくなった場合やCTRL+Zによって入力を中断した場合、Readメソッドは-1を返します。

Console.Readメソッドで標準入力から1文字ずつ読み込んで標準出力に書き込む
using System;

class Sample {
  static void Main()
  {
    while (true) { // 無限ループ
      // 標準入力から一文字読み込む
      int ch = Console.Read();

      if (ch == -1) {
        // 読み込める文字列が無くなった
        break;
      }
      else {
        // 読み込んだ文字にかっこを付けて標準出力に書き込む
        char c = (char)ch;

        Console.Write("<{0}>", c);
      }
    }
  }
}
キー入力を使った場合の実行例
>sample.exe
f<f>o<o>o<o>
<
>b<b>a<a>r<r>
<
>
^Z
パイプを使った場合の実行例
>type sample.txt
foo
bar

>type sample.txt | sample.exe
<f><o><o><
><b><a><r><
>

ReadLine, Readメソッドではキー入力された文字はそのままコンソールウィンドウにも表示されます(エコーバックされる)。 キー入力を表示したくない場合、エコーバックしないようにしたい場合はReadKeyメソッドを使うことができます。

ReadKeyメソッドの使用例はエコーバックせずに文字列を入力する(Console.ReadKey)をご覧ください。

§1.3 標準入出力のReader/Writer

Console.InプロパティConsole.Outプロパティを使うことでも標準入出力への読み書きを行うことができます。 例えばConsole.WriteLineとConsole.Out.WriteLine、Console.ReadLineとConsole.In.ReadLineを使って標準入出力への読み書きを行えますが、動作および結果はどちらも同じです。

Console.Outを使って標準出力へ書き込む
using System;

class Sample {
  static void Main()
  {
    // 以下の2つはどちらも同じ動作・同じ結果となる

    // Consoleクラスのメソッドを使って標準出力に書き込む
    Console.WriteLine("Hello, world!");

    // Console.Outプロパティを使って標準出力に書き込む
    Console.Out.WriteLine("Hello, world!");
  }
}
Console.Inを使って標準入力から読み込む
using System;

class Sample {
  static void Main()
  {
    // 以下の2つはどちらも同じ動作・同じ結果となる

    // Consoleクラスのメソッドを使って標準入力から読み込む
    string line1 = Console.ReadLine();

    // Console.Inプロパティを使って標準入力から読み込む
    string line2 = Console.In.ReadLine();
  }
}

Console.In/Console.Outは、それぞれ標準入出力への読み書きを行うTextReader/TextWriterとなっています。 これはJavaにおけるSystem.in/System.outに相当するものと言えます。

Console.Inを使った一例を挙げます。 Consoleクラスには標準入力に書き込まれる内容すべてを読み込むメソッドは存在しませんが、TextReaderにはReadToEndメソッドが用意されているため、これを使って標準入力の内容すべてを読み込むことができます。

Console.In.ReadToEndメソッドを使って標準入力の内容をすべて読み込む
using System;

class Sample {
  static void Main()
  {
    // ReadToEndメソッドで標準入力の内容をすべて読み込む
    string text = Console.In.ReadToEnd();

    // 読み込んだ内容を出力する
    Console.WriteLine("(length = {0})", text.Length);
    Console.WriteLine(text);
  }
}
キー入力を使った場合の実行例
>sample.exe
line1
line2
line3
^Z
(length = 21)
line1
line2
line3
パイプを使った場合の実行例
>type sample.txt
line1
line2
line3

>type sample.txt | sample.exe
(length = 21)
line1
line2
line3

コンソールアプリケーションの場合、上記のコードはCTRL-Z(LinuxではCTRL+D)で標準入力への入力を終了しない限りReadToEndメソッドから処理が戻らず待機状態になります。

コマンドラインでのリダイレクト・パイプと標準入出力との接続についてはMS-DOSコマンドプロンプトTips §.パイプとリダイレクトを参照してください。


XmlDocumentクラスなどではTextReader/TextWriterを入力元・出力先とすることができるようになっています。 こういったクラスでは、Console.In/Console.Outを指定することで標準入出力から直接読み書きすることができるようになります。

TextReader/TextWriterの代わりに標準入出力を入出力先に設定して読み書きを行う
using System;
using System.Xml;

class Sample {
  static void Main()
  {
    XmlDocument doc = new XmlDocument();

    // 標準入力の内容をXMLとして読み込む
    doc.Load(Console.In);

    // ルート要素下に新しい要素を追加する
    doc.DocumentElement.AppendChild(doc.CreateElement("bar"))
                       .AppendChild(doc.CreateTextNode("bar"));

    // 編集したXMLを標準出力に書き込む
    doc.Save(Console.Out);
  }
}
実行結果例
>type sample.xml
<?xml version="1.0" ?>
<doc>
  <foo>foo</foo>
</doc>

>type sample.xml | sample.exe
<?xml version="1.0"?>
<doc>
  <foo>foo</foo>
  <bar>bar</bar>
</doc>

後述のConsole.OpenStandardStreamメソッドを用いることにより、TextReader/TextWriterだけでなくStreamとして取得することもできます。

§1.4 標準エラー

Console.WriteLineConsole.ReadLineなど対応するメソッドがある標準入出力とは異なり、Consoleクラスには標準エラーへの書き込みを行うためのメソッドは用意されていません。 標準エラーへの書き込みを行う場合はConsole.Errorプロパティを使って書き込みを行います。

Console.ErrorもConsole.Outと同様、標準エラーへの書き込みを行うTextWriterとなっています。

標準出力と同様、実際の出力先はコンソールウィンドウ、リダイレクトされたファイルやIDEの出力ペインなどとなり、アプリケーションの種類や実行環境によって変わります。

Console.Error.WriteLineメソッドで標準エラーへ書き込む
using System;

class Sample {
  static void Main()
  {
    // 標準出力への書き込み
    Console.WriteLine("Hello, stdout!");
    Console.Out.WriteLine("Hello, stdout!");

    // 標準エラーへの書き込み
    Console.Error.WriteLine("Hello, stderr!");
  }
}
実行結果
>sample.exe
Hello, stdout!
Hello, stdout!
Hello, stderr!

>sample.exe > stdout.txt 2> stderr.txt

>type stdout.txt
Hello, stdout!
Hello, stdout!

>type stderr.txt
Hello, stderr!

コマンドラインでのリダイレクト・パイプと標準入出力との接続についてはMS-DOSコマンドプロンプトTips §.パイプとリダイレクトを参照してください。



§2 標準ストリームの取得

Console.In/Out/ErrorはTextReader/TextWriterであるためテキストデータの入出力しか行えません。 標準入出力でバイナリデータを扱いたい場合などはOpenStandardInput, OpenStandardOutput, OpenStandardErrorの各メソッドを使います。

これらのメソッドではそれぞれ標準入力(STDIN)・標準出力(STDOUT)・標準エラー(STDERR)に対応するStreamを取得することができます。 取得したStreamを使って直接読み書きをできるほか、取得したStreamからBinaryReader/BinaryWriterを作成してバイナリデータの読み書きを行うといったこともできます。

次の例では、標準入力から読み込んだ画像を水平方向に反転し、標準出力に書き込んでいます。

標準入出力のStreamを取得して画像の読み書きを行う
using System;
using System.Drawing;
using System.Drawing.Imaging;

class Sample {
  static void Main()
  {
    // 標準入力のStreamを取得して画像として読み込む
    using (var image = new Bitmap(Console.OpenStandardInput())) {
      // 画像を左右反転する
      image.RotateFlip(RotateFlipType.RotateNoneFlipX);

      // 標準出力のStreamを取得して加工した画像を書き込む
      image.Save(Console.OpenStandardOutput(), ImageFormat.Jpeg);
    }
  }
}
実行例
>sample.exe < sample.jpg > mirror.jpg

OpenStandardXXXメソッドで得られるStreamをラップすることにより、暗号化や圧縮・バッファリングなどの機能を付け加えることもできます。

次の例ではbase64コマンドに似たプログラムを実装しています。 CryptoStreamを使って標準入出力のStreamをラップすることにより、標準入力から読み込んだ内容をBASE64エンコード/デコードして標準出力に書き込んでいます。

標準入出力のStreamをラップしてBASE64エンコード/デコードの機能を付け加える
using System;
using System.IO;
using System.Security.Cryptography;

class Sample {
  static void Main(string[] args)
  {
    // コマンドライン引数に-dもしくは--decodeが指定されている場合はデコードを行う
    bool decode = false;

    foreach (string arg in args) {
      if (arg == "-d" || arg  == "--decode")
        decode = true;
    }

    Stream input, output;

    if (decode) {
      // BASE64デコードする場合

      // 標準入力から読み込みを行い、FromBase64TransformによってBASE64デコードを行うCryptoStreamを入力元ストリームとする
      input = new CryptoStream(Console.OpenStandardInput(), new FromBase64Transform(), CryptoStreamMode.Read);

      // 標準出力を出力先ストリームとする
      output = Console.OpenStandardOutput();
    }
    else {
      // BASE64エンコードする場合

      // 標準入力を入力元ストリームとする
      input = Console.OpenStandardInput();

      // ToBase64TransformによってBASE64エンコードを行い、標準出力へ書き込むCryptoStreamを出力先ストリームとする
      output = new CryptoStream(Console.OpenStandardOutput(), new ToBase64Transform(), CryptoStreamMode.Write);
    }

    // 入力元ストリームの内容を出力先ストリームに'コピー'する
    // (コピーを行う際、CryptoStreamによってデコード/エンコード処理が行われる)
    input.CopyTo(output);

    output.Close();
  }
}
実行例
>type sample.txt
Hello, world!

>type sample.txt | sample.exe
SGVsbG8sIHdvcmxkIQo=

>type sample.txt | sample.exe | sample.exe -d
Hello, world!

ストリームのラップについて、また暗号化や圧縮などのフォーマット変換、バッファリングなどの機能を付け加える方法等についてはストリームの基本とStreamクラス §.データフォーマットの変換やバッファリングなどの機能を追加するStream派生クラスも参照してください。

取得した標準ストリームを使う例として、コマンドラインオプションで標準入力とファイル入力を切り替える例を実装しているMD5ハッシュ文字列の作成 §.MD5クラスを使ってmd5sumコマンドを実装するもあわせてご覧ください。

§3 標準ストリームのリダイレクト

SetIn, SetOut, SetErrorの各メソッドを使うと、実行時に標準ストリームを任意のTextReader/Writerへとリダイレクトすることができます。 リダイレクトを行うと、Console.WriteLineやReadLineなどのメソッドによる入出力先は、指定されたTextReader/Writerへと変更されます。

次の例ではSetOutメソッドを使って標準出力の出力先を実行時に開いたファイルに変更しています。

標準出力をStreamWriterにリダイレクトする
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // この内容はデフォルトの標準出力に書き込まれる
    Console.WriteLine("Hello, world!");

    // ファイルstdout.txtへ書き込むStreamWriterを作成する
    StreamWriter writer = new StreamWriter("stdout.txt");

    // 書き込みの度にバッファをフラッシュさせるためにAutoFlushをtrueにする
    writer.AutoFlush = true;

    // 作成したStreamWriterを標準出力の出力先にする
    Console.SetOut(writer);

    // この内容はstdout.txtに書き込まれる
    Console.WriteLine("こんにちは、世界!");
  }
}
実行結果
>sample.exe
Hello, world!

>type stdout.txt
こんにちは、世界!

TextReader/Writerクラスは抽象クラスであるため、たいていの場合リダイレクト先の設定には派生クラスであるStreamReader/Writerを使うことになります。 TextReader/WriterおよびStreamReader/Writerの作成や扱い方についてはStreamReaderクラス・StreamWriterクラスを参照してください。

SetIn, SetOut等のメソッドではStreamをリダイレクト先として直接設定することはできません。 Streamをリダイレクト先として設定したい場合は、そのStreamをベースにしてStreamReader/Writerを作成する必要があります。 具体的な方法についてはStreamReaderクラス・StreamWriterクラス §.任意のStreamからの読み込み・書き込みを参照してください。

§3.1 標準出力の破棄・空の標準入力

TextReader.NullフィールドTextWriter.Nullフィールドを標準入出力のリダイレクト先として設定すると、標準入出力をNULLデバイス(NUL, /dev/null)にリダイレクトするのと同様の効果が得られます。 TextReader.Nullフィールドを使用すれば空の標準入力として利用でき、TextWriter.Nullを使用すれば標準出力を破棄することができます。

次の例では、SetOutメソッドでTextWriter.Nullを指定することにより、標準出力を一切出力しないようにしています。

標準出力をTextWriter.Nullにリダイレクトして書き込まれた内容を破棄する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // 標準出力の出力先をTextWriter.Nullに変更する
    Console.SetOut(TextWriter.Null);

    // この内容はTextWriter.Nullに書き込まれる
    // (TextWriter.Nullは'なにもしない'ので、実際にはどこにもなにも出力されない)
    Console.WriteLine("Hello, world!");
  }
}

§3.2 標準ストリームがリダイレクトされているか調べる

.NET Framework 4.5以降では、IsInputRedirected, IsOutputRedirected, IsErrorRedirectedの各プロパティを参照することにより、自プロセスの標準ストリームがリダイレクトされているかどうかを知ることができるようになっています。

これらのプロパティは、コマンドラインで標準ストリームのパイプ・リダイレクトを行った場合にtrueとなるほか、SetIn, SetOut, SetErrorメソッドによってリダイレクトを行った場合もtrueとなります。

標準ストリームがリダイレクトされているかどうかを調べる
using System;

class Sample {
  static void Main()
  {
    // 各標準ストリームがリダイレクトされているかどうかを調べる
    Console.WriteLine("IsInputRedirected:  {0}", Console.IsInputRedirected);
    Console.WriteLine("IsOutputRedirected: {0}", Console.IsOutputRedirected);
    Console.WriteLine("IsErrorRedirected:  {0}", Console.IsErrorRedirected);
  }
}
実行結果
>sample.exe
IsInputRedirected:  False
IsOutputRedirected: False
IsErrorRedirected:  False

>type sample.txt | sample.exe
IsInputRedirected:  True
IsOutputRedirected: False
IsErrorRedirected:  False

>sample.exe | more
IsInputRedirected:  False
IsOutputRedirected: True
IsErrorRedirected:  False

>sample.exe < sample.txt
IsInputRedirected:  True
IsOutputRedirected: False
IsErrorRedirected:  False

>sample.exe > stdout.txt

>type stdout.txt
IsInputRedirected:  False
IsOutputRedirected: True
IsErrorRedirected:  False

>test.exe 2> stderr.txt
IsInputRedirected:  False
IsOutputRedirected: False
IsErrorRedirected:  True

これらのプロパティをチェックすることで標準出力の出力先がコンソールウィンドウかどうかを知ることができるため、リダイレクトされているときはConsole.ForegroundColor/BackgroundColorプロパティで文字色を変えたり、Console.CursorLeft/CursorTopプロパティでカーソルの位置を変えたりしないようにする、といったことができます。

.NET Framework 4.5より前のバージョンではこれらのプロパティは用意されないため、c# - How to detect if Console.In (stdin) has been redirected? - Stack Overflowで提示されているような方法を使って調べる必要があります。