• 追加された行はこの色です。
  • 削除された行はこの色です。
#title(Command Line Parser Libraryを使ってコマンドライン引数を解析する2)

#navi(.NETプログラミング研究)

#contents

*Command Line Parser Libraryを使ってコマンドライン引数を解析する2 [#hb74e5c8]

今回は前回に続いて、[[Command Line Parser Library>http://commandline.codeplex.com/]]を使ってコマンドライン引数を解析する方法について説明します。内容も前回の続きになりますので、前回([[Command Line Parser Libraryを使ってコマンドライン引数を解析する1>http://wiki.dobon.net/index.php?.NET%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%B8%A6%B5%E6%2F108]])を読んでいないと、何のことだかさっぱり分からないと思います。

**大文字と小文字を区別する [#z7ab79e4]

今まで紹介してきた方法で解析すると、オプション名の大文字と小文字が区別されません。つまり、"o"というオプション名でも、"O"というオプション名でも同じものとして扱われます。

大文字と小文字が区別されるようにするには、CaseSensitiveプロパティをTrueにしたParserSettingsを使ってParserオブジェクトを作成し、このParseArgumentsメソッドを使って解析を行います。

以下にその例を示します。

#code(vbnet){{
Module Module1
    'エントリポイント
    Sub Main(args As String())
        'Optionsクラスのインスタンスを作成
        Dim opts = New Options()

        '大文字と小文字を区別するようにする
        Dim p = New CommandLine.Parser(Sub(w) w.CaseSensitive = True)
        'コマンドライン引数を解析する
        Dim isSuccess As Boolean = p.ParseArguments(args, opts)
        '解析結果を表示
        Console.WriteLine("OutputFile: {0}", opts.OutputFile)
        Console.WriteLine("Overwrite: {0}", opts.Overwrite)

        Console.ReadLine()
    End Sub
End Module
}}

#code(csharp){{
//エントリポイント
static void Main(string[] args)
{
    //Optionsクラスのインスタンスを作成
    var opts = new Options();

    //大文字と小文字を区別するようにする
    var p = new CommandLine.Parser(with => with.CaseSensitive = true);
    //コマンドライン引数を解析する
    bool isSuccess = p.ParseArguments(args, opts);
    //解析結果を表示
    Console.WriteLine("OutputFile: {0}", opts.OutputFile);
    Console.WriteLine("Overwrite: {0}", opts.Overwrite);

    Console.ReadLine();
}
}}

**不明なオプションを無視する [#w2e91ec6]

デフォルトでは、コマンドライン引数に不明なオプション(Option属性で定義されていない、先頭にダッシュやハイフンが付いている文字列)が指定されていた時、ParseArgumentsメソッドがFalseを返します。不明なオプションが指定されていても無視するには、ParserSettings.IgnoreUnknownArgumentsをTrueにします。

#code(vbnet){{
Module Module1
    'エントリポイント
    Sub Main(args As String())
        'Optionsクラスのインスタンスを作成
        Dim opts = New Options()

        '不明なオプションを無視する
        Dim p = New CommandLine.Parser(Sub(w) w.IgnoreUnknownArguments = True)
        'コマンドライン引数を解析する
        Dim isSuccess As Boolean = p.ParseArguments(args, opts)
        '解析結果を表示
        Console.WriteLine("OutputFile: {0}", opts.OutputFile)
        Console.WriteLine("Overwrite: {0}", opts.Overwrite)

        Console.ReadLine()
    End Sub
End Module
}}

#code(csharp){{
//エントリポイント
static void Main(string[] args)
{
    //Optionsクラスのインスタンスを作成
    var opts = new Options();

    //不明なオプションを無視する
    var p = new CommandLine.Parser(with => with.IgnoreUnknownArguments = true);
    //コマンドライン引数を解析する
    bool isSuccess = p.ParseArguments(args, opts);
    //解析結果を表示
    Console.WriteLine("OutputFile: {0}", opts.OutputFile);
    Console.WriteLine("Overwrite: {0}", opts.Overwrite);

    Console.ReadLine();
}
}}

