前情提要

六级要来了,要背单词了,又拿出来大二时候买的百词斩的实体书了。但是这次我要脱离app用这本实体书,毕竟我还是知道我自己的自制力的。

但是,用实体书的话我怎样才能够检查我背单词的效果呢?

  • 找人?就我这烂英语水平就不在别人面前丢人了;
  • 手盖住上面的单词,看下面的索引?还是改不掉偷看的毛病,我也不想在这本书上写写画画。

于是乎,我就想找个方法来把这些单词提取出来。

失败的提取的方法

首先,直接手敲是不可能手敲的,这辈子都不可能的。3000多个单词,要敲到猴年马月去啊?!

照相+复印

在我用两个台灯加一个手机闪光灯做补光的强大光源的照射下,纸张变的如此明亮。但是iPad 8MP 的辣鸡摄像头照出来的东西糊的要死,就算用 ps 增加了对比度,增加了亮度,打印出来照样是糊到要死。所以 pass。

照相+OCR

我还是想再试试其他的方法。不过就算使用联网的 ocr 平台,或者是 onenote 的识别,结果都差强人意。可能纠错花的时间比较少,但是还是会花许多时间。

那就,只能自己干了

自行编程

因为百词斩不是需要一直联网的,所以它肯定是要把自己的数据保存在本地端的。所以,能够找到它本地端的数据并加以处理,事情应当很快就能够迎刃而解了。

找寻文件

找到目录

单词这个东西小而零碎,并且肯定会有上万行,在本地保存的话,肯定要用到数据库;在安卓,肯定用的是sqlite数据库;又因为每个人用到的单词都不一样,所以它们应该会有两种保存的方法:

  1. 不同的书用不同的数据库;
  2. 使用同一个数据库,不同的书用一个文本文件标明主键方便查找。

而手机里数十G的文件又不是那么好搜索的,于是我就打开了万恶的百度,但你还别说,刚好让我找到了一个人和我有着同样的想法:

  1. 百词斩(最新版本6.2.14 )安卓版本, 在手机上运行时, 数据会存在类似以下目录中:

/data/media/0/Android/data/com.jiongji.andriod.card/files/baicizhan

具体路径有可能会不完全一样, 但是用关键字jiongji或者baicizhan查找应该能找到

  1. 该路径下有几个关键文件:

    • lookup.db : 所有单词都存放在这个数据库文件中, 每个单词都会有一个唯一的word id.
    • roadmap/road_map_xxx.baicizhan 要背的每一本书都会在此目录下有一个这样的文件。 该文件是文本文件, 其中用”wid”:xxxx 定义若干个word id.
    • 只要根据word id找到对应的单词即可。 但是不可能手工去一个一个地查询每个单词, 每本书都可能对应着几千个单词呢。所以接下来就要自己写程序了。[^1]

从这句话可知:百词斩用的是第二个方法。

按照他这个思路实施的话,我只需要按照他说的两个关键字查找就行了,但是垃圾的miui并不支持对文件夹名进行搜索。但是我之前还是看过一点关于安卓开发的,虽然有些毒瘤公司例如BAT 会把自己在系统中的文件写在系统的根目录下,但是安卓建议的是应该把文件通通写在/android/的目录下,此时鉴定百词斩是不是毒瘤公司的时间到了!

可喜可贺,百词斩并不是一家毒瘤公司,他老老实实地把文件写在了/android/data/com.jiongji.android.card/中,虽然比着之前直接写在baicizhan的目录中要难找一些,但还是不太困难的一件事情。

之后就是直接把这个目录下的baicizhan的文件夹全部打包,然后用QQ 发给自己的电脑。

找寻标记文件格式

这个过程并不算太难,在寻找教程的时候,破解百词斩单词数据之旅 回复中的第22楼明确指出了在roadmap/road_map_xxx.baicizhan的文件中是用json格式写的。[^2]

直接打开,龟龟,没有序列化过的文件就像一个垃圾桶,但毕竟是为了节约流量嘛,crlf 字符还是挺占空间的,可以理解,但是自己看还是导入 vs 来个快捷键,轻松序列化一下吧。

还是这样子看起来舒服。

开始编程

