首页 > 编程语言 >java实现解析pdf格式发票

java实现解析pdf格式发票

时间:2024-08-10 15:52:12浏览次数:14  
标签:java String Pattern System details 发票 put pdf out

为了减少用户工作量及误操作的可能性,需要实现用户上传PDF格式的发票,系统通过解析PDF文件获取发票内容,并直接将其写入表单。以下文章记录了功能实现的代码。

发票样式

在这里插入图片描述

发票内容解析

引用Maven

使用pdfbox

<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.24</version> <!-- 请检查最新版本 -->
</dependency>

获取PDF内容

设置 sortByPosition 为 true 可以按文本位置提取内容,否则获取到的内容错乱,无法获取到真正需要的内容

    @RequestMapping("uploadReceiptsTest")
    @ResponseBody
    public Map<String,String> uploadReceiptsTest() throws Exception{
        String filePath = "D:/apache-tomcat-8.5.98/webapps/tspspic/发票文件/24372000000145100092.pdf"; // 保存路径
        PDDocument document = PDDocument.load(new File(filePath));
        PDFTextStripper pdfStripper = new PDFTextStripper();
        // 排序文本行按其位置
        pdfStripper.setSortByPosition(true);
        String text = pdfStripper.getText(document);
        document.close();
        Map<String, String> map = pdfStr(text);
        return map;
    }

