うら干物書き

ゲームを作りたい!

【C#】コマンドライン引数をいい感じで取得する

 ちょっとしたツールが作りたくなりました。
 C#.NETを使ってパパっと作ってしまいましょうか。

 コンソールアプリケーションならUIとか考えなくていいから、すぐに出来るはずだ。
 でもコンソールアプリケーションなら、コマンドライン引数のこと考えなくちゃ。
 どうやって引数を解析しようか。


この記事には.NET Framework 4.6.1を使用しています。
この記事にはCommand Line Parser 2.2.1を使用しています。

Command Line Parser を使ってみる

f:id:urahimono:20180503205028p:plain

 NuGetにはCommand Line Parserというコマンドライン引数の解析に便利なものがあるみたいですよ。

Command Line Parser Libraryを使ってコマンドライン引数を解析する1 - DoboWiki
Command Line Parser Libraryを使ってコマンドライン引数を解析する2 - DoboWiki
.NETプログラミング研究/110 - DoboWiki

 このサイトさんは情報量が多くてすごく助かりますね。
 これを見るだけでCommand Line Parseを簡単に組み込めそうです。

実際に使ってみる

 では早速使ってみよう。
 まずコマンドライン引数で受け取るオプションのクラスを作ろう。
Options.cs

class Options
{
    [CommandLine.Option( 'i', "input", Required = true, HelpText = "入力するファイル名" )]
    public string InputFile
    {
        get;
        set;
    }

    [CommandLine.Option( 'o', "output", Required = true, HelpText = "出力するファイル名" )]
    public string OutputFile
    {
        get;
        set;
    }
}

 あとはエントリーポイントとなるメイン関数を作れば実行できそうだ。
Program.cs

class Program
{
    static void Main( string[] i_args )
    {
        var options = new Options();

        // ここで引数が間違っているとエラーが出てしまう!
        bool isSuccessful = CommandLine.Parser.Default.ParseArguments( i_args, options );


        if( isSuccessful )
        {
            System.Console.WriteLine( "Input: {0}", options.InputFile );
            System.Console.WriteLine( "Output: {0}", options.OutputFile );
        }
        else
        {
            System.Console.WriteLine( "コマンドライン引数を間違えてないかぃ?" );
        }

        System.Console.WriteLine( "..." );
        System.Console.ReadKey();
    }
}

 あれ、エラーが出てしまった。
 先ほどのサイトさんのコード通りに作ったのに、何故だろうか。

ParseArguments()の使い方

 エラーが出ているParser.ParseArguments()を見てみよう。
 確認する限り、渡す引数が違うようだ。

 Command Line Parserのバージョンが違うのかもしれない。
 もう少しCommand Line Parserについて情報を集める必要がありそうだね。

 調べたところCommand Line ParserのコードはGitHubにアップロードされているようだ。
 確認してみよう。

github.com

 ふむぅ、デモコードを用意してくれているのだが、ちと複雑だなぁ。
 もっとシンプルにParser.ParseArguments()を使えると思うの。
 先ほどのコードを改良してみよう。
Program.cs

class Program
{
    static void Main( string[] i_args )
    {
        CommandLine.ParserResult<Options> result = CommandLine.Parser.Default.ParseArguments<Options>( i_args );

        if( result.Tag == CommandLine.ParserResultType.Parsed )
        {
            // パースに成功していれば、Parsed<T>型に変換できるはずだ!
            var parsed  = (CommandLine.Parsed<Options>)result;

            System.Console.WriteLine( "Input: {0}", parsed.Value.InputFile );
            System.Console.WriteLine( "Output: {0}", parsed.Value.OutputFile );
        }
        else
        {
            // パースに失敗していれば、NotParsed<T>型に変換できるはずだ! (使わんけど……)
            var notParsed = (CommandLine.NotParsed<Options>)result;

            System.Console.WriteLine( "コマンドライン引数を間違えてないかぃ?" );
        }

        System.Console.WriteLine( "..." );
        System.Console.ReadKey();

    }
}

f:id:urahimono:20180503205045p:plain
f:id:urahimono:20180503205054p:plain

 これなら問題なく動いたぞ。
 Parser.ParseArguments()の返る型であるParserResult<T>Parsed<T>に変換してあげればオプション用のクラス情報が取得出来た。

 Parser.ParseArguments()の返り値を、Parsed<Options>に変換出来るかどうかでパースの成否を判断してしまえば、もう少しコードがシンプルになりそうだ。
 やってみよう。

Program.cs

class Program
{
    static void Main( string[] i_args )
    {
        var result = CommandLine.Parser.Default.ParseArguments<Options>( i_args ) as CommandLine.Parsed<Options>;

        if( result != null )
        {
            System.Console.WriteLine( "Input: {0}", result.Value.InputFile );
            System.Console.WriteLine( "Output: {0}", result.Value.OutputFile );
        }
        else
        {
            System.Console.WriteLine( "コマンドライン引数を間違えてないかぃ?" );
        }

        System.Console.WriteLine( "..." );
        System.Console.ReadKey();

    }
}

 うん、これでも動くみたいだ。

 コマンドライン引数もこれで綺麗に整ったし、ツールを頑張って作っていこう。