[java] 汇率换算器实现(2)

 

[java] 汇率换算器实现(2)

 

[java] 汇率换算器实现(2)

1 系列文章地址

2 前言

在上篇文章中, 我们实现了汇率换算器的最简单的版本, 实现了:

  • 帮助信息的提示
  • 汇率表的输入
  • 错误输入的处理
  • 汇率计算的输入
  • 汇率计算结果的输出

不同类之间的关系图如下(不是严格按照uml规则绘制的): //images0.cnblogs.com/blog/188068/201405/082238273389754.jpg

在接下来的内容中主要介绍如何实现汇率表的实时更新.

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();
    }
}
   

接下来要干的活就是从获得的网页内容中提取出汇率表信息了. 网页上显示的格式如下: //images0.cnblogs.com/blog/188068/201405/082238288075769.jpg

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&nbsp;</div></TD>
    <TD HEIGHT=15 align="center" valign="middle"><div align="right">616.8900&nbsp;</div></TD>
    <TD align="center" valign="middle"><div align="right">624.3700&nbsp;</div></TD>
    <TD align="center" valign="middle"><div align="right">623.1200&nbsp;</div></TD>
    <TD HEIGHT=15 align="center" valign="middle"><div align="right">615.5700&nbsp;</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.*>(.*?)&nbsp;</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代码的特征实现相应的匹配. 为了实现更普遍的方法, 应该对该匹配规则进行改进, 下一篇文章种将对该方法进行进一步的改进.

Date: 2014-05-07 Wed

Author: Zhong Xiewei

Org version 7.8.11 with Emacs version 24

Validate XHTML 1.0
posted @ 2014-05-08 22:37  grassofsky  阅读(3419)  评论(2编辑  收藏  举报