2014年09月03日

C#で標準入力と標準出力のリダイレクト

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書く?orgに感謝を込めて」シリーズ その38

■問題 (出題者:にしお さん)
以下のようなプログラムを作ってください。

       * コマンドライン引数を二つ取り、引数で指定されたプログラムを起動する(以下A, B)
       * Aの標準出力をBの標準入力へ、Bの標準出力をAの標準入力へ中継する。
       * A, Bどちらかが終了した場合はもう片方を終了して自身も終了する。

この問題は、標準入力と標準出力のリダイレクトをどうやるかという問題ですね。
ただ、プログラムA,Bとも標準入力待ちから始まると、いつまでたっても待ち状態の ままになってしまうので、どんなプログラムでもOKというわけじゃないですね(たぶん)...
なので、ここでは起動するプログラムA,Bも自作することとしました。

Aは単に標準入力を()で囲んで標準出力に複写することを永遠に続けるだけのプログラム。
Bは最初に文字列を標準出力に出力後に、標準入力の文字列を加工をし、標準出力に出力することを3回繰り返すプログラム。
ただし、これだけだと、それぞれのの出力を食い合って、何をやっているかを確認できないので、 標準エラーにもその内容を出力するようにしています。
つまらない例ですみません m(_ _)m

C#では、プロセスを起動する際のオプションであるProcessStartInfo の RedirectStandardInput、RedirectStandardOutput を true にすることで、リダイレクトが可能になります。

■プログラムA
using System;

namespace Doukaku.Org {
    class ProgramA {
        static void Main(string[] args) {
            while (true) {
                string s = Console.ReadLine();
                var t = string.Format("({0})", s);
                Console.Error.WriteLine("A > " + t);
                Console.WriteLine(t);
            }
        }
    }
}

■プログラムB
using System;

namespace Doukaku.Org {
    class ProgramB {
        static void Main(string[] args) {
            string o = "文字数は?";
            Console.WriteLine(o);
            Console.Error.WriteLine("B > " + o);
            for (int i = 0; i < 3; i++) {
                string s = Console.ReadLine();
                o = string.Format("{0}:{1}文字", s , s.Length);
                Console.WriteLine(o);
                Console.Error.WriteLine("B > " + o);
             }
        }
    }
}

■C#で書いたA,Bを起動するプログラム
using System;
using System.Diagnostics;
using System.Threading;

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            var pa = ProcessStart("A.exe");
            var pb = ProcessStart("B.exe");
            Relay(pa, pb);
            Relay(pb, pa);
            do {
                pa.WaitForExit(500);
                pb.WaitForExit(500);
            } while ((pa.HasExited & pb.HasExited) == true);
            if (pa.HasExited == true && pb.HasExited == false)
                pb.Kill();
            if (pa.HasExited == false && pb.HasExited == true)
                pa.Kill();
            Console.WriteLine("END");
        }

        static Thread Relay(Process outp, Process inp) {
            Thread thread = new Thread(() => {
                while (outp.StandardOutput.EndOfStream == false) {
                    inp.StandardInput.WriteLine(outp.StandardOutput.ReadLine());
                };
            });
            thread.Start();
            return thread;
        }

        static Process ProcessStart(string cmd) {
            Process process = new Process();
            ProcessStartInfo psi = new ProcessStartInfo(cmd);
            psi.UseShellExecute = false;
            psi.RedirectStandardInput = true;
            psi.RedirectStandardOutput = true;
            process.StartInfo = psi;
            process.Start();
            return process;
        }
    }
}

これを起動すると、以下のような結果が得られます。

B > 文字数は?
A > (文字数は?)
B > (文字数は?):7文字
A > ((文字数は?):7文字)
B > ((文字数は?):7文字):13文字
A > (((文字数は?):7文字):13文字)
B > (((文字数は?):7文字):13文字):20文字
A > ((((文字数は?):7文字):13文字):20文字)
END



 

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/gushwell/52381508