跳到主要内容

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方法。