前两步实际上并不算什么,花费了 40 余分钟就搞好了,但是后面的问题才是真正的问题,应当如何编程?因为我之前从来没有接触过 json,数据库这门课也是睡过去的,我也不太清楚如何编程,不过还是走一步看一步吧。

json的解析

引入依赖

直接导包

我不是为了专门学习 json,也懒得自己造轮子,所以我先想到的就是用前人的伟大经验。

经过多次辗转,我找到了阿里巴巴的fastjson项目,从github 上下载下来源码,第一个难题就摆在我面前,怎么导?

下载下来的文件并不是直接编译好的jar包,但是天真的我并不知道这件事情,所以我不断的在IDEA中的project structure中寻找,油腻的师姐在哪里。 导入依赖,删除依赖。折腾了几次,在这折腾的过程中,我想到了之前写安卓项目的时候优雅:写一行依赖项,android studio就直接帮你全下下来,配置好,你自己拿来用就够了。

在我搜索的过程中,我才认识到一个事实,github上分享的代码大部分都只是源代码,并没有经过编译,我需要自己编译才能够导出来所需的jar包。而fastjson中含有pom.xml文件,这是maven的标志,所以我需要maven将它编译。

我还知道了我刚才说的那种优雅的做法实际上是android用了grandle来管理依赖,知道了这的我像打开了新世界的大门一样。

maven管理
下载maven

这个是我一定要拿出来单独说的事情,ISP太小气,或者怪防火长城,maven的下载只有几kps,于是我只能冒着被装垃圾文件的风险跑到脚本之家去下载,幸好脚本之家还是不错的,没有垃圾下载器,更没有垃圾文件。

引入依赖

IDEA中新建一个maven项目,在项目根目录下的pom.xml按下alt+ ins键,输入fastjson自动导入,一键下载,方便快捷。写android时的优雅仿佛又浮现在了我的面前。

学习使用

那就是去找写文件的demo了,看了不少的文章,大致思路就是:

  1. 利用InputStream读入文件;
  2. 利用IOUtils对于字符串进行编码;
  3. 利用fastjson中的parseArray方法将json文本转化成对应的javaBean元素。

其中使用fastjson解析json文件 这篇文章还是很不错的,大部分思路就是从这里搬的。[^3]

连接数据库

因为用的是数据库嘛,当然要连接数据库的。

用 SQL Server

首先,这是一个鞭尸现场,之后不可行。先把原来的SQL Server 2008 R2 卸载,实在是难安又难卸,鬼知道老师为啥还非得说现在是教学,不要用那么新的,够用就好云云。去您妈个头,再也不见,这是真的垃圾,之前舍友安安卸卸愣是把系统给搞崩,又重新安装系统才算了,这么垃圾的东西早该进坟堆了。

换上了 SQL Server 2017 Developer,但自己还是愚钝,并没有找到方法,折腾了半个下午,放弃。

用 SQLiteman 分析数据库构成

一个字,好用。比着 SQL Server 这个软件只能查看前一百行的数据,它能够查看数据库中所有的资源。

可以看到整个数据库有10个表,每个表的属性有json中解析出来的id主键,有单词,有音标,有中文意思,有freq,还有每个单词的长度。

找到这些之后,就应当把数据库放在项目下了。

数据库导入

这个不是难点,直接打开数据库,然后下载对应的 jdbc文件就ok。

这些完成了之后,如何编码已经非常清晰了,下面就是介绍具体的思路了。

编码构成

语言选择

实际上这种编码用解释型语言比如好多短命仔用的语言,或者pop的语言,但是我不会py也不怎么会c++的导包,所以只能用oop的语言来写一堆pop的屎山了。

大致构成

  1. 连接数据库;
  2. 导入json;
  3. json解析;
  4. 遍历每一个解析出的对象:
    1. 查表,找出;
    2. 写入文本。

代码分块解析

连接数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static final String DBURL = "jdbc:sqlite:\\src\\main\\resources\\lookup.db";//数据库相对位置
/*
在main函数中
*/
Connection connection = null;
try {
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection(DBURL);
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
System.exit(-1);
}
// System.out.println("open db successfully");
connection.setAutoCommit(true);

新建一个 Connection对象,并将其指向数据库的位置。

Class.forName此方法含义是:加载参数指定的类,并且初始化它。[^4]