**同時に指定できないオプションを設定する [#ace3b62b]

私が試した限りでは、この機能はドキュメントで説明されているのとは全く違った(むしろ逆の)動作をしました。ドキュメントの記述と実際の動作のどちらが正しいのか分かりませんが、ここでは実際の動作に基づいて説明します。

あるオプションと別のオプションを同時に指定できないようにする(排他的にする)には、Option属性のMutuallyExclusiveSetプロパティを使います。MutuallyExclusiveSetプロパティの値が同じオプションは1つしか指定できなくなります。

#column(補足){{
ドキュメントの「[[Mutually Exclusive Options>https://github.com/gsscoder/commandline/wiki/Mutually-Exclusive-Options]]」では、MutuallyExclusiveSetプロパティの値が違うオプションを組み合わせて使うと解析に失敗すると説明されています。
}}

以下の例では、TextFileとXmlFileのMutuallyExclusiveSetが"file"ですので、これらのオプションを同時に指定できません。

#code(vbnet){{
Public Class Options
    <CommandLine.Option("t"c, "textfile", MutuallyExclusiveSet:="file")> _
    Public Property TextFile As String

    <CommandLine.Option("x"c, "xmlfile", MutuallyExclusiveSet:="file")> _
    Public Property XmlFile As String

    <CommandLine.Option("v"c)> _
    Public Property Overwrite As Boolean
End Class
}}

#code(csharp){{
public class Options
{
    [CommandLine.Option('t', "textfile", MutuallyExclusiveSet = "file")]
    public string TextFile
    {
        get;
        set;
    }

    [CommandLine.Option('x', "xmlfile", MutuallyExclusiveSet = "file")]
    public string XmlFile
    {
        get;
        set;
    }

    [CommandLine.Option('v')]
    public bool Overwrite
    {
        get;
        set;
    }
}
}}

このMutuallyExclusiveSetを有効にするには、解析を行うときに、MutuallyExclusiveプロパティがTrueのParserを使う必要があります。Parser.DefaultのMutuallyExclusiveはFalseだからです。

#code(vbnet){{
Module Module1
    'エントリポイント
    Sub Main(args As String())
        'Optionsクラスのインスタンスを作成
        Dim opts = New Options()

        'MutuallyExclusiveをTrueにする
        Dim p = New CommandLine.Parser(Sub(w) w.MutuallyExclusive = True)
        'コマンドライン引数を解析する
        Dim isSuccess As Boolean = p.ParseArguments(args, opts)

        Console.ReadLine()
    End Sub
End Module
}}

#code(csharp){{
//エントリポイント
static void Main(string[] args)
{
    //Optionsクラスのインスタンスを作成
    var opts = new Options();

    //MutuallyExclusiveをTrueにする
    var p = new CommandLine.Parser(with => with.MutuallyExclusive = true);
    //コマンドライン引数を解析する
    bool isSuccess = p.ParseArguments(args, opts);

    Console.ReadLine();
}
}}

このようにして以下のようなコマンドライン引数を渡すと、解析に失敗します。

 -t 1.txt -x 1.xml

MutuallyExclusiveSetに何も指定されていないオプションであれば、同時に複数指定可能です。

**カルチャを変更する [#ea149381]

前回指摘したように、例えばコマンドライン引数から整数のオプション値を取得する時、文字列を整数に変換するという工程が入りますので、カルチャの影響を受ける可能性があります。

解析で使用するカルチャを変更するには、ParserのParsingCultureプロパティを変更します。

なおParser.DefaultのParsingCultureプロパティは、InvariantCultureです。

以下の例では、解析に使用するカルチャを"ja-JP"に変更しています。

#code(vbnet){{
Module Module1
    'エントリポイント
    Sub Main(args As String())
        'Optionsクラスのインスタンスを作成
        Dim opts = New Options()

        'カルチャを"ja-JP"に変更する
        Dim p = New CommandLine.Parser(Sub(w) w.ParsingCulture =
            New System.Globalization.CultureInfo("ja-JP"))
        'コマンドライン引数を解析する
        Dim isSuccess As Boolean = p.ParseArguments(args, opts)

        Console.ReadLine()
    End Sub
End Module

}}

#code(csharp){{
//エントリポイント
static void Main(string[] args)
{
    //Optionsクラスのインスタンスを作成
    var opts = new Options();

    //カルチャを"ja-JP"に変更する
    var p = new CommandLine.Parser(with => with.ParsingCulture =
        new System.Globalization.CultureInfo("ja-JP"));
    //コマンドライン引数を解析する
    bool isSuccess = p.ParseArguments(args, opts);

    Console.ReadLine();
}
}}

