2014/01/04

[プログラミング]通貨のレート計算

,
 3つめは"A.2 Currency Calculator"です。通貨のレート計算ですね。問題文を読めば分かりますが、仕様は
  • 引数に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したりするのも大変そう)
  • じゃあ元からデータで持っておこう
という話でした。"JPY"で他通貨取得の方がいいのかなあ…

 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

でもコメントの//後に半角開けるのって気持ち悪い…

0 コメント to “[プログラミング]通貨のレート計算”

コメントを投稿