Java解析pdf格式發(fā)票的代碼實現(xiàn)
發(fā)票樣式
發(fā)票內(nèi)容解析
引用Maven
使用pdfbox
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.24</version> <!-- 請檢查最新版本 --> </dependency>
獲取PDF內(nèi)容
設(shè)置 sortByPosition 為 true 可以按文本位置提取內(nèi)容,否則獲取到的內(nèi)容錯亂,無法獲取到真正需要的內(nèi)容
@RequestMapping("uploadReceiptsTest") @ResponseBody public Map<String,String> uploadReceiptsTest() throws Exception{ String filePath = "D:/apache-tomcat-8.5.98/webapps/tspspic/發(fā)票文件/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; }
文本內(nèi)容(部分內(nèi)容以*替代)
public static void main(String[] args) { String invoiceInfo = "電子發(fā)票(普通發(fā)票) 發(fā)票號碼:******\n" + "開票日期:******\n" + "購 名稱:****** 銷 名稱:******\n" + "買 售\n" + "方 方\n" + "信 統(tǒng)一社會信用代碼/納稅人識別號:****** 信 統(tǒng)一社會信用代碼/納稅人識別號:******\n" + "息 息\n" + "項目名稱 規(guī)格型號 單 位 數(shù) 量 單 價 金 額 稅率/征收率 稅 額\n" + "******* 100ml:5g 袋 800 4.070796 3256.64 13% 423.36\n" + "******\n" + "合 計 ¥3256.64 ¥423.36\n" + "價稅合計(大寫) 叁仟陸佰捌拾圓整 (小寫)¥3680.00\n" + "批號:******/ 生產(chǎn)日期:2024-05-15/ 有效期至:2026-04-30/ 含稅單價:4.6000/ 生產(chǎn)廠家:******/ 批準(zhǔn)文號:******/\n" + "備 注\n" + "開票人:王寧\n" + "王寧";
解析文件內(nèi)容,返回數(shù)據(jù)
初版測試
public Map<String,String> pdfStr(String invoiceInfo) { //因解析出的括號不確定為中文還是英文,統(tǒng)一替換為英文字符 invoiceInfo = invoiceInfo.replaceAll("(","(").replaceAll(")",")"); // 定義正則表達(dá)式模式 Pattern patternInvoiceNumber = Pattern.compile("發(fā)票號碼:(\\d+)"); Pattern patternInvoiceDate = Pattern.compile("開票日期:(\\d{4}年\\d{1,2}月\\d{1,2}日)"); Pattern patternBuyerName = Pattern.compile("購 名稱:(.+?) 銷 名稱:(.+?)\n"); //由上圖可以發(fā)現(xiàn)“項目名稱 規(guī)格型號 單 位 數(shù) 量 單 價 金 額 稅率/征收率 稅 額”所需要的內(nèi)容在下一行數(shù)據(jù),使用笨方法直接獲取“稅額與合計”之間的數(shù)據(jù)通過之后的空格分割進(jìn)行獲取數(shù)據(jù) Pattern patternItemDetails = Pattern.compile("稅 額\\s+(.*?)合 計", Pattern.DOTALL); Pattern patternTotal = Pattern.compile("\\(小寫\\)¥(\\d+(\\.\\d+)?)"); Pattern patternBatchNumber = Pattern.compile("批號:(.+?)/"); Pattern patternProductionDate = Pattern.compile("生產(chǎn)日期:(\\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("生產(chǎn)廠家:(.+?)/"); Pattern patternApprovalNumber = Pattern.compile("批準(zhǔn)文號:(.+?)/"); Pattern patternIssuer = Pattern.compile("開票人:(.+)"); // 創(chuàng)建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); // 提取數(shù)據(jù) 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); } // 處理項目名稱、規(guī)格型號、單位、數(shù)量、單價、金額、稅率/征收率、稅額 if (matcherItemDetails.find()) { String itemDetailsLine = matcherItemDetails.group(1).trim(); itemDetailsLine = itemDetailsLine.replace("\n"," "); String[] details = itemDetailsLine.split(" "); // 按空格分割 if (details.length >= 8) { // 確保有足夠的字段 //因部分名稱過長,換行數(shù)據(jù)解析到最后進(jìn)行拼接 productName = details[0].trim(); // 項目名稱 if (details.length >= 9){ productName = details[0].trim()+details[8].trim(); // 項目名稱 } specification = details[1].trim(); // 規(guī)格型號 unit = details[2].trim(); // 單位 quantity = Integer.parseInt(details[3].trim()); // 數(shù)量 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("規(guī)格型號: " + specification); System.out.println("單位: " + unit); System.out.println("數(shù)量: " + 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); } // 輸出其他結(jié)果 System.out.println("發(fā)票號碼: " + invoiceNumber); System.out.println("開票日期: " + invoiceDate); System.out.println("購買方名稱: " + buyerName); System.out.println("銷售方名稱: " + sellerName); System.out.println("價稅合計: " + total); System.out.println("批號: " + batchNumber); System.out.println("生產(chǎn)日期: " + productionDate); System.out.println("有效期至: " + expirationDate); System.out.println("含稅單價: " + taxIncludedPrice); System.out.println("生產(chǎn)廠家: " + manufacturer); System.out.println("批準(zhǔn)文號: " + approvalNumber); System.out.println("開票人: " + issuer); }
優(yōu)化代碼
Map存儲正則表達(dá)式:將所有正則表達(dá)式模式和對應(yīng)的字段名稱存儲在一個Map中,遍歷Map并執(zhí)行匹配,從而避免了為每個字段都寫單獨的匹配代碼。
抽取通用邏輯:將匹配邏輯抽象成一個通用方法,簡化了代碼結(jié)構(gòu),減少了重復(fù)代碼。
處理商品詳情:在匹配完itemDetails后,再拆分字符串并填充對應(yīng)的字段。
public static Map<String, String> pdfStr(String invoiceInfo) { invoiceInfo = invoiceInfo.replaceAll("(", "(").replaceAll(")", ")"); // 定義正則表達(dá)式模式 Map<String, String> patterns = new HashMap<>(); patterns.put("invoiceNumber", "發(fā)票號碼:(\\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", "生產(chǎn)日期:(\\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", "生產(chǎn)廠家:(.+?)/"); patterns.put("approvalNumber", "批準(zhǔn)文號:(.+?)/"); patterns.put("issuer", "開票人:(.+)"); // 提取數(shù)據(jù) 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()); } } // 處理項目名稱、規(guī)格型號、單位、數(shù)量、單價、金額、稅率/征收率、稅額 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()); } } // 打印結(jié)果 for (Map.Entry<String, String> entry : result.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } return result; }
到此這篇關(guān)于Java解析pdf格式發(fā)票的代碼實現(xiàn)的文章就介紹到這了,更多相關(guān)Java解析pdf格式發(fā)票內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解如何用spring Restdocs創(chuàng)建API文檔
這篇文章將帶你了解如何用spring官方推薦的restdoc去生成api文檔。具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05JAVA 生成隨機(jī)數(shù)并根據(jù)后臺概率靈活生成的實例代碼
本篇文章主要介紹了JAVA 生成隨機(jī)數(shù)并根據(jù)后臺概率靈活生成的實例代碼,具有一定的參考價值,有興趣的可以了解一下2017-08-08SpringBoot整合Kaptcha實現(xiàn)圖形驗證碼功能
這篇文章主要介紹了SpringBoot整合Kaptcha實現(xiàn)圖形驗證碼功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09java springmvc 注冊中央調(diào)度器代碼解析
這篇文章主要介紹了java springmvc 注冊中央調(diào)度器代碼解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-08-08Java使用路徑通配符加載Resource與profiles配置使用詳解
這篇文章主要介紹了Java使用路徑通配符加載Resource與profiles配置使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Spring Boot MyBatis 連接數(shù)據(jù)庫配置示例
本篇文章主要介紹了Spring Boot MyBatis 連接數(shù)據(jù)庫示例的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02