[java] 汇率换算器实现(2)
[java] 汇率换算器实现(2)
Table of Contents
1 系列文章地址
2 前言
在上篇文章中, 我们实现了汇率换算器的最简单的版本, 实现了:
- 帮助信息的提示
- 汇率表的输入
- 错误输入的处理
- 汇率计算的输入
- 汇率计算结果的输出
不同类之间的关系图如下(不是严格按照uml规则绘制的):
在接下来的内容中主要介绍如何实现汇率表的实时更新.
3 获取实时汇率信息
想要获取汇率的实时信息, 很容易想到的方法就是从一个网页中提取相应的汇率信息, 填充到当前的汇率表内. 接着自然想要, 使用java进行network programming, 可以借用java.net库.
3.1 获取网页内容
class Rate { // 从网站:http://www.usd-cny.com/中获取最新的汇率信息 final static String webSite = "http://www.usd-cny.com/"; // 利用hashtable对不同货币之间的利率进行存储 // key: $from+$to, value: $rate private static Hashtable rateTable = new Hashtable(); // 获取网页内容 public static void update() throws Exception { URL hp = new URL(webSite); URLConnection hpCon = hp.openConnection(); System.out.println("== Content =="); InputStream input = (InputStream)hpCon.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(input, "gb2312")); String str = null; while (( str = br.readLine() ) != null) { System.out.println(str); } input.close(); } }
接下来要干的活就是从获得的网页内容中提取出汇率表信息了. 网页上显示的格式如下:
3.2 提取web表单
为了存储表单中提取的信息, 建立了一个新类: class RateInfo
class RateInfo { String to; // [0]: 现汇买入价 [1]: 现钞买入价 // [2]: 卖出价 [3]: 中间价 [4]: 基准价 Double price[] = new Double[5]; }
具体的实现可以通过两种方式:
- 正则表达示匹配
- 借用现有的库, jsoup
该文主要实现了第一种方式, 这里再次说明一下,在后续的文章中会介绍关于jsoup的使用。
4 正则表达示匹配获取表单信息
首先让我们来观察一下需要处理的网页的source code.
<TABLE BORDER CELLSPACING=0 BORDERCOLOR="#CCCCCC" CELLPADDING=0 WIDTH=777 align="center" height="113"> <TR bgcolor="#E1FDE8"> <TD WIDTH="121" HEIGHT=28 bgcolor="#E1FDE8"> <DIV ALIGN="center"><b><font color="#000000"><font lang="ZH-CN">货币名称</font></font> </b> </TD> <TD WIDTH="133" HEIGHT=21> <DIV ALIGN="center"><b><font color="#000000"><font lang="ZH-CN">现汇买入价</font></font> </b> </TD> <TD WIDTH="137" HEIGHT=21> <DIV ALIGN="center"><b><font color="#000000"><font lang="ZH-CN">现钞买入价</font></font> </b> </TD> <TD WIDTH="132"><div align="center"><b><font color="#000000"><font lang="ZH-CN">卖出价</font></font></b></div></TD> <TD WIDTH="130"><div align="center"><b><font color="#000000">中间价</font></b></div></TD> <TD WIDTH="133" HEIGHT=21> <DIV ALIGN="center"><b><font color="#000000">基准价</font> </b> </TD> </TR> <TR bgcolor="#FFFFFF"> <TD HEIGHT=28 valign="middle" bgcolor="#FFFFFF"> <div align="center"><!--?hbmc=美元&topic="--><a href="http://www.usd-cny.com/usd-rmb.htm">美元 USD</a></div></TD> <TD HEIGHT=15 align="center" valign="middle"><div align="right">621.8700 </div></TD> <TD HEIGHT=15 align="center" valign="middle"><div align="right">616.8900 </div></TD> <TD align="center" valign="middle"><div align="right">624.3700 </div></TD> <TD align="center" valign="middle"><div align="right">623.1200 </div></TD> <TD HEIGHT=15 align="center" valign="middle"><div align="right">615.5700 </div></TD> </TR>
分析发现, 汇率表由大写的TABLE包括起来, 每一行由TR包围, 每一项由TD包围. 因此, 正则表达式为:
- <TABLE: means enter the table
- </TABLE: means quit the table
- <TR: means enter a row
- </TR: means quit a rwo
- <TD && href: means first col
- <TD && ~href: means other cols
- <TD.*<a href.*?>(.*?)</a>: now "1" means "美元 USD"
- <TD.*<div.*?>(.*?)</d: now 1 meas the rates
对于上述字符的识别可以用以下方式进行实现:
str.startwith(token), where token = "<TABLE" or "</TABLE" or "<TR" or "</TR" str.startwith("<TD") && str.indexOf("href) != -1, to recognise the first col str.startwith("<TD") && str.indexOf("href) == -1, to recognise the other cols to extract the unit of the money import java.util.regex.*; String patt = "<TD.*<a href.*?>\(.*?\)</a>" Pattern r = Pattern.compile(patt); Matcher m = r.matcher(str); m.group(1); // is the result, such as "美元 USD" 用类似的方法可以获得rates
然后编写代码进行实现, 具体实现思路如下:
- 实现html源代码中关于汇率表的相关内容行的识别
- 然后, 从特定的行中提取出相应的汇率信息
class Rate {
// 从网站:http://www.usd-cny.com/中获取最新的汇率信息
final static String webSite = "http://www.usd-cny.com/";
// 利用hashtable对不同货币之间的利率进行存储
// key: $from+$to, value: $rate
private static Hashtable rateTable = new Hashtable();
// 从网上自动更新汇率信息
// 只将前16个具有完整汇率信息的内容进行存储
public static void update() throws Exception {
URL hp = new URL(webSite);
URLConnection hpCon = hp.openConnection();
System.out.println("== Content ==");
InputStream input = (InputStream)hpCon.getInputStream();
BufferedReader br = new BufferedReader(new
InputStreamReader(input, "gb2312"));
String str = null;
boolean inTable = false;
int nRows = 0;
String matchStr = null;
while (( str = br.readLine() ) != null) {
str = str.trim();
// 判断是否进入汇率表的势力范围内部
if (str.startsWith("<TABLE")) {
inTable = true;
continue;
}
if (str.startsWith("</TABLE")) {
break;
}
if (inTable == false)
continue;
if (str.startsWith("<TR")) {
nRows += 1;
// 忽略第一行的标题
if (nRows == 1) continue;
// 汇率表的读取只到港币
if (nRows == RateInfo.NKINDS+2) break;
// 获得第一列的完整代码
str = br.readLine().trim();
str = str + br.readLine().trim();
// 获取币种缩写
String patt = "<TD.*<a href.*?>(.*)</a>";
Pattern r = Pattern.compile(patt);
Matcher m = r.matcher(str);
// matchStr = m.group(1);
// 将汉字与缩写进行分离
// matchStr = (matchStr.split())[1];
if (m.find()) {
matchStr = m.group(1);
matchStr = (matchStr.split(" "))[1];
System.out.println(matchStr);
} else {
System.out.println("No Match");
}
for (int i = 0; i < RateInfo.NELEM; i++) {
str = br.readLine();
String pattE = "<TD.*>(.*?) </div>";
r = Pattern.compile(pattE);
m = r.matcher(str);
if (m.find())
System.out.println(m.group(1));
else
System.out.println("No Match");
}
}
}
input.close();
}
// 设置不同货币之间的利率
// 1 $from * $rate = 1 $to
public static void setRate(String from, String to, double rate) {
rateTable.put(from+to, new Double(rate));
}
public static Double getRate(String from, String to) {
return 615.65;
// return (Double) rateTable.get(from + to);
}
// 将一定量的货币$m, 转变成单位为$to的货币量
// return: 相应的货币值
public static Money exchangeRate(Money m, String to) {
if (m.unit.equals(to)) return new Money(m);
Double rate = getRate(m.unit, to);
if (rate == null) {
throw new IllegalArgumentException();
}
return new Money(m.amount*rate.doubleValue(), to);
}
}
5 总结
该文实现了利用正则表达式获取html表单信息. 但是, 该方法主要有的不足是: 具有太明显的目的性, 根据具体html代码的特征实现相应的匹配. 为了实现更普遍的方法, 应该对该匹配规则进行改进, 下一篇文章种将对该方法进行进一步的改进.