- 引数に10進数と、2つのISO 4217準拠の通貨コードみたいなのを与えるので、もう一つの通貨でいくらになるか計算する
- プログラムで扱えない通貨コードならエラーを吐く
- 引数が無い場合は、てきとーな解説を吐く
むずかしいところ - 通貨レートの取得
さて、これを実現するのには一つ問題があります。それは「変動する通貨レートを、どうやって取得するか」です。問題の出題者さんは「1$ = \100だよね!」みたいな感じで軽く出題したのかもしれませんが…幸いにして、ネット上で最新の通貨レートのデータを取得できるサイトがあります。
クジラ 外国為替 確認 API (為替 RSS) - http://api.aoikujira.com/kawase/
ほんとはこのデータ元のXurrencyのAPIを使おうと思っていたのですが、Pricingページに"only 29,99 eruos per year"と書かれてたので諦めました。
なんとかXML形式なら扱えるだろう、ということで、今回はこれを使うことにします。
コード
とりあえずコード貼っておきます。using System; using System.Linq; using System.Xml.Linq; namespace Rate1 { class Program { /// <summary> /// 通貨リストを作成する関数。といっても面倒なので、currencies.xmlを作る。 /// </summary> static void MakeCurrencyList() { // Currency List(Compliant to ISO 4217) can be get from http://api.aoikujira.com/kawase/ // (you should replace ", " to "\", \"") //var currencyList = new List<string> {"eur", "gbp", "aud", "brl", "cad", "chf", "cny", "dkk", "hkd", "inr", "jpy", "krw", "lkr", "myr", "nzd", "sgd", "twd", // "zar", "thb", "sek", "nok", "mxn", "bgn", "czk", "huf", "ltl", "lvl", "pln", "ron", "isk", "hrk", "rub", "try", "php", // "cop", "ars", "clp", "svc", "tnd", "pyg", "mad", "jmd", "sar", "qar", "hnl", "syp", "kwd", "bhd", "egp", "omr", "ngn", // "pab", "pen", "ils", "uyu", "usd"}; //var savexml = new XElement("Currencies"); //savexml.Add(currencyList.Select(item => new XElement("Currency", item.ToUpper()))); //savexml.Save(@"currencies.xml"); } static void Main(string[] args) { // 使用できる通貨リストを、currencies.xmlから読み込む。 var path = @"currencies.xml"; var currencyxml = XElement.Load(path); var currencyList = from currency in currencyxml.Elements() select currency.Value; // 引数のチェックをする if (3 > args.Length) { Console.WriteLine("args < 3"); return; } // パラメータ(価格、元の通貨、相手先の通貨) var price = 0.0; try { price = double.Parse(args[0]); } catch (FormatException fe) { Console.WriteLine(fe.Message); return; } var srcCurrency = args[1].ToUpper(); var dstCurrency = args[2].ToUpper(); // 扱ってない通貨を指定すると、前の変換結果が返ってくる。 // なので、仮にこういうエラーチェックしてる。 if(!(currencyList.Contains(srcCurrency))){ Console.WriteLine(srcCurrency + @"は扱えないです"); return; }else if(!(currencyList.Contains(dstCurrency))){ Console.WriteLine(dstCurrency + @"は扱えないです"); return; } // レート表のxmlをDLする var url = @"http://api.aoikujira.com/kawase/xml/" + srcCurrency.ToLower(); var elem = XElement.Load(url); // xmlのkawase/resultがokでなかったらエラー if (elem.Element("result").Value != "ok") { Console.WriteLine("XML result isn't ok"); } else { // xmlからレートを取得する var rates = from p in elem.Elements() where p.Name.LocalName == dstCurrency select p.Value; foreach (var rate in rates) { // 結果を表示する(結果は1つのはず…) try { Console.WriteLine("{0} {1} == {2} {3}", price, srcCurrency, price * double.Parse(rate), dstCurrency); } catch (FormatException fe) { Console.WriteLine(fe.Message); return; } } } } } }コードは大きく分けてMakeCurrencyList()とMain()の2つです。前者は、「プログラムが対応している通貨レートを保存しておくため、対応通貨を書いたXMLを作成する」関数です。それもネットから取得してくればいーじゃんと思うかもですが、
- そもそも上のAPIは、Invalidな通貨を指定すると、前に取得が成功したデータが返ってくる(キャッシュ?)(/kawase/iiiとかにすると、その前に指定した/kawase/jpyとかが来た) → よって、返ってきたデータで通貨対応してるか判断できない
- 1つの通貨を指定すると、対応した他の(54種の)通貨全てとのレートが出るけど、これを用いて対応通貨を取得するとしても、最初の1つは指定しないといけない
- そもそもXurrencyも上のサイトも、対応通貨を別表で配布したりはしてない(HPにはテキストで書いてある、けどHTMLをDOMしたりするのも大変そう)
- じゃあ元からデータで持っておこう
XML作成ですが、面倒だからxmlns名前空間とかは特に指定してないのですが、多分したほうがいいのですね…
var savexml = new XElement("Currencies"); savexml.Add(currencyList.Select(item => new XElement("Currency", item.ToUpper()))); savexml.Save(@"currencies.xml");ここでは、XElementを用いて、
<Currencies>
<Currency>EUR</Currency>
<Currency>GBP</Currency>
</Currencies>
のようなXMLファイルを作成しています。最初にルートのCurrencies要素を作って、その子としてcurrencyListの各要素を追加しています。
// パラメータ(価格、元の通貨、相手先の通貨) var price = 0.0; try { price = double.Parse(args[0]); } catch (FormatException fe) { Console.WriteLine(fe.Message); return; }ここはargs[0]( = 元の通貨での額)をdoubleにしてます。Parse()出来なかった時のためにtry~catchしてますが、「tryスコープ中で変数を宣言すると、tryのスコープが終わった瞬間に見えなくなる」という厄介な問題があるので、最初にわざわざ書いてます。もしかしたらTryParse()の方がいいのかもしれない…
コーディング規則
オンリーワンなコーディング規則、多分あると思うんですけど、天下のMicrosoftさんが「こう書け!」って言ってるみたいですので、今回はこれになるべく沿うように書きました。C# のコーディング規則 (C# プログラミング ガイド) - http://msdn.microsoft.com/ja-jp/library/ff926074.aspx
でもコメントの//後に半角開けるのって気持ち悪い…