php响应式网站开发教程,手机网站一般做多大尺寸,深圳福田区住房和建设局网站,网站开发程序哪个好文章目录 一. 项目结构二.流程分析2.1 批处理器核心代码解析 三. 跨页表格相似度匹配原理3.1 表头内容相似度-特征向量归一化3.2 表头内容相似度-余弦相似度3.3 定时缓存清理 ocr扫描有其局限性。对于pdf文本类型这种pdfbox#xff0c;aspose-pdf#xff0c;spire直接提取文本… 文章目录 一. 项目结构二.流程分析2.1 批处理器核心代码解析 三. 跨页表格相似度匹配原理3.1 表头内容相似度-特征向量归一化3.2 表头内容相似度-余弦相似度3.3 定时缓存清理 ocr扫描有其局限性。对于pdf文本类型这种pdfboxaspose-pdfspire直接提取文本的精准性更高。经过综合对比我们觉得aspose和spire在读取pdf文本方面较为优秀。基于此我们可能需要提取pdf中所有表格数据完成数据录入。但是表格数据不同还存在跨页表格问题。但是按照以下方案即可解决。本文的表格处理思想来源于mybatis的底层设计。
特征余弦相似度编辑距离原理衡量向量方向的夹角语义相似性计算字符串转换所需的最小操作次数字符级差异输入类型向量如文本的TF-IDF或词嵌入向量字符串或序列关注点语义层面的相似性如主题、用词结构层面的差异如拼写错误、字符顺序输出范围[-1, 1]通常取绝对值或归一化为0-1非负整数0表示完全匹配计算复杂度O(n)向量化后快速计算O(n*m)对长文本较慢典型应用文档相似度、推荐系统、语义搜索拼写纠错、DNA序列比对、短文本模糊匹配
开源地址
一. 项目结构
本设计基于aspose-pdf实现
|-- SpringContextUtil.java
-- pdf|-- AbstractTextMappingTemplate.java #抽象模板映射器 解析内容映射到结构化对象|-- PDFboxTable.java # 暂留扩展|-- PdfTableParsingEngine.java # 表格解析引擎 提供从PDF文档中提取并处理表格数据的功能|-- StringEscapeUtil.java # 字符串转义工具类 防止注入攻击|-- TableBatchProcessor.java # 具体表格执行处理器 表格批处理器|-- annotation # 映射注解包|-- aspect # 注解处理器包|-- converter # 抽象模板映射器具体实现 包-- entity # 想要映射的结构化对象包二.流程分析
表格解析器提取pdf表格文本表格批处理器负责具体执行表格解析字符串转义避免恶意攻击抽象映射器允许用户具体实现映射实体
2.1 批处理器核心代码解析
表格解析器每检测一页的所有表格就提交到批处理器进行具体数据清洗归一化。以下是进行数据批处理的核心逻辑 /*** 添加表格到批处理队列** param pageIndex 页码索引* param tables 页面中的表格列表*/public void addPageTables(int pageIndex, ListAbsorbedTable tables) {// 资源限制检查 一页10个表格if (tables.size() MAX_TABLES_PER_PAGE) {log.warn(页面{}表格数量超过限制: {}, pageIndex, tables.size());// 截取前MAX_TABLES_PER_PAGE个表格tables tables.subList(0, MAX_TABLES_PER_PAGE);}// 处理当前页的表格ListStringBuilder processedTables new ArrayList();for (AbsorbedTable table : tables) {// 处理单个表格StringBuilder tableContent processSingleTable(table);if (tableContent null) continue;// 数据清洗PdfTableParsingEngine.cleanData(tableContent);// 生成表格指纹String tableFingerprint generateTableFingerprint(tableContent);// 将表格指纹和内容存储到跨页表格缓存中crossPageTableCache.putIfAbsent(tableFingerprint, new CacheEntry(new StringBuilder(tableContent)));// 更新缓存条目的最后访问时间crossPageTableCache.get(tableFingerprint).updateLastAccessTime();// 检查是否为跨页表格if (isCrossPageTable(tableFingerprint)) {// 合并跨页表格tableContent mergeCrossPageTable(tableContent, tableFingerprint);} else {// 异常检测连续重复表格if (isDuplicateTable(tableFingerprint)) {log.warn(检测到连续重复表格类型: {}, tableFingerprint);continue;}}// 添加到处理队列processedTables.add(tableContent);}// 将处理后的表格添加到缓冲队列if (!processedTables.isEmpty()) {try {// 尝试添加到队列如果队列已满则提交当前队列中的所有表格if (!tableBufferQueue.offer(processedTables, 100, TimeUnit.MILLISECONDS)) {log.info(缓冲队列已满提交批处理任务);submitBatchTask();// 重新尝试添加tableBufferQueue.put(processedTables);}} catch (InterruptedException e) {log.error(添加表格到缓冲队列失败: {}, e.getMessage());Thread.currentThread().interrupt();}}}如上述代码
processSingleTable(AbsorbedTable table)用于具体解析表格内容并拼接成特定字符串。cleanData(StringBuilder builder) 移除所有空白字符和换行符generateTableFingerprint(StringBuilder tableContent) 用于识别跨页表格相似度合并crossPageTableCache 缓存跨页表格因为是以页为单位检测表格的。下一页需要保留上一页表格mergeCrossPageTable(tableContent, tableFingerprint) 设定相似度大于85%且不为100%。为同一表格。进行合并。submitBatchTask() 提交批处理任务processBatchTables(ListList batchTables) 获取抽象映射器的具体实现。根据具体规则进行映射匹配 三. 跨页表格相似度匹配原理
1.根据特定表头内容相似度2.根据表格样式特征
3.1 表头内容相似度-特征向量归一化
字符串长度建议不要超过特征矩阵维度长度 使用余弦相似矩阵,比较两个表头字符串相似度.一般认为表头字串很短因此初始化16特征向量即可 表示我们可以把字符ascii映射到特征向量上并通过单位向量归一化结果。获取第一块内容字串的标准化特征向量。同理对第二块内容字串做标准化计算。 /*** 计算内容相似度基于矢量相似度** param str1 字符串1* param str2 字符串2* return 内容相似度*/private double calculateContentSimilarity(String str1, String str2) {if (str1 null || str2 null) {throw new IllegalArgumentException(输入字符串不能为空);}// 将字符串转换为特征向量double[] vector1 stringToVector(str1);double[] vector2 stringToVector(str2);// 计算余弦相似度return cosineSimilarity(vector1, vector2);}/*** 将字符串转换为特征向量** param str 输入字符串* return 特征向量*/private double[] stringToVector(String str) {// 初始化特征向量double[] vector new double[VECTOR_DIMENSION];// 创建字符频率映射MapCharacter, Integer charFrequency new HashMap();// 统计字符频率for (char c : str.toCharArray()) {charFrequency.put(c, charFrequency.getOrDefault(c, 0) 1);}// 将字符频率映射到特征向量for (char c : charFrequency.keySet()) {int index Math.abs(c) % VECTOR_DIMENSION;vector[index] charFrequency.get(c);}// 归一化向量normalizeVector(vector);return vector;}/*** 归一化向量** param vector 输入向量*/private void normalizeVector(double[] vector) {double magnitude 0.0;// 计算向量模长for (double value : vector) {magnitude value * value;}magnitude Math.sqrt(magnitude);// 归一化向量if (magnitude 0) {for (int i 0; i vector.length; i) {vector[i] / magnitude;}}}3.2 表头内容相似度-余弦相似度
我们将原始特征向量进行标准化归一化处理使其转化为单位向量模长为1从而消除向量尺度差异对相似性度量的影响。注此步骤确保所有向量处于同一量纲空间使得后续计算具有可比性对于两个单位向量 u u u 和 v v v其点积在数值上等于它们的余弦相似度即 c o s θ cosθ cosθ。 几何意义余弦相似度反映向量方向的接近程度与向量维度无关。 数学表达 c o s θ u ⋅ v ∣ u ∣ ⋅ ∣ v ∣ cosθ\frac{u·v}{|u|·|v|} cosθ∣u∣⋅∣v∣u⋅v 结果解释 cos θ ≈ 1 c o s θ ≈ 1 \cos\theta \approx 1cosθ≈1 cosθ≈1cosθ≈1向量方向高度一致对应字符串内容几乎相同。 cos θ ≈ 0 c o s θ ≈ 0 \cos\theta \approx 0cosθ≈0 cosθ≈0cosθ≈0向量正交字符串内容无相关性。 应用示例在文本匹配任务中可通过该值量化两段文本的语义相似性。
点积与哈达玛积的区别 点积输出标量用于衡量整体相似性 哈达玛积为元素级乘法输出同维向量常用于局部特征交互。 private double cosineSimilarity(double[] vector1, double[] vector2) {if (vector1.length ! vector2.length) {throw new IllegalArgumentException(向量维度不匹配);}double dotProduct 0.0;double magnitude1 0.0;double magnitude2 0.0;for (int i 0; i vector1.length; i) {dotProduct vector1[i] * vector2[i];magnitude1 vector1[i] * vector1[i];magnitude2 vector2[i] * vector2[i];}magnitude1 Math.sqrt(magnitude1);magnitude2 Math.sqrt(magnitude2);if (magnitude1 0.0 || magnitude2 0.0) {return 0.0;} else {return dotProduct / (magnitude1 * magnitude2);}}
3.3 定时缓存清理
由于我们为了保证跨页表格的关联关系。我们使用map集合保存上一页表格内容。 /*** 构造函数*/public TableBatchProcessor() {// 使用虚拟线程池处理批量映射任务this.executorService Executors.newVirtualThreadPerTaskExecutor();// 初始化表格缓冲队列this.tableBufferQueue new LinkedBlockingQueue(BUFFER_CAPACITY);// 初始化表格类型计数器this.tableTypeCounter new ConcurrentHashMap();// 初始化跨页表格缓存this.crossPageTableCache new ConcurrentHashMap();// 初始化缓存清理调度器this.cacheCleanupScheduler Executors.newScheduledThreadPool(1);// 启动定时清理任务this.cacheCleanupScheduler.scheduleAtFixedRate(this::cleanupCrossPageTableCache, 1, 1, TimeUnit.MINUTES);}我设计了最早时间淘汰机制同时为了进一步防止内存溢出。设计了map最大值。超出阈值清理所有。但显然这是有问题的可能导致跨表关联关系断开。因此先以抛出异常解决 /*** 清理跨页表格缓存增强版*/private void cleanupCrossPageTableCache() {long currentTime System.currentTimeMillis();ListString expiredKeys new ArrayList();for (Map.EntryString, CacheEntry entry : crossPageTableCache.entrySet()) {if (currentTime - entry.getValue().lastAccessTime CACHE_ENTRY_TTL) {expiredKeys.add(entry.getKey());}}// 限制缓存条目数量if (crossPageTableCache.size() MAX_CACHE_ENTRIES) {crossPageTableCache.clear();throw new IllegalStateException(缓存条目数量超过限制);}for (String key : expiredKeys) {crossPageTableCache.remove(key);log.info(清理过期缓存条目: {}, key);}}