**「Parser.Default」と「new Parser()」の違い [#q4918994]

非常に厄介なことに、前回使用していた「Parser.Default」と、上記で使用している新しく作成したParserオブジェクトは、デフォルトのプロパティ値が異なりますので、これらを使って解析した結果も異なる可能性があります。

Parser.DefaultのCaseSensitiveはFalseですが、コンストラクタの引数に何も指定せずに作成したParserオブジェクトのCaseSensitiveはTrueです。ParsingCultureは、前者がInvariantCultureで、後者がThread.CurrentThread.CurrentCultureです。

MutuallyExclusiveとIgnoreUnknownArgumentsは、両者ともFalseです。

また、後で詳しく説明しますが、HelpWriterプロパティも両者で異なり、Parser.DefaultはConsole.Errorですが、新しく作成したParserオブジェクトはnullです。よって、下で紹介するエラーメッセージを表示する方法をParser.Default以外で試すと、何も表示されないでしょう。その場合は、HelpWriterプロパティをConsole.Errorにしてください。

**オプションの説明や、エラーメッセージを自動的に表示する [#ca220daa]

Command Line Parser Libraryには、解析できなかった時に自動でエラーメッセージを表示したり、オプションの説明を表示したりする機能があります。ここからはこの機能について説明します。

***解析できなかった時や「--help」オプションが指定された時に、自動的にメッセージを表示する [#r3e049aa]

コマンドライン引数の解析に失敗した時、自動的に指定したメッセージを表示することができます。それには、OptionsクラスにStringを返すメソッドを作成して、HelpOption属性を適用し、表示したいメッセージを返すようにします。

さらにこのメッセージは、アプリケーションに「--help」オプションを指定して起動した時にも表示されます。

例えばOptionsクラスに以下のようなGetUsageメソッドを追加すると、ParseArgumentsメソッドがFalseを返す時、コンソール(標準エラー出力)に「コマンドラインオプションの記述が正しくありません。」と表示されます。

#code(vbnet){{
'オプションをプロパティに持つクラス
Public Class Options
    <CommandLine.Option("o"c)> _
    Public Property OutputFile As String

    <CommandLine.Option("v"c)> _
    Public Property Overwrite As Boolean

    '解析に失敗した時に表示するメッセージを返すメソッド
    <CommandLine.HelpOption> _
    Public Function GetUsage() As String
        Return "コマンドラインオプションの記述が正しくありません。" & vbCrLf
    End Function
End Class
}}

#code(csharp){{
//オプションをプロパティに持つクラス
public class Options
{
    [CommandLine.Option('o')]
    public string OutputFile
    {
        get;
        set;
    }

    [CommandLine.Option('v')]
    public bool Overwrite
    {
        get;
        set;
    }

    //解析に失敗した時に表示するメッセージを返すメソッド
    [CommandLine.HelpOption]
    public string GetUsage()
    {
        return "コマンドラインオプションの記述が正しくありません。\n";
    }
}
}}

#column(注意){{
前述したように、新しく作成したParserオブジェクトを使って解析を行った場合は、何も表示されない可能性があります。その場合は、HelpWriterプロパティをConsole.Errorにしてください。これについて詳しくは、後述します。
}}

#column(補足){{
GetUsageメソッドがなくても、解析に失敗した時(あるいは「--help」が指定された時)にメッセージを表示する方法があります。Parser.ParseArgumentsメソッドの代わりにParser.ParseArgumentsStrictメソッドを使って解析を行うと、自動的にメッセージが表示されます。この時表示されるメッセージは、後述するHelpText.AutoBuildメソッドを使って作成されたものです。

さらにParseArgumentsStrictメソッドは、3番目の引数がない場合は、メッセージを表示後に自動的に「Environment.Exit(1)」を呼び出して、アプリケーションを終了させます。3番目の引数にデリゲートが指定されていた場合は、それを実行します。
}}