setAutoCommit此方法的含义是:参数为true时,sql命令的提交(commit)由驱动程序负责。[^5]

导入并解析json
1
2
3
4
5
InputStream inputStream = new FileInputStream("src\\main\\java\\helloworld\\word");

String string = IOUtils.toString(inputStream, "utf8");

List<word> words = JSON.parseArray(string, word.class);

第一三行获取文件并格式化,没什么好说的。

第五行,parseArray方法,看源码解析[^6],传出来的是一个list,所以就要用list接收了。

访问list的元素,不能像访问数组一样直接用下标访问,而是用objects.get(id)的方法来访问。

写到这里的时候,我本身是想写相对路径的,但是怎么搞都找不到文件,明明放在同一个文件夹中的啊,无奈,先用的绝对路径。之后上网上找思路的时候,貌似说是必须要将项目下的路径写全才可以,所以我就这么写了,如果你知道如何才能写的更简便的话一定要告诉我啊。

解析json的时候,需要一个Java Bean,这个就是word.class

1
2
3
4
5
6
7
8
9
10
11
12
public class word {

private int topic_id;

private String options;

private String tag_id;

private String word_level_id;

//Getter and Setter
}
查表
1
2
3
4
5
for (word d : words) {
int topic_id = d.getTopic_id();

Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);

for each语句访问每一个list的元素,然后获取主键。

createStatement是为了创建状态并将sql语句送给数据库。

数据库返回一个resultset元素。

由于单词只会在一张表中,而且十张表中的属性相同,我就使用了union子句在十张表中进行查询。

1
2
3
4
5
6
select word 
from table1
where topic_id = " + topic_id

union
//……
写入数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int i = 0;
int mm = 1;

File file = new File("word"+mm+".txt");

//for each前

if(i == 300){
mm++;
file = new File("word"+mm+".txt");
i%=300;
}

FileWriter fileWriter = new FileWriter(file.getName(),true);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

bufferedWriter.write(resultSet.getString("word")+'\n');

bufferedWriter.close();
fileWriter.close();
//for each 中

我需要每300个单词保存在一个文件中,所以有了这个需求。

这个姑且算个难点:将数据写入多个文本文件中,我也给标出来吧。

实质上也没有什么难度,每一个文件的文件名都是一行字符串,用一个会变的量再将两部分固定的量拼在一起就够了。

方法就是设置一个遍历变量i每三百个的时候,再new一个新文本出来。

其他就是正常的I/O操作,参考了网上的资源[^7]写出来了一个不是很好的代码,最后保存的时候有的文件是299有的是300,不过不太影响,也就懒得深究了。

尾巴

说实话,在编码的时候就想着写出来的时候要写个博炫耀一下的,在中间休息的时候也会想想自己在写什么,写出来之后的兴奋劲。但是编码编了两天多,期间遇到了大大小小的问题,最后才写出来40行的代码。我就在想之前轮子哥说过的一句话,大学生要写这样的代码写10k行才能够出师,而这样的代码我觉得满打满算这两年写过的不会超过一千行,自己光顾着玩了,还是没有把时间用在正地方,导致编码量才如此地少吧。

更博的时候感觉还挺兴奋,但是到最后,写道现在凌晨一点半才写完,看着左下角字数统计写了将近3.5k字,头懵懵的,好几天也没睡好觉了,追的番现在更新也没来得及看,还是坚持写完,现在室友已经躺在床上了。

突然感觉自己自己的自制力在想做好一件事上的时候,还是不错的,也不像自己所想的,我没有一点自制力的,还是要承认自己一下的。

2019年9月23日01点34分于开封

博客主题不支持脚注,佛了QAQ。

[^1]: 怎么把百词斩中的单词列表打印出来
[^2]: 破解百词斩单词数据之旅
[^3]: 使用fastjson解析json文件
[^4]: Java class.forname 详解
[^5]: conn.setAutoCommit(true)和(false)的区别
[^6]: json.parseArray源码解析
[^7]: Java 流(Stream)、文件(File)和IO

相关文章
评论
分享
Please check the post_relate setting in config.yml of hexo-theme-Annie! You should make sure 'postsEnable == true' and the number of site posts greater than 1.
Please check the comment setting in config.yml of hexo-theme-Annie!