前情提要
六级要来了,要背单词了,又拿出来大二时候买的百词斩的实体书了。但是这次我要脱离app
用这本实体书,毕竟我还是知道我自己的自制力的。
但是,用实体书的话我怎样才能够检查我背单词的效果呢?
- 找人?就我这烂英语水平就不在别人面前丢人了;
- 手盖住上面的单词,看下面的索引?还是改不掉偷看的毛病,我也不想在这本书上写写画画。
于是乎,我就想找个方法来把这些单词提取出来。
失败的提取的方法
首先,直接手敲是不可能手敲的,这辈子都不可能的。3000多个单词,要敲到猴年马月去啊?!
照相+复印
在我用两个台灯加一个手机闪光灯做补光的强大光源的照射下,纸张变的如此明亮。但是iPad 8MP 的辣鸡摄像头照出来的东西糊的要死,就算用 ps 增加了对比度,增加了亮度,打印出来照样是糊到要死。所以 pass。
照相+OCR
我还是想再试试其他的方法。不过就算使用联网的 ocr 平台,或者是 onenote 的识别,结果都差强人意。可能纠错花的时间比较少,但是还是会花许多时间。
那就,只能自己干了
自行编程
因为百词斩不是需要一直联网的,所以它肯定是要把自己的数据保存在本地端的。所以,能够找到它本地端的数据并加以处理,事情应当很快就能够迎刃而解了。
找寻文件
找到目录
单词这个东西小而零碎,并且肯定会有上万行,在本地保存的话,肯定要用到数据库;在安卓,肯定用的是sqlite
数据库;又因为每个人用到的单词都不一样,所以它们应该会有两种保存的方法:
- 不同的书用不同的数据库;
- 使用同一个数据库,不同的书用一个文本文件标明主键方便查找。
而手机里数十G的文件又不是那么好搜索的,于是我就打开了万恶的百度,但你还别说,刚好让我找到了一个人和我有着同样的想法:
- 百词斩(最新版本6.2.14 )安卓版本, 在手机上运行时, 数据会存在类似以下目录中:
/data/media/0/Android/data/com.jiongji.andriod.card/files/baicizhan
具体路径有可能会不完全一样, 但是用关键字
jiongji
或者baicizhan
查找应该能找到
该路径下有几个关键文件:
- 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了,看了不少的文章,大致思路就是:
- 利用
InputStream
读入文件; - 利用
IOUtils
对于字符串进行编码; - 利用
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的屎山了。
大致构成
- 连接数据库;
- 导入
json
; json
解析;- 遍历每一个解析出的对象:
- 查表,找出;
- 写入文本。
代码分块解析
连接数据库
1 | private static final String DBURL = "jdbc:sqlite:\\src\\main\\resources\\lookup.db";//数据库相对位置 |
新建一个 Connection
对象,并将其指向数据库的位置。
Class.forName
此方法含义是:加载参数指定的类,并且初始化它。[^4]
setAutoCommit
此方法的含义是:参数为true
时,sql命令的提交(commit)由驱动程序负责。[^5]
导入并解析json
1 | InputStream inputStream = new FileInputStream("src\\main\\java\\helloworld\\word"); |
第一三行获取文件并格式化,没什么好说的。
第五行,parseArray
方法,看源码解析[^6],传出来的是一个list
,所以就要用list
接收了。
访问
list
的元素,不能像访问数组一样直接用下标访问,而是用objects.get(id)
的方法来访问。
写到这里的时候,我本身是想写相对路径的,但是怎么搞都找不到文件,明明放在同一个文件夹中的啊,无奈,先用的绝对路径。之后上网上找思路的时候,貌似说是必须要将项目下的路径写全才可以,所以我就这么写了,如果你知道如何才能写的更简便的话一定要告诉我啊。
解析json
的时候,需要一个Java Bean
,这个就是word.class
:
1 | public class word { |
查表
1 | for (word d : words) { |
for each
语句访问每一个list
的元素,然后获取主键。
createStatement
是为了创建状态并将sql语句送给数据库。
数据库返回一个resultset
元素。
由于单词只会在一张表中,而且十张表中的属性相同,我就使用了union
子句在十张表中进行查询。
1 | select word |
写入数据
1 | int i = 0; |
我需要每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