***オプションの説明文を作成する [#g46c36e2]

通常のコンソールアプリケーションでは、「/?」のようなオプションによって、使用法と使用できるオプションの説明が表示されます。Command Line Parser LibraryではHelpTextクラスを使うことによって、オプションの説明文を簡単に作成することができます。

そのための準備として、Option属性のHelpTextプロパティを使って、オプションに説明を記述しておきます。また、オプションがBoolean型ではなく、オプションの後に値を記述する必要があるならば、値の説明をMetaValueプロパティに記述します。

このようにしておけば、HelpTextクラスのAddOptionsメソッドを使って、オプションの説明を作成できます。また、HelpText.AddPreOptionsLineメソッドによってオプションの説明の上に表示するメッセージを、HelpText.AddPostOptionsLineメソッドによって下に表示するメッセージを追加できます。

このようにして、使用法とオプションの説明を表示する例を示します。

#code(vbnet){{
Public Class Options
    <CommandLine.Option("o"c, "output", Required:=True, _
        HelpText:="出力先のファイル名", MetaValue:="ファイル")> _
    Public Property OutputFile As String

    <CommandLine.Option("v"c, HelpText:="ファイルを上書き保存する")> _
    Public Property Overwrite As Boolean

    '解析に失敗した時に表示するメッセージを返すメソッド
    <CommandLine.HelpOption> _
    Public Function GetUsage() As String
        Dim help = New CommandLine.Text.HelpText()
        'オプションの前にダッシュをつける(これがないとダッシュが付かない)
        help.AddDashesToOption = True

        'オプションの説明の前に表示する文字列を追加
        help.AddPreOptionsLine("使用法: TestApp -o ファイル [-v]")
        help.AddPreOptionsLine("")
        help.AddPreOptionsLine("オプション:")
        'オプションの説明を追加。"[必須]"を省略すると、
        ' 必須オプションの説明の前に付く文字列が"Required."になる。
        help.AddOptions(Me, "[必須]")
        'オプションの説明の後に表示する文字列を追加
        help.AddPostOptionsLine("詳しくは、ヘルプをご覧ください。")
        help.AddPostOptionsLine("")

        Return help
    End Function
End Class
}}

#code(csharp){{
public class Options
{
    [CommandLine.Option('o', "output", Required = true,
        HelpText = "出力先のファイル名", MetaValue = "ファイル")]
    public string OutputFile
    {
        get;
        set;
    }

    [CommandLine.Option('v', HelpText="ファイルを上書き保存する")]
    public bool Overwrite
    {
        get;
        set;
    }

    //解析に失敗した時に表示するメッセージを返すメソッド
    [CommandLine.HelpOption]
    public string GetUsage()
    {
        var help = new CommandLine.Text.HelpText();
        //オプションの前にダッシュをつける(これがないとダッシュが付かない)
        help.AddDashesToOption = true;

        //オプションの説明の前に表示する文字列を追加
        help.AddPreOptionsLine("使用法: TestApp -o ファイル [-v]");
        help.AddPreOptionsLine("");
        help.AddPreOptionsLine("オプション:");
        //オプションの説明を追加。"[必須]"を省略すると、
        // 必須オプションの説明の前に付く文字列が"Required."になる。
        help.AddOptions(this, "[必須]");
        //オプションの説明の後に表示する文字列を追加
        help.AddPostOptionsLine("詳しくは、ヘルプをご覧ください。");
        help.AddPostOptionsLine("");

        return help;
    }
}
}}

このようにすると、解析に失敗した時、以下のように表示されるようになります。

#pre{{

使用法: TestApp -o ファイル [-v]

オプション:

  -o ファイル, --output=ファイル    [必須] 出力先のファイル名
  -v                        ファイルを上書き保存する
  --help                    Display this help screen.

詳しくは、ヘルプをご覧ください。
}}

***ヘルプを表示するオプションを「--help」以外にする [#p48b99c1]

「--help」というオプションによってもGetUsageが返すメッセージを表示することができますが、この名前を変更することもできます。このオプションは、GetUsageメソッドに適用されているHelpOption属性によって決められており、Option属性と同じ方法(前号で説明しています)でオプション名を変更できます。

さらに、HelpOptionもHelpTextプロパティによって、「--help」オプションの説明を記述(変更)できます。

例えばGetUsageメソッドに適用するHelpOption属性を次のように変更してみましょう。

#code(vbnet){{
<CommandLine.HelpOption("?"c, HelpText:="この説明画面を表示します")> _
Public Function GetUsage() As String
}}

#code(csharp){{
//解析に失敗した時に表示するメッセージを返すメソッド
[CommandLine.HelpOption('?' , HelpText="この説明画面を表示します")]
public string GetUsage()
}}

