这次写的这段代码是关键字模糊查询。Steam上有许多游戏。用户输入一段查询文字,然后程序会比较所有保存的条目的键,将符合查询内容的条目按照相关度从大到小打印出来。
如果输入的查询内容没有有效字符则提示信息不进行查询。
如果输入的查询内容没有匹配的条目,也会给出提示。
因为网页还不是很懂,所以是将网页上的内容直接复制到本地txt。然后读取文档将游戏信息抽出封装成对象,再添加到集合中。后期学会如何读取网页信息后就可以实现实时查询了!
其实想做的是将所有匹配的结果列出后用户可以点击选择自己想要查询的游戏,然后再显示出这个游戏的相关信息。目前能想到的是将匹配的结果做成Label显示在窗体上,当用户点击某个Label就表示这个Label是用户想要的结果。然后再返回游戏信息。
/**
* 需求:模糊查询游戏名
* 输入:用户输入查询内容
* 输出:按照相关度从高到低排列的查询结果
* 数据来源:http://www.steamcardexchange.net/index.php?badgeprices。为简化,目前是将此页面信息复制到本地TXT。内容格式如下:
0RBITALIS 6 0.97
1... 2... 3... KICK IT! (Drop That Beat Like an Ugly Baby) 6 1.15
1Heart 6 1.63
1Quest 6 0.63
*游戏名\t卡牌数\t徽章价格\r\n
*
* 步骤:
* 0.为了保存游戏卡牌信息建立一个Card类 有三个字段存放信息:private String name;private int num;private double setPrice;
* 1.加载“数据库”:
* 1)输入:文件 FileReader,为了方便读取,使用BufferedReader.
* 2)用split("\t")分割每行内容并保存在一个TreeMap中。key=游戏名。value=Card对象。
* 2.用户输入查询内容。为了简化,这里暂时是代码里输入了一个具有代表性的查询内容。
* 3.处理用户输入的内容。为了实现模糊查询,我使用了以下方法:
* 1)将输入拆分成单词。然后每个拆出的单词都作为正则表达式去和“数据库”的键进行匹配。使用表达式:"\\b\\w+\\b"
* 2)拆分的原则是a.单词b.去掉特殊符号。像"aasd121da asd31 a412a !ar3@ #3! @13fa 133# !a@b#"这段文字,可以拆出aasd121da,asd31,a412a,ar3,3,13fa,133,a,b这几个单词。
* c.考虑到1个字符去比较是比较没有意义的事情,所以只保留2个字符以上的单词。比如"!a@b#"虽然能匹配出a,b但是因为是单字符,所以这两个都抛弃。
* 3)将结果保存进一个ArrayList<String>中。如果列表长度为0,则说明没有有效输入,这时候就不要再浪费资源进行下一步,直接提示输入无效后返回。
* 4.建立一个保存匹配结果的TreeMap<String,Integer>,String代表KEY,Integer标记这个键被匹配到的次数(单词可能有多个)。次数越多表示相关性越大。
* 5.将每个捕获的单词都作为表达式,在循环中与数据库的keySet()进行匹配,每当匹配到一个新的KEY就添加到保存匹配结果的Map中。每当匹配到一个结果中已存在的KEY,就取出其标记次数,并+1后再存入。
* 6.如果TreeMap<String,Integer>的大小为0说明没有匹配结果,就给用户提示然后返回,不要浪费资源进行下一步。
* 7.将这个TreeMap的entrySet()转换成一个ArrayList<Map.Entry<String,Integer>>,然后用Collections。sort(list,comparator)进行排序。排序方法用自定义的比较器制定:按照Value值大小降序排序。打印出来。
*
*/
package test;
import java.io.*;
import java.util.*;
import java.util.regex.*;
class Card// 此类在此不是重点。不讨论
{
private String name;
private int num;
private double setPrice;
private double avrValueInUSD;
private double avrValueInCNY;
public Card(String name, int num, double setPrice) {
super();
this.name = name;
this.num = num;
this.setPrice = setPrice;
calVal();
}
@Override
public String toString() {
return "Card [name=" + name + ", num=" + num + ", setPrice=" + setPrice
+ "]";
}
public String getName() {
return name;
}
public int getNum() {
return num;
}
public double getSetPrice() {
return setPrice;
}
private void calVal() {
double obtain = Math.ceil(num / 2);
avrValueInUSD = setPrice / num * obtain - obtain * 0.02;
avrValueInCNY = avrValueInUSD * 6.25;
}
}
public class TestStringSearch {
public static void main(String[] args) throws Exception {
TreeMap<String, Card> tm = (TreeMap<String, Card>) loadData();
// testRegex();
// search for the game: //N.P.P.D.RUSH// - The milk of Ultra violet
search("NPPD.RUSH The milk of Ultra violet", tm.keySet());
// search("!@#99!@# !@#!@#",tm.keySet());
}
private static void search(String inputWords, Set<String> dbSet) {
// 切割用户的查询内容,将之按照单词划分出来。
// String
// input="//N.P.P.D.RUSH// - The milk of Ultra violet"/*"aasd121da asd31 a412a !ar3@ #3! @13fa 133# !a@b#"*/;
// String regex="\\b[.\\w]+\\b";//这个式子可以匹配出N.P.P.D.RUSH
String regex = "\\b\\w+\\b";// 这个式子可以匹配出RUSH
Pattern p4Regex = Pattern.compile(regex);
Matcher m4Regex = p4Regex.matcher(inputWords);
// 将查询单词存入表达式列表中。
ArrayList<String> regexList = new ArrayList<String>();
String tmp = null;
while (m4Regex.find()) {
tmp = m4Regex.group();
if (tmp.length() > 1) {// 当匹配结果的长度只有1,就是1个字母,拿1个字母去做表达式,结果太模糊。所以放弃。之所以会做这个判断是因为有的游戏名包含//N.P.P.D.RUSH//
// System.out.println(tmp);
regexList.add(tmp);
}
}
// 如果有效单词结果为0就不要再去查询“数据库”了,提示信息后直接返回即可。
if (0 == regexList.size()) {
System.out.println("InValid words with your search:\"" + inputWords
+ "\".");
return;
}
// 将所有和查询单词匹配的键全部存入匹配结果的MAP中。并为每个键维护一个int数值,标记键与查询单词匹配的次数。
TreeMap<String, Integer> matchResults = new TreeMap<String, Integer>();
int cntInputs = regexList.size();
Pattern p4Search = null;
Matcher m4Search = null;
while (0 < cntInputs--) {
for (String input : dbSet) {
p4Search = Pattern.compile(regexList.get(cntInputs),
Pattern.CASE_INSENSITIVE);// 忽略大小写
m4Search = p4Search.matcher(input);
if (m4Search.find()) {
if (matchResults.containsKey(input))// 如果已存入,则取出标记次数并+1再存回去。
{
matchResults.put(input, matchResults.get(input) + 1);
} else {// 如果没存入则新增一个条目
matchResults.put(input, 1);
}
}
}
}
// 如果查询结果为0,提示信息后直接返回即可。
if (0 == matchResults.size()) {
System.out.println("Could not find the result with your search:\""
+ inputWords + "\".");
return;
}
// 将匹配结果按照标记次数排列,已备之后由用户选择匹配出的结果。
// 也就是把MAP按照value降序排列
// 先将map的条目转换成list
List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(
matchResults.entrySet());
// 用sort排序,自定义一个比较器
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
@Override
// 让value大的排在前面。
public int compare(Map.Entry<String, Integer> arg0,
Map.Entry<String, Integer> arg1) {
return arg1.getValue().compareTo(arg0.getValue());
}
});
System.out.println("There are " + list.size()
+ " results match your search \"" + inputWords + "\":");
int cntResults = 0;
for (Map.Entry<String, Integer> me : list) {
System.out.println((++cntResults) + ":\t[" + me.getKey()
+ "]......with [" + me.getValue() + "] relevance.");
// 为了控制输出内容的数量,可以做个限制
if (10 <= cntResults) {
System.out.println("Other results have less relevance.");
break;
}
}
}
private static void testRegex() {
String input = "//N.P.P.D.RUSH// - The milk of Ultra violet"/* "aasd121da asd31 a412a !ar3@ #3! @13fa 133# !a@b#" */;
// String regex="\\b[.\\w]+\\b";//这个式子可以匹配出N.P.P.D.RUSH
String regex = "\\b\\w+\\b";// 这个式子可以匹配出RUSH
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
while (m.find()) {
String tmp = m.group();
if (tmp.length() > 1) {// 当匹配的长度只有1,就是1个字母,拿1个字母去做表达式,结果太模糊。所以放弃。之所以会做这个判断是因为有的游戏名包含//N.P.P.D.RUSH//
System.out.println(tmp);
}
}
}
private static Map loadData() throws Exception {
File file = new File(
"G:\\Codes\\Language\\JAVA\\Project\\Study_Prime\\steam",
"db.txt");
TreeMap<String, Card> tm = new TreeMap<String, Card>();
BufferedReader bin = new BufferedReader(new FileReader(file));
String tmpLine = null;
String[] results = null;
// Sleeping Dogs™这种有特殊字符的需要处理
while (null != (tmpLine = bin.readLine())) {
results = tmpLine.split("\t");
tm.put(results[0],
new Card(results[0], Integer.parseInt(results[1]), Double
.parseDouble(results[2])));
}
bin.close();
return tm;
}
}
运行结果:
There are 343 results match your search "NPPD.RUSH The milk of Ultra violet":
1: [//N.P.P.D.RUSH// - The milk of Ultra violet]......with [6] relevance.
2: [9 Clues: The Secret of Serpent Creek]......with [2] relevance.
3: [Abyss: The Wraiths of Eden]......with [2] relevance.
4: [Adventure Time: The Secret Of The Nameless Kingdom]......with [2] relevance.
5: [Alex Hunter - Lord of the Mind]......with [2] relevance.
6: [Beatbuddy: Tale of the Guardians]......with [2] relevance.
7: [Blood of the Werewolf]......with [2] relevance.
8: [Call of Cthulhu: The Wasted Land]......with [2] relevance.
9: [Crypt of the NecroDancer]......with [2] relevance.
10: [Defender's Quest: Valley of the Forgotten]......with [2] relevance.
Other results have less relevance.