文本内容(部分内容以*替代)


    public static void main(String[] args) {
        String invoiceInfo = "电子发票(普通发票) 发票号码:******\n" +
                             "开票日期:******\n" +
                             "购 名称:****** 销 名称:******\n" +
                             "买 售\n" +
                             "方 方\n" +
                             "信 统一社会信用代码/纳税人识别号:****** 信 统一社会信用代码/纳税人识别号:******\n" +
                             "息 息\n" +
                             "项目名称 规格型号 单 位 数 量 单 价 金 额 税率/征收率 税 额\n" +
                             "******* 100ml:5g 袋 800 4.070796 3256.64 13% 423.36\n" +
                             "******\n" +
                             "合 计 ¥3256.64 ¥423.36\n" +
                             "价税合计(大写) 叁仟陆佰捌拾圆整 (小写)¥3680.00\n" +
                             "批号:******/ 生产日期:2024-05-15/ 有效期至:2026-04-30/ 含税单价:4.6000/ 生产厂家:******/ 批准文号:******/\n" +
                             "备 注\n" +
                             "开票人:王宁\n" +
                             "王宁";

解析文件内容,返回数据

初版测试
    public Map<String,String> pdfStr(String invoiceInfo) {
 		//因解析出的括号不确定为中文还是英文,统一替换为英文字符
        invoiceInfo = invoiceInfo.replaceAll("(","(").replaceAll(")",")");
        // 定义正则表达式模式
        Pattern patternInvoiceNumber = Pattern.compile("发票号码:(\\d+)");
        Pattern patternInvoiceDate = Pattern.compile("开票日期:(\\d{4}年\\d{1,2}月\\d{1,2}日)");
        Pattern patternBuyerName = Pattern.compile("购 名称:(.+?) 销 名称:(.+?)\n");
        //由上图可以发现“项目名称 规格型号 单 位 数 量 单 价 金 额 税率/征收率 税 额”所需要的内容在下一行数据,使用笨方法直接获取“税额与合计”之间的数据通过之后的空格分割进行获取数据
        Pattern patternItemDetails = Pattern.compile("税 额\\s+(.*?)合 计", Pattern.DOTALL);
        Pattern patternTotal = Pattern.compile("\\(小写\\)¥(\\d+(\\.\\d+)?)");
        Pattern patternBatchNumber = Pattern.compile("批号:(.+?)/");
        Pattern patternProductionDate = Pattern.compile("生产日期:(\\d{4}-\\d{1,2}-\\d{1,2})/");
        Pattern patternExpirationDate = Pattern.compile("有效期至:(\\d{4}-\\d{1,2}-\\d{1,2})/");
        Pattern patternTaxIncludedPrice = Pattern.compile("含税单价:(\\d+(\\.\\d+)?)");
        Pattern patternManufacturer = Pattern.compile("生产厂家:(.+?)/");
        Pattern patternApprovalNumber = Pattern.compile("批准文号:(.+?)/");
        Pattern patternIssuer = Pattern.compile("开票人:(.+)");

        // 创建Matcher对象
        Matcher matcherInvoiceNumber = patternInvoiceNumber.matcher(invoiceInfo);
        Matcher matcherInvoiceDate = patternInvoiceDate.matcher(invoiceInfo);
        Matcher matcherBuyerName = patternBuyerName.matcher(invoiceInfo);
        Matcher matcherItemDetails = patternItemDetails.matcher(invoiceInfo);
        Matcher matcherTotal = patternTotal.matcher(invoiceInfo);
        Matcher matcherBatchNumber = patternBatchNumber.matcher(invoiceInfo);
        Matcher matcherProductionDate = patternProductionDate.matcher(invoiceInfo);
        Matcher matcherExpirationDate = patternExpirationDate.matcher(invoiceInfo);
        Matcher matcherTaxIncludedPrice = patternTaxIncludedPrice.matcher(invoiceInfo);
        Matcher matcherManufacturer = patternManufacturer.matcher(invoiceInfo);
        Matcher matcherApprovalNumber = patternApprovalNumber.matcher(invoiceInfo);
        Matcher matcherIssuer = patternIssuer.matcher(invoiceInfo);

        // 提取数据
        String invoiceNumber = "";
        String invoiceDate = "";
        String buyerName = "";
        String sellerName = "";
        String productName = "";
        String specification = "";
        String unit = "";
        int quantity = 0;
        double unitPrice = 0.0;
        double amount = 0.0;
        String taxRate = "";
        double taxAmount = 0.0;
        double total = 0.0;
        String batchNumber = "";
        String productionDate = "";
        String expirationDate = "";
        double taxIncludedPrice = 0.0;
        String manufacturer = "";
        String approvalNumber = "";
        String issuer = "";

        if (matcherInvoiceNumber.find()) {
            invoiceNumber = matcherInvoiceNumber.group(1);
        }

        if (matcherInvoiceDate.find()) {
            invoiceDate = matcherInvoiceDate.group(1);
        }

        if (matcherBuyerName.find()) {
            buyerName = matcherBuyerName.group(1);
            sellerName = matcherBuyerName.group(2);
        }

        // 处理项目名称、规格型号、单位、数量、单价、金额、税率/征收率、税额
        if (matcherItemDetails.find()) {
            String itemDetailsLine = matcherItemDetails.group(1).trim();
            itemDetailsLine = itemDetailsLine.replace("\n"," ");
            String[] details = itemDetailsLine.split(" "); // 按空格分割

            if (details.length >= 8) { // 确保有足够的字段
            //因部分名称过长,换行数据解析到最后进行拼接
                productName = details[0].trim(); // 项目名称
                if (details.length >= 9){
                    productName = details[0].trim()+details[8].trim(); // 项目名称
                }
                specification = details[1].trim(); // 规格型号
                unit = details[2].trim(); // 单位
                quantity = Integer.parseInt(details[3].trim()); // 数量
                unitPrice = Double.parseDouble(details[4].trim()); // 单价
                amount = Double.parseDouble(details[5].trim()); // 金额
                taxRate = details[6].trim(); // 税率/征收率
                taxAmount = Double.parseDouble(details[7].trim()); // 税额

                System.out.println("项目名称: " + productName);
                System.out.println("规格型号: " + specification);
                System.out.println("单位: " + unit);
                System.out.println("数量: " + quantity);
                System.out.println("单价: " + unitPrice);
                System.out.println("金额: " + amount);
                System.out.println("税率/征收率: " + taxRate);
                System.out.println("税额: " + taxAmount);
            }
        }

        if (matcherTotal.find()) {
            total = Double.parseDouble(matcherTotal.group(1));
        }

        if (matcherBatchNumber.find()) {
            batchNumber = matcherBatchNumber.group(1);
        }

        if (matcherProductionDate.find()) {
            productionDate = matcherProductionDate.group(1);
        }

        if (matcherExpirationDate.find()) {
            expirationDate = matcherExpirationDate.group(1);
        }

        if (matcherTaxIncludedPrice.find()) {
            taxIncludedPrice = Double.parseDouble(matcherTaxIncludedPrice.group(1));
        }

        if (matcherManufacturer.find()) {
            manufacturer = matcherManufacturer.group(1);
        }

        if (matcherApprovalNumber.find()) {
            approvalNumber = matcherApprovalNumber.group(1);
        }

        if (matcherIssuer.find()) {
            issuer = matcherIssuer.group(1);
        }

        // 输出其他结果
        System.out.println("发票号码: " + invoiceNumber);
        System.out.println("开票日期: " + invoiceDate);
        System.out.println("购买方名称: " + buyerName);
        System.out.println("销售方名称: " + sellerName);
        System.out.println("价税合计: " + total);
        System.out.println("批号: " + batchNumber);
        System.out.println("生产日期: " + productionDate);
        System.out.println("有效期至: " + expirationDate);
        System.out.println("含税单价: " + taxIncludedPrice);
        System.out.println("生产厂家: " + manufacturer);
        System.out.println("批准文号: " + approvalNumber);
        System.out.println("开票人: " + issuer);
    }
优化代码
  • Map存储正则表达式:将所有正则表达式模式和对应的字段名称存储在一个Map中,遍历Map并执行匹配,从而避免了为每个字段都写单独的匹配代码。

  • 抽取通用逻辑:将匹配逻辑抽象成一个通用方法,简化了代码结构,减少了重复代码。

  • 处理商品详情:在匹配完itemDetails后,再拆分字符串并填充对应的字段。

    public static Map<String, String> pdfStr(String invoiceInfo) {
        invoiceInfo = invoiceInfo.replaceAll("(", "(").replaceAll(")", ")");

        // 定义正则表达式模式
        Map<String, String> patterns = new HashMap<>();
        patterns.put("invoiceNumber", "发票号码:(\\d+)");
        patterns.put("invoiceDate", "开票日期:(\\d{4}年\\d{1,2}月\\d{1,2}日)");
        patterns.put("buyerName", "购 名称:(.+?) 销 名称:(.+?)\n");
        patterns.put("itemDetails", "税 额\\s+(.*?)合 计");
        patterns.put("total", "\\(小写\\)¥(\\d+(\\.\\d+)?)");
        patterns.put("batchNumber", "批号:(.+?)/");
        patterns.put("productionDate", "生产日期:(\\d{4}-\\d{1,2}-\\d{1,2})/");
        patterns.put("expirationDate", "有效期至:(\\d{4}-\\d{1,2}-\\d{1,2})/");
        patterns.put("taxIncludedPrice", "含税单价:(\\d+(\\.\\d+)?)");
        patterns.put("manufacturer", "生产厂家:(.+?)/");
        patterns.put("approvalNumber", "批准文号:(.+?)/");
        patterns.put("issuer", "开票人:(.+)");

        // 提取数据
        Map<String, String> result = new HashMap<>();

        for (Map.Entry<String, String> entry : patterns.entrySet()) {
            Pattern pattern = Pattern.compile(entry.getValue(), Pattern.DOTALL);
            Matcher matcher = pattern.matcher(invoiceInfo);
            if (matcher.find()) {
                result.put(entry.getKey(), matcher.group(1).trim());
            }
        }

        // 处理项目名称、规格型号、单位、数量、单价、金额、税率/征收率、税额
        if (result.containsKey("itemDetails")) {
            String[] details = result.get("itemDetails").replace("\n", " ").split(" ");
            if (details.length >= 8) {
                result.put("productName", details[0].trim() + (details.length > 8 ? details[8].trim() : ""));
                result.put("specification", details[1].trim());
                result.put("unit", details[2].trim());
                result.put("quantity", details[3].trim());
                result.put("unitPrice", details[4].trim());
                result.put("amount", details[5].trim());
                result.put("taxRate", details[6].trim());
                result.put("taxAmount", details[7].trim());
            }
        }

        // 打印结果
        for (Map.Entry<String, String> entry : result.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        return result;
    }

标签:java,String,Pattern,System,details,发票,put,pdf,out
From: https://blog.csdn.net/weixin_45049746/article/details/141064886

相关文章

  • Java知识点1
    Java知识点什么是字节码?采用字节码的好处是什么?在Java中,JVM可以理解的代码就叫做字节码(即扩展名为.class的文件),它只面向虚拟机。Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。字节码并不针对一种特定......
  • java导出pdf
    文章目录概要maven包进入工具类使用示例注意事项概要java利用itextpdf导出pdfmaven包进入 <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.3</version> </dependency> ......
  • Java--实例化
    目录五种方法构造器定义特点作用构造方法种this的使用五种方法无论哪一种方式必须经过的一步---调用构造方法。无论怎样构造函数作为初始化类的意义怎样都不会改变。new语句创建对象,最常用创建对象的方法工厂方法返回对象,如:Stringstr=String.valueOf();反射,调用java......
  • Java SpringBoot实现PDF转图片
    不是单页图片,是多页PDF转成一张图片的逻辑。我这里的场景是PDF转成图片之后返回给前端,前端再在图片上实现签字,并且可拖拽的逻辑,就是签订合同的场景。但是这里只写后端多页PDF转图片的逻辑。先说逻辑,后面直接上代码,大致分为以下几步:读取PDF文件(我的代码里是将在线的URL转成Inp......
  • Java常用类
    目录String类日期时间类数学类包装类Runtime类1.获取运行时环境信息2.Runtime类的实例化和使用3.执行系统命令System类1.标准输入输出和错误输出2.系统属性3.时间和日期4.数组操作5.其他重要方法String类字符串常量池常见方法charAt(intindex):返回字符串指定位置的字......
  • Java--String类查找方法
    目录1.indexOf(Stringstr)2.indexOf(Stringstr,intfromIndex)3.lastIndexOf(Stringstr)4.lastIndexOf(Stringstr,intfromIndex)5.contains(CharSequences)6.startsWith(Stringprefix)7.endsWith(Stringsuffix)booleanequalsStringtrim在Java中,String类提供了......
  • 工厂模式与策略模式的区别及其在Java中的应用
    工厂模式与策略模式的区别及其在Java中的应用1.引言在软件开发中,设计模式被广泛应用于解决各种常见问题,提高代码的可维护性、可扩展性和可读性。工厂模式(FactoryPattern)和策略模式(StrategyPattern)是两种非常重要的设计模式,它们解决了不同的设计问题,并且在许多情况下可......
  • Java设计模式:中介者模式详解与最佳实践
    Java设计模式:中介者模式详解与最佳实践1.引言在软件开发过程中,特别是复杂系统的构建中,模块间的交互往往成为影响代码质量的重要因素。当模块之间耦合度过高时,系统的维护、扩展和理解成本都会显著增加。为了降低模块之间的耦合度,保持系统的灵活性和可维护性,我们可以使用设......
  • go项目实战之word、pdf、txt操作
    最近在项目开发中,频繁的遇到需要对Docx、PDF、TXT等类型的文本进行操作,而目前这方面有unidoc/unioffice,但这个是非开源的,所以使用起来有诸多不方便。而且也搜了很多资料,但是都太笼统了,不方便使用,所以特写此文章希望能帮助大家解决问题!以下代码都可直接复制粘贴使用DO......
  • Java中方法的学习
    方法方法的概念在我们的日常生活中,方法可以理解为要做某件事情,而采取的解决办法。如:小明同学在路边准备坐车来学校学习。这就面临着一件事情(坐车到学校这件事情)需要解决,解决办法呢?可采用坐公交车或坐出租车的方式来学校,那么,这种解决某件事情的办法,我们就称为方法。所以:Java方......