するとメッセージは「-?」で表示できて「--help」では表示できなくなり、メッセージの内容も以下のように変わります。

#pre{{

使用法: TestApp -o ファイル [-v]

オプション:

  -o ファイル, --output=ファイル    [必須] 出力先のファイル名
  -v                        ファイルを上書き保存する
  -?                        この説明画面を表示します

詳しくは、ヘルプをご覧ください。
}}

***エラーの理由によって表示するメッセージを変える [#ib905bbd]

Optionsクラスに、ParserState属性を適用したIParserStateのプロパティを用意すると、解析に失敗した理由を知ることができるようになります。ただし分かるのは、必須のオプションが指定されていない時、フォーマットが間違えている時、排他的なオプションが指定されている時のみです。不明なオプションが付けられていた場合は、GetUsageメソッドは呼び出されますが、この方法ではエラーの原因を知ることはできないようです。

さらに、エラーがあった時は、HelpText.RenderParsingErrorsTextメソッドによってエラーメッセージを自動的に作成できます。

これらを使用した例を以下に示します。

#code(vbnet){{
'オプションをプロパティに持つクラス
Public Class Options
    <CommandLine.Option("o"c, "output", Required:=True, _
        HelpText:="出力先のファイル名", MetaValue:="ファイル")> _
    Public Property OutputFile As String

    <CommandLine.Option("v"c, HelpText:="ファイルを上書き保存する")> _
    Public Property Overwrite As Boolean

    <CommandLine.Option("s"c, HelpText:="出力するファイルのサイズ", _
        MetaValue:="サイズ")> _
    Public Property Size As Integer

    <CommandLine.ParserState> _
    Public Property LastParserState As CommandLine.IParserState

    '解析に失敗した時に表示するメッセージを返すメソッド
    <CommandLine.HelpOption> _
    Public Function GetUsage() As String
        Dim help = New CommandLine.Text.HelpText()
        help.AddDashesToOption = True

        If Me.LastParserState Is Nothing Then
            '--help オプションで表示される時?
            help.AddPreOptionsLine("使用できるオプション一覧:")
        ElseIf Me.LastParserState.Errors IsNot Nothing AndAlso _
            Me.LastParserState.Errors.Count > 0 Then
            'エラーがあった時
            'エラーメッセージを取得する。インデントは2とする。
            Dim errMsg As String = help.RenderParsingErrorsText(Me, 2)
            help.AddPreOptionsLine(errMsg)
        Else
            '不明なオプションが指定されている時?
            help.AddPreOptionsLine("理解できないオプションが含まれています。")
            help.AddPreOptionsLine("以下に使用できるオプションの一覧を示します。")
        End If
        'オプションの説明を追加
        help.AddOptions(Me)
        'オプションの説明の後に改行を入れる
        help.AddPostOptionsLine("")

        Return help
    End Function
End Class
}}

#code(csharp){{
//オプションをプロパティに持つクラス
public class Options
{
    [CommandLine.Option('o', "output", Required = true,
        HelpText = "出力先のファイル名", MetaValue = "ファイル")]
    public string OutputFile
    {
        get;
        set;
    }

    [CommandLine.Option('v', HelpText = "ファイルを上書き保存する")]
    public bool Overwrite
    {
        get;
        set;
    }

    [CommandLine.Option('s',
        HelpText = "出力するファイルのサイズ", MetaValue = "サイズ")]
    public int Size
    {
        get;
        set;
    }

    [CommandLine.ParserState]
    public CommandLine.IParserState LastParserState
    {
        get;
        set;
    }

    //解析に失敗した時に表示するメッセージを返すメソッド
    [CommandLine.HelpOption]
    public string GetUsage()
    {
        var help = new CommandLine.Text.HelpText();
        help.AddDashesToOption = true;

        if (this.LastParserState == null)
        {
            //--help オプションで表示される時?
            help.AddPreOptionsLine("使用できるオプション一覧:");
        }
        else if (this.LastParserState.Errors != null &&
            this.LastParserState.Errors.Count > 0)
        {
            //エラーがあった時
            //エラーメッセージを取得する。インデントは2とする。
            string errMsg = help.RenderParsingErrorsText(this, 2);
            help.AddPreOptionsLine(errMsg);
        }
        else
        {
            //不明なオプションが指定されている時?
            help.AddPreOptionsLine("理解できないオプションが含まれています。");
            help.AddPreOptionsLine("以下に使用できるオプションの一覧を示します。");
        }
        //オプションの説明を追加
        help.AddOptions(this);
        //オプションの説明の後に改行を入れる
        help.AddPostOptionsLine("");

        return help;
    }
}
}}

