3つめは"A.2 Currency Calculator"です。通貨のレート計算ですね。問題文を読めば分かりますが、仕様は
幸いにして、ネット上で最新の通貨レートのデータを取得できるサイトがあります。
クジラ 外国為替 確認 API (為替 RSS) - http://api.aoikujira.com/kawase/
ほんとはこのデータ元のXurrencyのAPIを使おうと思っていたのですが、Pricingページに"only 29,99 eruos per year"と書かれてたので諦めました。
なんとかXML形式なら扱えるだろう、ということで、今回はこれを使うことにします。
XML作成ですが、面倒だからxmlns名前空間とかは特に指定してないのですが、多分したほうがいいのですね…
<Currencies>
<Currency>EUR</Currency>
<Currency>GBP</Currency>
</Currencies>
のようなXMLファイルを作成しています。最初にルートのCurrencies要素を作って、その子としてcurrencyListの各要素を追加しています。
C# のコーディング規則 (C# プログラミング ガイド) - http://msdn.microsoft.com/ja-jp/library/ff926074.aspx
でもコメントの//後に半角開けるのって気持ち悪い…
Read more →
- 引数に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
でもコメントの//後に半角開けるのって気持ち悪い…
