2014年07月06日

C#で起動オプションの解析

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

前回に引き続き、コマンドラインのパラメータ解析の問題です。

■問題 (※ ごめんなさい、出題者の方が分かりません)
次の起動インタフェースを持つコマンドを作成してください。

書式:cmdopt -o [-q] [-d{0|1|2}] 文字列 [文字列 ...]

書式を説明すると
- オプション「o」
  必須オプションです。指定されていない場合、異常終了してください。
- オプション「q」
  選択オプションです。
  省略されていても問題有りません。
- オプション「d」
  引数付きオプションです。
  「0」「1」「2」のいずかが続いて指定されます。
- 文字列
  パラメータです。
  1つ以上であればいくつでも指定できます。
  指定されていなかった場合、異常終了してください。

オプションの開始が「-」になっていますが
「+」や「/」でもかまいません。
余力があればロングオプションに対応してもよいです。

起動例:(すべて許容されるのが望ましいです)
1. cmdopt -o AAA
2. cmdopt -o AAA BBB CCC
3. cmdopt -oq AAA
4. cmdopt -o  -q AAA
5. cmdopt -o -d1 AAA
6. cmdopt -o -d 1 AAA
7. cmdopt -q -d2 -o AAA


出力例:
[オプション情報]
o(output): ON|OFF
q(quote): ON|OFF
d(debug): 0|1|2

[パラメータ情報]
指定数: N
1: 文字列1
2: 文字列2
...
N: 文字列N
 

CommandOptionsというクラスを定義し、そこに解析した結果を入れています。 オプション o, q, d は大文字でもOKにしています。

この手のコードって、どうしても力技になっちゃいますね。 もっと、スマートな方法はないのかなーといつも思うのですが...

「指定されていない場合、異常終了してください。」の異常終了は、例外を発生させるのでは なく、標準エラー出力にメッセージを表示し、プログラムを正常終了させています。まあ当たり前か...

■C#で書いたコード

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Doukaku.Org { class Program { static void Main(string[] args) { CommandOptions co = GetOptions(args); if (co.OptionOutput == false) StopProgram("Oの指定がありません"); if (co.Parameters.Count == 0) StopProgram("文字列の指定がありません"); if (0 > co.OptionDebug || co.OptionDebug > 2) StopProgram("Dオプションは、0|1|2のいずれかです"); Console.WriteLine("[オプション情報]"); Console.WriteLine("o(output):{0}", co.OptionOutput ? "ON" : "OFF"); Console.WriteLine("q(quote):{0}", co.OptionQuote ? "ON" : "OFF"); Console.WriteLine("d(debug):{0}", co.OptionDebug); Console.WriteLine("[パラメータ情報]"); Console.WriteLine("指定数: {0}", co.Parameters.Count); for (int i = 0; i < co.Parameters.Count; i++) { Console.WriteLine("{0}:{1}", i, co.Parameters[i]); } } private static void StopProgram(string msg) { Console.Error.WriteLine(msg); Environment.Exit(1); } static private CommandOptions GetOptions(string[] args) { CommandOptions co = new CommandOptions(); bool dopt = false; co.Parameters = new List<string>(); foreach (var arg in args) { if (arg[0] == '-' || arg[0] == '/') { for (int i = 1; i < arg.Length; i++) { switch (char.ToUpper(arg[i])) { case 'O': co.OptionOutput = true; break; case 'Q': co.OptionQuote = true; break; case 'D': dopt = true; if (i + 1 < arg.Length) { int num = arg[++i] - '0'; co.OptionDebug = num; dopt = false; } break; } } } else { if (dopt) { int num = -1; int.TryParse(arg, out num); co.OptionDebug = num; } else { co.Parameters.Add(arg); } dopt = false; } } return co; } } public class CommandOptions { public bool OptionOutput { get; set; } public bool OptionQuote { get; set; } public int OptionDebug { get; set; } public List<string> Parameters { get; set; } } }

GetOptionsメソッドを、ちょっと手直ししたバージョンも載せておきます。
こっちのほうが少し簡潔になったかな。
        static private CommandOptions GetOptions(string[] args) {
            CommandOptions co = new CommandOptions();
            co.Parameters = new List<string>();
            var ite = args.GetEnumerator();
            while (ite.MoveNext()) {
                string arg = (string)ite.Current;
                if (arg[0] == '-' || arg[0] == '/') {
                    for (int i = 1; i < arg.Length; i++) {
                        switch (char.ToUpper(arg[i])) {
                        case 'O':
                            co.OptionOutput = true;
                            break;
                        case 'Q':
                            co.OptionQuote = true;
                            break;
                        case 'D':
                            int num = -1;
                            if (i + 1 < arg.Length) {
                                num = arg[++i] - '0';
                            } else {
                                ite.MoveNext();
                                int.TryParse((string)ite.Current, out num);
                            }
                            co.OptionDebug = num;
                            break;
                        }
                    }
                } else {
                    co.Parameters.Add(arg);
                }
            }
            return co;
        }  

  

Posted by gushwell at 22:20Comments(0)TrackBack(0)

2014年07月02日

C#でコマンドライン引数の取得

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

■問題 (出題者:dankogai さん)
以下にけだし同感なので。
inferno :: どう書く?orgは多言語クックブックになれるか > 一般投稿が可になった時に、ちょっと感じてたんですがやっぱり最近ある傾向が顕著で。というのは数学パズル系とか、(数学的な、事務処理などではない)アルゴリズム勝負!なお題ばっかりなんですよね。

というわけで、たまには簡単でその場で答えが出て、なによりある言語使いにとって「外国語」ではこういうんだというのがわかる問題として考えてみました。 % program a b c d で a, b, c, d を得るにはどうしたらよいかという、それこそネイティブには刺身タンポポより簡単だけど、「外国人」にはとっさに浮かばないという問題です。 Dan the Practical Programmer

2つのやり方で書いてみました。

■C#で書いたコード
using System;
using System.Linq;

namespace Doukaku.Org {
    static class Program {
        static void Main(string[] args) {
            // Mainメソッドのargs引き数を利用する方法
            foreach (var s in args) {
                Console.WriteLine(s);
            }


            // Environment.GetCommandLineArgsメソッドを利用する方法
            var args2 = Environment.GetCommandLineArgs().Skip(1);
            foreach (var s in args2)
                Console.WriteLine(s);
        }
    }
}
  
Posted by gushwell at 23:30Comments(0)TrackBack(0)