上記のコードで、必須オプションの欠落とフォーマットのエラーがあった時に表示されるメッセージは、以下のようになります。

#pre{{

  -s option violates format.
  -o/--output required option is missing.


  -o ファイル, --output=ファイル    Required. 出力先のファイル名
  -v                        ファイルを上書き保存する
  -s サイズ                    出力するファイルのサイズ
  --help                    Display this help screen.
}}

さらに説明を補足します。上記の例では、エラーの原因が「this.LastParserState.Errors」にコレクションとして格納されます。「this.LastParserState」がnullの時は「--help」オプションで表示した時で、「this.LastParserState.Errors.Count」が0の時は不明なオプションが指定された時だと思われますが、これはあくまで推測です。

「this.LastParserState.Errors」はParsingErrorオブジェクトのコレクションですが、ParsingErrorクラスが持つ3つのBoolean型のプロパティ、ViolatesRequired、ViolatesFormat、ViolatesMutualExclusivenessによって、エラーの理由が分かります。ViolatesRequiredがTrueなら必須のオプションが指定されていない、ViolatesFormatがTrueならフォーマットが間違いている、ViolatesMutualExclusivenessがTrueなら排他的なオプションが指定されている時です。

また、ParsingErrorオブジェクトのBadOptionプロパティによって、エラーの原因となったオプションの名前が分かります。

これらを駆使してエラーメッセージを自分で作成することができますが、上記の例では手を抜いて、RenderParsingErrorsTextメソッドで自動的に作成しています。RenderParsingErrorsTextメソッドで作成したエラーメッセージは英語ですが、これを日本語にする方法は、後述します。

***自動でメッセージを作成する最も簡単な方法 [#j05d9e29]

HelpText.AutoBuildとHelpText.DefaultParsingErrorsHandlerを使えば、たった1行でメッセージを作成できます。

#code(vbnet){{
'解析に失敗した時に表示するメッセージを返すメソッド
<CommandLine.HelpOption> _
Public Function GetUsage() As String
    'メッセージを自動的に作成する
    Return CommandLine.Text.HelpText.AutoBuild(Me, _
        Sub(current) CommandLine.Text.HelpText.DefaultParsingErrorsHandler( _
            Me, current))
End Function
}}

#code(csharp){{
//解析に失敗した時に表示するメッセージを返すメソッド
[CommandLine.HelpOption]
public string GetUsage()
{
    //メッセージを自動的に作成する
    return CommandLine.Text.HelpText.AutoBuild(this, current =>
        CommandLine.Text.HelpText.DefaultParsingErrorsHandler(this, current));
}
}}

このようにした時に表示されるメッセージは、例えば次にようになります。(ここでは、2つのエラーが発生しています。)

#pre{{
TestApplication 1.0.0.0
Copyright c  2013 DOBON!

ERROR(S):
  -s option violates format.
  -o/--output required option is missing.


  -o ファイル, --output=ファイル    Required. 出力先のファイル名

  -v                        ファイルを上書き保存する

  -s サイズ                    出力するファイルのサイズ

  --help                    Display this help screen.
}}

この中で、「TestApplication」というのはアプリケーションのアセンブリ情報で「タイトル」に指定されている文字列です。同様に、「1.0.0.0」は「アセンブリバージョン」、「Copyright c  2013」は「著作権」、「DOBON!」は「会社名」です。アセンブリ情報を変更すると、ここに表示される内容もそれに合わせて変わります。

#column(補足){{
このようなアプリケーションのタイトルとバージョンの説明はHeadingInfo.Defaultで、著作権と会社名の説明はCopyrightInfo.Defaultで取得できます。
}}

さらに、「AssemblyInfo.cs」(VB.NETでは「AssemblyInfo.vb」)でAssemblyLicense属性を使うとライセンス情報を、AssemblyUsage属性を使うとアプリケーションの使い方を表示できるようになります。

例えばAssemblyInfo.csに次のような記述を追加したとします。

