2018-09-25
pdf解析
根据坐标
根据坐标来判断表格,主要是根据坐标的对应关系,而坐标的对应关系最终落实在单元格起始和结束。
总的来说,表格分为左对齐,右对齐和居中对齐,对于这三种情况都可以归纳为起点坐标相同或者终点坐标相同。
或者,另一个层面来说,是单元格个数相同,且对应单元格的距离相等。即一旦出现超过两行单元格个数相同,或者超过两行(每行每个)单元格起始坐标或结束坐标相同,即将其初步判断为表格。
即,先数个数,再算起始坐标,以及宽度。
pdf解析思路整理
先将pdf分解,解析到每一个字符,该节点携带了确定该字符的所有信息,(此处图片无法解析)。
每一个字符都携带了其必须的信息后,对其进行建模。
建模——起点+宽度+高度+字符模型
如前所述,pdf里的任何对象都是此模型,该模型具有通用性。
假设
假设1——行划分节点正确
假设对每一页pdf的每一行的节点划分都正确,具体为节点个数正确、节点起始坐标、高度、宽度、正确。
假设2——单元格规整
假设单元格规整,即单元格不存在跨行跨列,且每一行个数相同。
实际情况
1.并不是每一行的节点划分都正确。 2.并不是所有行都具有相同的节点数。 3.真正规整的表格较少,绝大多数存在跨行跨列。
整合折中
<table><tr><td bgcolor="Khaki">
但一个表格中至少有相连两行节点数相同,也不存在跨行跨列。
这将作为先判断表格中间行的依据。</td></table>
判断表格
第一遍先计算节点数,当节点数符合条件,计算距离。当两者均满足条件时,确定表格,然后再根据起始点和距离去寻找表格边框。
2018-09-26
pdf解析
最终得到page,其内含有三个对象,代码如下:
//存放不根据表格线解析的表格所在的行索引
List<int []> form=new ArrayList<int[]>();
//存放根据表格线解析的表格信息
List<Form> forms=new ArrayList<Form>();
//存放所有的文本内容
List<TextRow> content=new ArrayList<TextRow>();
static关键字(修饰方法时方便在没有创建对象的情况下来进行调用)
static方法
static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。
static方法不依赖于任何对象就可以进行访问,即非static方法需要先建一个对象才可以进行访问。在static方法中不能访问类的非static成员变量和非static方法,因为非static方法/变量都必须依赖具体的对象才能被调用。但在非static成员方法中可以访问static方法/变量。
static变量
static变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。
final关键字
java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。
修饰类
final修饰类,表示这个类不能被继承。
修饰方法
使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。
因此,只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。
2018-09-27
java abstract关键字
抽象方法
1、从上面的例子中我们可以看到抽象方法跟普通方法是有区别的,它没有自己的主体(没有包起来的业务逻辑),跟接口中的方法有点类似。所以我们没法直接调用抽象方法
2、抽象方法不能用private修饰,因为抽象方法必须被子类实现(覆写),而private权限对于子类来说是不能访问的,所以就会产生矛盾
3、抽象方法也不能用static修饰,试想一下,如果用static修饰了,那么我们可以直接通过类名调用,而抽象方法压根就没有主体,没有任何业务逻辑,这样就毫无意义了。
抽象类
1、用abstract关键字来表达的类,其表达形式为:(public)abstract class 类名{}
。
2、抽象类不能被实例化,也就是说我们没法直接new 一个抽象类。抽象类本身就代表了一个类型,无法确定为一个具体的对象,所以不能实例化就合乎情理了,只能有它的继承类实例化。
3、抽象类虽然不能被实例化,但有自己的构造方法。
4、抽象类与接口(interface)有很大的不同之处,接口中不能有实例方法去实现业务逻辑,而抽象类中可以有实例方法,并实现业务逻辑,比如我们可以在抽象类中创建和销毁一个线程池。
5、抽象类不能使用finally关键字修饰,因为finally修饰的类是无法被继承,而对于抽象类来说就是需要通过继承去实现抽象方法,这又会产生矛盾。
抽象类与抽象方法的关联
如果一个类中至少有一个抽象方法,那么这个类一定是抽象类,但反之则不然。也就是说一个抽象类中可以没有抽象方法。这样做的目的是为了此类不能被实例化。
如果一个类继承了一个抽象类,那么它必须全部覆写抽象类中的抽象方法,当然也可以不全部覆写,如果不覆写全部抽象方法则这个子类也必须是抽象类。
java注解
Java中的注释主要有以下三种:
- Override
- Deprecated
- SuppressWarning
注释是java代码的元数据(所谓元数据,就是数据的数据。也就是说,元数据存在的意义,就是为了描述数据)。
Override是用来标识当前的方法,是否覆盖了它的父类中的方法。 Deprecated用来标记过时的元素。 SuppressWarning用来阻止警告。
<T extends Comparable<? super T\>\>
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
能调用sort方法进行排序,list中的元素必须是实现了Comparable接口的类或者其子类,通过<T extends Comparable<? super T\>\>
来进行限定。
Java采取的是类型擦除的方法来实现泛型,并通过extends和super关键字来约束泛型的上界和下界。
-
extends关键字用于确定泛型的上界,
<A extends B\>
表示类B或者其子类。 -
super关键字用于确定泛型的下界,
<A super B\>
表示类B或者其父类,一直到Object。?则是一个通配符。
<T extends Compatable<? super T\>\>
表示了上界为实现了Comparable接口的类,<? super T\>
则表示实现Comparable接口的类的子类也可以,从而确定下界
2018-09-28
pdfbox解析说明
该项目为pdf无表格线解析,具体为将pdf文件转换为XML格式,方便数据入库。
该项目只提取pdf数据,不对表格进行识别。即除了过滤掉部分多余数据外,保留源文件的所有信息。
该项目最终输出XML文件,方便下游识别表格时对数据的读取。
重点
所需要的数据主要在表格中,具体表现为:
- 資產負債表(资产负债表)
- 利潤表(利润表)
- 現金流量表(现金流量表)
- 財務報表(财务报表)
- 股東權益變動表(股东权益变动表)
即主要问题转换为对表格的识别判断以及一些附加数据,附加数据主要包括
- 表格名称
- 表格发布的时间日期
处理
1.与表格有关的数据必须保留
- 表格名称
- 表格发布的时间日期
- 表的具体内容
以上数据不能有任何缺失
2.部分多余数据必须删除
1.页眉
2.页脚
3.页码
4.下划线
操作
综上,主要涉及两部分操作:
1.对于需要保留的数据,需要 正确划分节点;
2.正确识别要删除的数据,不能误删。
实现
采用pdfbox的框架解析出字符串后,再对字符串进行上述的处理。
pdfbox解析字符串
pdfbox解析的主要类是PDFTextStripper,需要对其中的writeString方法进行重写,重点处理对象为List<TextPosition> textPositions
,具体如下:
public class TextPositonExtracter extends PDFTextStripper {
@Override
protected void writeString(String text, List<TextPosition> textPositions) throws IOException {
//textPosition中存放了pdf中每一个字符的坐标,内容,高度,宽度
for(TextPosition textPosition:textPositions){
//具体的处理逻辑
//包括如何划分节点,如何舍弃多余数据
}
}
}
字符识别节点及删除多余数据
目前的节点识别主要包括以下几点:
1.下划线直接删除
2.根据空格和距离划分节点
具体为计算第一个空格和最后一个空格的距离,设定一个阀值作为判别依据。
3.特殊标点符号直接处理
如顿号、冒号、括号直接合并。
2018-09-29
pdf解析维护、调整、优化
到目前为止,会出现问题的也只有三个方面:
- 节点划分
- 表格缺失
- 页脚页码未去除
节点划分出现问题解决方法
1.特殊标点符号划分基本不会有问题;
2.节点划分一个重要参数就是字符阀值上限,该值是划分节点的主要依据。
该值在TextPositonExtracte类中定义。
//部分文档解析会误把两个单元格的内容合并到一起,同一个单元格里面内容的最大距离
//目前调整为14,后续若其他方法无法解决节点划分问题时可对其进行修改
private static final int CHARTHRESHOLD = 14;//20;
表格名称误删除问题解决
查看表名称是否在页的顶部,同时查看表名称的关键字是否包含在用于判断的正则表达式中。 对于表名称主要使用TextPositonExtracte类中的isSpecialChinese进行匹配,主要代码如下:
//可将新的表关键字加入到该匹配序列解决
Pattern isSpecialChinese=Pattern.compile("^.*表|^.*損益.*|^.*收益.*|^.*全面.*|^.*財務.*|^.*變動.*|^.*损益.*|^.*收益.*|^.*财务.*|^.*資産.*|"
+ "^.*綜合.*|^.*簡明.*|簡|明|綜|合|損|益|及|其|他|全|面|收|財|務|狀|況|權|變|動|現|金|流|量|简|"
+ "明|综|财|务|状|况|损|现|权|股|变|动|資|産|產|利|潤|润|併|負|債|東|截|止|年|月|日|[0-9]|料|本|东");
页脚页码未删除
删除页脚主要判断每一页底部的三行,即筛选出三个最大的Y坐标,对这三个坐标内的字符进行判别。
代码方法为Pragrom类的RemoveFooter。
java访问权限控制
对于类的成员而言,其能否被其他类所访问,取决于该成员的修饰词;而对于一个类而言,其能否被其他类所访问,也取决于该类的修饰词。在Java中,类成员访问权限修饰词有四类:private,无(包访问权限),protected 和 public,而其中只有包访问权限和public才能修饰一个类(内部类除外)。具体如下:
- public :被public修饰的类成员能被所有的类直接访问
- private:被public修饰的类成员只能在定义它的类中被访问,其他类都访问不到。特别地,我们一般建议将成员变量设为private的,并为外界提供 getter/setter 去对成员变量进行访问,这种做法充分体现了Java的封装思想;
- 包访问权限:包访问权限就是Java中的默认的权限,具有包访问权限的类成员只能被同一包中的类访问
protected关键字
- 基类的protected成员是包内可见的,并且对子类可见;
- 若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法。