6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Linqを使ってコマンドライン引数をシンプルにパースする

Posted at

コマンドライン引数と言えばあれです

>hoge.exe a b c d

みたいなやつ。 このa b c d が↓の例だとそれぞれエントリポイントであるMain関数の引数(args)に入りますね。

public static void Main(string[] args){
    //args[0] = "a" [1] = "b" [2] = "c" [3] = "d" 
}

しかし、これだと

  • 引数の省略が出来ない
  • 引数の順番が固定される
  • 引数指定が分かりづらい(何番目に何を入れる。とか覚える必要がある)

という欠点があります。そのため、

>hoge.exe in=d:\huga.txt out=d:\test override=true 

せめてこんな感じでurlパラメータみたいに、=で指定したいところ。
では、これをプログラムで受け取った時にどう解析(パース)するか色々な方法はありますが…(本題)

Linqを使う

Linqを使えば1行です。(引数の変数は string[] args とする)

var argsDict = args.Select(arg => arg.Split('=')).Where(s => s.Length == 2).ToDictionary(v => v[0], v => v[1]);

シンプル!

一応説明をすると、まず
args.Select(arg => arg.Split('='))
で、引数で渡されているstring[]の要素一つ一つを'='で分割、in=d:\huga.txt なら、 [0]="in",[1]="d:\huga.txt" に分割されます。 この時点で、 string[]IEnumerable<string[]> に(=で分割されたstring[]が列挙)

次に
.Where(s => s.Length == 2)
で、=で分割されたstring[]の要素数が2のものだけに厳選。 引数からイコールが入っていないパターンと a=b=c のように、イコールが2つ以上ある物を弾いてます。

最後に
.ToDictionary(v => v[0], v => v[1]);
で、=で分割されたstring[]の[0]の方をKeyに、[1]の方をValueにしたDictionaryに変換して完了です。
(一つ前のWhereで要素数2が確定しているので、範囲チェックが必要ない)

これで、

>hoge.exe in=d:\huga.txt out=d:\test override=true 

この引数だとして

using System;
using System.Linq;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var argsDict = args.Select(arg => arg.Split('=')).Where(s => s.Length == 2).ToDictionary(v => v[0], v => v[1]);

            Console.WriteLine("in:" + argsDict["in"] );
            Console.WriteLine("out:" + argsDict["out"] );
            Console.WriteLine("override:" + argsDict["override"] );
        }
    }
}
出力
in:d:\huga.txt
out:d:\test
override:true
続行するには何かキーを押してください . . .

このように、Dictionry化されているので、使いやすい!!

もちろん世の中にはたくさん高機能なパーサーがあるので、それを使えばいいと言えばそれまでなんですが。
ちょっとテストで作ってるコンソールアプリにライブラリとか、手の込んだパーサー入れたくない。 という人(例えば自分)には手軽で良いと思います。

おまけ

ちょっとまたんか、引数省略すると例外発生するではないか!!
まぁ、そりゃそうですね。 このプログラムを引数指定しないで実行すると、

ハンドルされていない例外: System.Collections.Generic.KeyNotFoundException: 指定されたキーはディレクトリ内に存在しません でした。

と例外発生してしまいます。

まぁ、そこは良しなにContainsKeyとかやってくだされば~というのは簡単ですが。

C#7から、out変数宣言の拡張がされたので、これまた1行で済むと言えば済みます。

var inData = argsDict.TryGetValue("in",out var value) ? value : null;

これでinDataには、コマンドライン引数で in=*** が指定されている時には *** の部分が入りますし、指定されていない場合はnullになります。
やったね!

まぁ、個人的にはこういう拡張メソッドを用意したほうが良いと思いますけれど。

それでは。

6
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?