#code(vbnet){{
<Assembly: CommandLine.AssemblyLicense( _
    "このアプリケーションは、MITライセンスで公開します。", _
    "The MIT License (MIT) <http://opensource.org/licenses/mit-license.html>.")> 
<Assembly: CommandLine.AssemblyUsage( _
    "使用法: TestApp -o ファイル [-v] [-s サイズ]", _
    "        TestApp --output=ファイル")> 
}}

#code(csharp){{
[assembly: CommandLine.AssemblyLicense(
    "このアプリケーションは、MITライセンスで公開します。",
    "The MIT License (MIT) <http://opensource.org/licenses/mit-license.html>.")]
[assembly: CommandLine.AssemblyUsage(
    "使用法: TestApp -o ファイル [-v] [-s サイズ]",
    "        TestApp --output=ファイル")]
}}

するとエラーメッセージは、次のように変わります。

#pre{{
TestApplication 1.0.0.0
Copyright c  2013 DOBON!

ERROR(S):
  -s option violates format.
  -o/--output required option is missing.

このアプリケーションは、MITライセンスで公開します。
The MIT License (MIT) <http://opensource.org/licenses/mit-license.html>.
使用法: TestApp -o ファイル [-v] [-s サイズ]
        TestApp --output=ファイル

  -o ファイル, --output=ファイル    Required. 出力先のファイル名

  -v                        ファイルを上書き保存する

  -s サイズ                    出力するファイルのサイズ

  --help                    Display this help screen.
}}

***エラーメッセージを日本語にする [#n1c08a14]

RenderParsingErrorsTextやAutoBuild(DefaultParsingErrorsHandler)では、エラーメッセージが英語で表示されます。しかし、BaseSentenceBuilderの派生クラスを作成すると英語以外でも表示できるようなので、試してみました。

まず以下のようなBaseSentenceBuilderの派生クラス「JapaneseSentenceBuilder」を作成します。

#code(vbnet){{
Public Class JapaneseSentenceBuilder
    Inherits CommandLine.Text.BaseSentenceBuilder
    Public Overrides ReadOnly Property AndWord() As String
        Get
            Return "且つ"
        End Get
    End Property

    Public Overrides ReadOnly Property ErrorsHeadingText() As String
        Get
            Return "エラー:"
        End Get
    End Property

    Public Overrides ReadOnly Property OptionWord() As String
        Get
            Return "オプション"
        End Get
    End Property

    Public Overrides ReadOnly Property RequiredOptionMissingText() As String
        Get
            Return "必須のオプションが指定されていません"
        End Get
    End Property

    Public Overrides ReadOnly Property ViolatesFormatText() As String
        Get
            Return "書式が正しくありません"
        End Get
    End Property

    Public Overrides ReadOnly Property ViolatesMutualExclusivenessText() As String
        Get
            Return "一緒に使用できないオプションが使われています"
        End Get
    End Property
End Class
}}

#code(csharp){{
public class JapaneseSentenceBuilder : CommandLine.Text.BaseSentenceBuilder
{
    public override string AndWord
    {
        get { return "且つ"; }
    }

    public override string ErrorsHeadingText
    {
        get { return "エラー:"; }
    }

    public override string OptionWord
    {
        get { return "オプション"; }
    }

    public override string RequiredOptionMissingText
    {
        get { return "必須のオプションが指定されていません"; }
    }

    public override string ViolatesFormatText
    {
        get { return "書式が正しくありません"; }
    }

    public override string ViolatesMutualExclusivenessText
    {
        get { return "一緒に使用できないオプションが使われています"; }
    }
}
}}

このクラスを使えば、RenderParsingErrorsTextやDefaultParsingErrorsHandlerが返すエラーメッセージを日本語にできます。ただし、先ほどのようにAutoBuildメソッドを使って1発でメッセージを作成する時には、このクラスを使って日本語化する方法が用意されていません。

それでもあえてAutoBuildを使う場合は、例えば以下のような方法が考えられます。

#code(vbnet){{
<CommandLine.HelpOption> _
Public Function GetUsage() As String
    'エラーメッセージのないメッセージを作成
    Dim help = CommandLine.Text.HelpText.AutoBuild(Me)
    '日本語のエラーメッセージを作成
    Dim jhelp = New CommandLine.Text.HelpText(New JapaneseSentenceBuilder())
    CommandLine.Text.HelpText.DefaultParsingErrorsHandler(Me, jhelp)
    '日本語のエラーメッセージを追加
    help.AddPreOptionsLine(jhelp)

    Return help
End Function
}}

#code(csharp){{
[CommandLine.HelpOption]
public string GetUsage()
{
    //エラーメッセージのないメッセージを作成
    var help = CommandLine.Text.HelpText.AutoBuild(this);
    //日本語のエラーメッセージを作成
    var jhelp = new CommandLine.Text.HelpText(new JapaneseSentenceBuilder());
    CommandLine.Text.HelpText.DefaultParsingErrorsHandler(this, jhelp);
    //日本語のエラーメッセージを追加
    help.AddPreOptionsLine(jhelp);

    return help;
}
}}

すると、エラーメッセージは以下のようになります。

#pre{{
TestApplication 1.0.0.0
Copyright c  2013
このアプリケーションは、MITライセンスで公開します。
The MIT License (MIT) <http://opensource.org/licenses/mit-license.html>.
使用法: TestApp -o ファイル [-v] [-s サイズ]
        TestApp --output=ファイル


エラー:
  -s オプション 書式が正しくありません.
  -o/--output 必須のオプションが指定されていません.


  -o ファイル, --output=ファイル    Required. 出力先のファイル名

  -v                        ファイルを上書き保存する

  -s サイズ                    出力するファイルのサイズ

  --help                    Display this help screen.
}}

エラーメッセージが表示される位置が変わりましたが、エラーメッセージが日本語化されました。

***メッセージをコンソール以外に表示する [#u623bf1d]

前に触れましたが、ParserクラスのHelpWriterプロパティによって、メッセージが出力される先が決まります。HelpWriterプロパティにStreamが指定されていればそのStreamに書き込み、nullであればどこにも書き込みません。

Parser.DefaultのHelpWriterはConsole.Errorなので、標準エラー出力ストリームに書き込みます。しかし、新しく作成したParserオブジェクトのHelpWriterはnullなので、どこにも書き込みません。

コンソールアプリケーションであれば標準エラー出力ストリームへの書き込みで問題ないかもしれませんが、Windowsフォームアプリケーションであればそうもいかないでしょう。そのような場合は、例えばParserのHelpWriterプロパティをStringWriterにすることで、メッセージを取得することができます。

以下にその例を示します。

#code(vbnet){{
Module Module1
    'エントリポイント
    Sub Main(args As String())
        'Optionsクラスのインスタンスを作成
        Dim opts = New Options()

        'メッセージを書き込むStringWriterを作成
        Dim sw = New System.IO.StringWriter()
        'Parserを作成して、HelpWriterをStringWriterに変更
        Dim p = New CommandLine.Parser(Sub(w) w.HelpWriter = sw)
        'コマンドライン引数を解析する
        Dim isSuccess As Boolean = p.ParseArguments(args, opts)

        'メッセージを取得する
        sw.Close()
        Dim helpMsg As String = sw.ToString()
        'メッセージがある時は、表示する
        If Not String.IsNullOrEmpty(helpMsg) Then
            Console.WriteLine(helpMsg)
        End If

        Console.ReadLine()
    End Sub
End Module
}}

#code(csharp){{
//エントリポイント
static void Main(string[] args)
{
    //Optionsクラスのインスタンスを作成
    var opts = new Options();

    //メッセージを書き込むStringWriterを作成
    var sw = new System.IO.StringWriter();
    //Parserを作成して、HelpWriterをStringWriterに変更
    var p = new CommandLine.Parser(with => with.HelpWriter = sw);
    //コマンドライン引数を解析する
    bool isSuccess = p.ParseArguments(args, opts);

    //メッセージを取得する
    sw.Close();
    string helpMsg = sw.ToString();
    //メッセージがある時は、表示する
    if (!string.IsNullOrEmpty(helpMsg))
    {
        Console.WriteLine(helpMsg);
    }

    Console.ReadLine();
}
}}

Command Line Parser Libraryにはまだ紹介していない機能が多数ありますが、長々書きすぎてしまった感がありますので、この辺で終わりにしたいと思います。

**コメント [#cc3fe02d]
#comment

//これより下は編集しないでください
#pageinfo([[:Category/.NET]] [[:Category/ASP.NET]],2013-04-15 (月) 03:58:19,DOBON!,2013-04-15 (月) 03:58:19,DOBON!)

[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]