目录
5.1.3:该函数判断一个字符串是否包含标点符号(中文标点符号)
随着数字化时代的到来,PDF文件已经成为我们日常工作中不可或缺的一部分。然而,对于那些需要从PDF文件中提取纯文本内容的人来说,这可能是一项挑战。因此,本文旨在介绍一种简单有效的方法,帮助您快速提取PDF文件的纯文本内容。
首先,您需要一个能够提取PDF文件纯文本内容的工具。目前,许多免费和付费的工具都提供了这一功能。其中,Adobe Acrobat和Foxit PDF Editor是两个比较受欢迎的选择。
我这里提供一种免费的提纯代码、需要根据你的业务需求、做出相应的改动!
直接上代码、如下:
一、PDF文件流
1、如何获取PDF文件流、就不在这说了、大家应该都知道哈、做好try就OK了。
二、文件流转换字节流
使用 FileCopyUtils 将你的文件流、转换为字节流、当然可以使用别的方法。
// 输入流中转换为字节流
byte[] repeatByteArray = FileCopyUtils.copyToByteArray(“你自己的文件流”);
三、通过 SpirePdf 解析内容
直接上代码、如下:
用的一些静态变量、根据你的需求改、即可!
SPECIAL_CHARACTER_EMPTY = ""
DEFAULT_FILE_END_CHARACTER = "......</br>"
DEFAULT_FILE_CONTENT = 这个你看你想返回啥、你就修改成什么即可!
/**
* 通过SpirePdf解析内容
*
* @param fileName String 文件名
* @param byteArray byte[] 文件字节流
* @param size 预览页码
* @return 解析的内容
*/
public String getContentBySpirePdf(String fileName, byte[] byteArray, int size) {
try {
//创建PdfDocument实例
PdfDocument doc = new PdfDocument(byteArray);
// 获取页码
int endPage = doc.getPages().getCount();
logger.info("info message: 文件 <{}> 的总页数是 <{}>", fileName, endPage);
// 文档未结束后缀,默认为空字符串
String suffix = ConstantUtil.SPECIAL_CHARACTER_EMPTY;
// 总页码大于预览页码,只获取预览页码的文本信息,并添加文档未结束后缀标识
if (endPage > size) {
endPage = size;
suffix = ConstantUtil.DEFAULT_FILE_END_CHARACTER;
}
return pdfToTextBySpirePDF(doc, endPage, suffix, fileName);
}
// 获取预览数据异常
catch (Exception e) {
logger.error("error message: 解析文件 <{}> 字节流至 PDF 失败,原因是:", fileName, e);
return ConstantUtil.DEFAULT_FILE_CONTENT;
}
}
四、提取PDF纯文本信息并自动生成摘要返回
静态常量如下:
SPECIAL_CHARACTER_EMPTY = "" PARAGRAPH_DELIMITER = "</br>" DEFAULT_FILE_MATCH_CHARACTER = "\\s*"
代码如下:
/**
* 提取PDF纯文本信息并自动生成摘要返回
*
* @param doc doc
* @param endPage endPage
* @param suffix suffix
* @param fileName fileName
* @return 摘要
*/
private String pdfToTextBySpirePDF(PdfDocument doc, int endPage, String suffix, String fileName) {
logger.info("info message: 开始从文件 <{}> 提取纯文本信息", fileName);
PdfPageBase page;
List<String> stringList = new ArrayList<>();
List<String> allStringList = new ArrayList<>();
//遍历PDF页面,获取每个页面的文本并添加到StringBuilder对象
for (int i = 0; i < endPage; i++) {
page = doc.getPages().get(i);
String text = page.extractText(false);
if (containsCatalogue(i, text)) {
continue;
}
setStringList(doc, stringList, allStringList, i, page);
}
doc.close();
// 去除页眉信息
final List<String> collect = allStringList.stream().filter(i -> !Objects.equals(i, ConstantUtil.SPECIAL_CHARACTER_EMPTY))
// 获得元素出现频率的 Map,键为元素,值为元素出现的次数
.collect(Collectors.toMap(e -> e, e -> 1, Integer::sum))
.entrySet()
// 所有 entry 对应的 Stream
.stream()
// 过滤出元素出现次数大于 1 (重复元素)的 entry
.filter(e -> e.getValue() > 1)
// 获得 entry 的键(重复元素)对应的 Stream
.map(Map.Entry::getKey)
.collect(Collectors.toList());
if (!CollectionUtil.isEmpty(collect)) {
for (String str : collect) {
stringList.removeIf(str::equals);
}
}
String text = getFormatContent(stringList);
if (StringUtil.isEmpty(text.replace(ConstantUtil.PARAGRAPH_DELIMITER, ConstantUtil.SPECIAL_CHARACTER_EMPTY)) || text.matches(ConstantUtil.DEFAULT_FILE_MATCH_CHARACTER)) {
logger.info("info message: 无法从文件 <{}> 提取有效的纯文本信息", fileName);
return ConstantUtil.DEFAULT_FILE_CONTENT;
}
// 填充结束符
else {
logger.info("info message: 从文件 <{}> 提取纯文本信息成功", fileName);
return text.concat(suffix);
}
}
4.1:判断是否包含目录
常量如下:
public static final String DEFAULT_FILE_MATCH_CHARACTER = "\\s*";
public static final String SPECIAL_CHARACTER_EMPTY = "";
public static final String CATALOGUE_REGEX_ONE = "(.*)\\·{7,}(.*)";public static final String CATALOGUE_REGEX = "(.*)\\.{7,}(.*)";
/**
* 判断是否包含目录
*
* @param i 页码
* @param text 当前页面提取的文本
* @return true-包含;false-不包含
*/
private boolean containsCatalogue(int i, String text) {
if (text.replaceAll(ConstantUtil.DEFAULT_FILE_MATCH_CHARACTER, ConstantUtil.SPECIAL_CHARACTER_EMPTY).matches(ConstantUtil.CATALOGUE_REGEX_ONE)
|| text.replaceAll(ConstantUtil.DEFAULT_FILE_MATCH_CHARACTER, ConstantUtil.SPECIAL_CHARACTER_EMPTY).matches(ConstantUtil.CATALOGUE_REGEX)) {
logger.info("跳过第<{}>页-目录页面", i + 1);
return true;
}
return false;
}
五、过滤最终得到可用行内容集合
直接上代码、代码提供参考、也可直接用、根据你的需要进行、调整以及优化
静态常量如下:
public static final String CHINESE_PUNCTUATION_ONE_REGEX = "[\uff08\uff09\u300a\u300b]";
public static final String DEFAULT_FILE_MATCH_CHARACTER = "\\s*";
public static final String SPECIAL_CHARACTER_EMPTY = "";
public static final String TABLE_XXX_REGEX = "\\[Table_[A-Za-z\\s]+]";public static final String SPECIAL_CHARACTER_LEFT_BOOK_TITLE = "《";
public static final String SPECIAL_CHINESE_CHARACTER_DOT = "。";
public static final String SPECIAL_CHINESE_CHARACTER_SEMICOLON = ";";public static final String SPECIAL_CHINESE_CHARACTER_QUESTION_MARK = "?";
public static final String DATE_REGEX = ".*\\d{4}-\\d{1,2}-\\d{1,2}\\s*$";
public static final String SPECIAL_CHARACTER_TWO_HYPHEN = "——";
public static final String WRAP_REGEX_ONE = "\r\n";
public static final String PAGE_NUMBER_REGEX = "\\s*\\S?\\d\\S?\\s*";
/**
* 过滤最终得到可用行内容集合
*
* @param doc PdfDocument
* @param stringList 过滤后的字符串
* @param allStringList 所有的字符串(为了过滤掉页眉)
* @param i 页码
* @param page PdfPageBase
*/
private void setStringList(PdfDocument doc, List<String> stringList, List<String> allStringList, int i, PdfPageBase page) {
boolean thisContainImage = checkThisContainImage(doc, i);
boolean thisContainTable = checkContainTable(doc, i);
List<String> textList;
// 包含有图片表格的处理逻辑
if (thisContainImage || thisContainTable) {
String text = page.extractText(false);
textList = text.lines().collect(Collectors.toList());
allStringList.addAll(textList);
int size = textList.size();
textList = textList.stream()
// 过滤掉空白行
.filter(s -> !s.matches(ConstantUtil.DEFAULT_FILE_MATCH_CHARACTER))
// 不包含中文标点
.filter(DownloadServiceImpl::checkConditionOne)
.filter(s -> checkConditionOne(s.replaceAll(ConstantUtil.CHINESE_PUNCTUATION_ONE_REGEX, ConstantUtil.SPECIAL_CHARACTER_EMPTY)))
// 以《开头的过滤掉
.filter(s -> !s.replaceAll(ConstantUtil.TABLE_XXX_REGEX, ConstantUtil.SPECIAL_CHARACTER_EMPTY)
.replaceAll(ConstantUtil.DEFAULT_FILE_MATCH_CHARACTER, ConstantUtil.SPECIAL_CHARACTER_EMPTY)
.startsWith(ConstantUtil.SPECIAL_CHARACTER_LEFT_BOOK_TITLE))
// 总行数小前于2的且当行长度小于40且没有以中文句号结尾的过滤掉
.filter(s -> {
if (size < 2) {
return true;
} else if (getStrLength(s.replaceAll(ConstantUtil.DEFAULT_FILE_MATCH_CHARACTER, ConstantUtil.SPECIAL_CHARACTER_EMPTY)) <= 40) {
return s.trim().endsWith(ConstantUtil.SPECIAL_CHINESE_CHARACTER_DOT)
|| s.trim().endsWith(ConstantUtil.SPECIAL_CHINESE_CHARACTER_SEMICOLON)
|| s.trim().endsWith(ConstantUtil.SPECIAL_CHINESE_CHARACTER_QUESTION_MARK);
} else {
return true;
}
})
// 以日期格式结尾的过滤掉
.filter(s -> !s.matches(ConstantUtil.DATE_REGEX))
// 中文之间包含空格的过滤掉
// .filter(s -> !s.matches(".*[\u4e00-\u9fa5]\\s+[\u4e00-\u9fa5].*"))
// 含有 —— 的过滤掉
.filter(s -> !s.contains(ConstantUtil.SPECIAL_CHARACTER_TWO_HYPHEN))
.filter(s -> !checkConditionTwo(s))
.collect(Collectors.toList());
}
// 通过空格保留原格式处理逻辑
else {
String text = page.extractText(false);
text = text// 多个换行只保留一个
.replaceAll("[" + ConstantUtil.WRAP_REGEX_ONE + "]+", ConstantUtil.WRAP_REGEX_ONE);
textList = text.lines().collect(Collectors.toList());
allStringList.addAll(textList);
}
textList = textList.stream()
// 过滤掉空白行
.filter(s -> !s.matches(ConstantUtil.DEFAULT_FILE_MATCH_CHARACTER))
// 过滤掉页码
.filter(s -> !s.matches(ConstantUtil.PAGE_NUMBER_REGEX))
.collect(Collectors.toList());
stringList.addAll(textList);
}
5.1: 以上代码所用到的其他方法如下
5.1.1:判断页面是否包含图片
代码提供参考、根据你当前的需求进行、优化或者更新!(可直接使用)
/**
* 判断页面是否包含有图片
*
* @param pdf PdfDocument
* @param pageNumber 页码
* @return true - 按照图片处理;false - 按照纯文本处理
*/
private static boolean checkThisContainImage(PdfDocument pdf, int pageNumber) {
boolean flag = false;
PdfPageBase pdfPageBase = pdf.getPages().get(pageNumber);
PdfImageInfo[] imagesInfos = pdfPageBase.getImagesInfo();
if (imagesInfos != null && imagesInfos.length > 0) {
for (int i = 0; i < imagesInfos.length; i++) {
if (imagesInfos[i].getImage().getHeight() > 100
&& imagesInfos[i].getImage().getWidth() > 500) {
logger.info("图片<{}>的高度<{}> 大于 100 ,宽度<{}> 大于 500, 判定为包含图片", i + 1, imagesInfos[i].getImage().getHeight(), imagesInfos[i].getImage().getWidth());
flag = true;
break;
}
}
}
return flag;
}
5.1.2:判断是否有表格
代码提供参考、根据你当前的需求进行、优化或者更新!(可直接使用)
/**
* 判断页面是否包含有表格
*
* @param pdf PdfDocument
* @param pageNumber 页码
* @return true - 包含;false - 不包含
*/
private static boolean checkContainTable(PdfDocument pdf, int pageNumber) {
boolean flag = false;
//创建PdfTableExtractor类的对象
PdfTableExtractor extractor = new PdfTableExtractor(pdf);
//提取页面中的表格存入PdfTable[]数组
PdfTable[] tableLists = extractor.extractTable(pageNumber);
if (tableLists != null && tableLists.length > 0) {
flag = true;
}
return flag;
}
5.1.3:该函数判断一个字符串是否包含标点符号(中文标点符号)
/**
* 该函数判断一个字符串是否包含标点符号(中文标点符号)。
*
* @param s 待校验字符串
* @return true-包含;false-不包含
*/
public static boolean checkConditionOne(String s) {
boolean b = false;
String tmp = s;
tmp = tmp.replaceAll(ConstantUtil.CHINESE_PUNCTUATION_REGEX, ConstantUtil.SPECIAL_CHARACTER_EMPTY);
if (s.length() != tmp.length()) {
b = true;
}
return b;
}
public static boolean checkConditionTwo(String s) {
boolean b = false;
String tmp = s;
tmp = tmp.replaceAll(ConstantUtil.PDF_CONDITION_ONE, ConstantUtil.SPECIAL_CHARACTER_EMPTY)
.replace(ConstantUtil.PDF_CONDITION_TWO, ConstantUtil.SPECIAL_CHARACTER_EMPTY)
.replace(ConstantUtil.PDF_CONDITION_THREE, ConstantUtil.SPECIAL_CHARACTER_EMPTY);
if (s.length() != tmp.length()) {
b = true;
}
return b;
}
六、获取格式化后的文本内容
代码提供参考、根据你当前的需求进行、优化或者更新!(可直接使用)
静态常量:
public static final String SPECIAL_CHARACTER_EMPTY = "";
public static final String SPECIAL_CHARACTER_BACKSPACE = " ";
public static final String WRAP_REGEX_ONE = "\r\n";
public static final String TABLE_XXX_REGEX = "\\[Table_[A-Za-z\\s]+]";
public static final String TABLE_XXX_REGEX_ONE = "\\[table_[A-Za-z\\s]+]";
public static final String FORMAT_CONTENT_REGEX = "[。?!::][\\s\r\n]";
public static final String PARAGRAPH_DELIMITER = "</br>";
/**
* 获取格式化后的文本内容
*
* @param stringList 每行文本内容集合
* @return 格式化后的文本内容
*/
private String getFormatContent(List<String> stringList) {
if (CollectionUtil.isEmpty(stringList)) {
return ConstantUtil.SPECIAL_CHARACTER_EMPTY;
}
// 文本内容对齐,需要补空格的补空格
List<Integer> indexOfNonWhitespace = new ArrayList<>();
for (String s : stringList) {
int indexOfNonWhitespaceAfterWhitespace = getIndexOfNonWhitespaceAfterWhitespace(s);
indexOfNonWhitespace.add(indexOfNonWhitespaceAfterWhitespace);
}
int mostConsecutive = findMostConsecutive(indexOfNonWhitespace);
if (mostConsecutive > 1) {
for (int i = 0; i < stringList.size(); i++) {
int indexOfNonWhitespaceAfterWhitespace = getIndexOfNonWhitespaceAfterWhitespace(stringList.get(i));
if (indexOfNonWhitespaceAfterWhitespace == -1) {
stringList.set(i, ConstantUtil.SPECIAL_CHARACTER_BACKSPACE.repeat(mostConsecutive).concat(stringList.get(i)));
}
}
}
String content = String.join(ConstantUtil.WRAP_REGEX_ONE, stringList);
content = content.replaceAll(ConstantUtil.TABLE_XXX_REGEX, ConstantUtil.SPECIAL_CHARACTER_EMPTY)
.replaceAll(ConstantUtil.TABLE_XXX_REGEX_ONE, ConstantUtil.SPECIAL_CHARACTER_EMPTY);
StringBuilder stringBuilder = new StringBuilder(content);
Pattern pattern = Pattern.compile(ConstantUtil.FORMAT_CONTENT_REGEX);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
int start = stringBuilder.indexOf(matcher.group());
String replace = stringBuilder.substring(start, start + 1).concat(ConstantUtil.PARAGRAPH_DELIMITER);
stringBuilder.replace(start, start + 1, replace);
}
stringBuilder.append(ConstantUtil.PARAGRAPH_DELIMITER);
return new String(stringBuilder).replaceAll("[" + ConstantUtil.WRAP_REGEX_ONE + "]+", "");
}
6.1:获取文本第一个分空格字符索引位置
直接上代码了............
/**
* 获取文本第一个分空格字符索引位置
*
* @param string 字符串
* @return 第一个分空格字符索引位置
*/
public static int getIndexOfNonWhitespaceAfterWhitespace(String string) {
char[] characters = string.toCharArray();
boolean lastWhitespace = false;
for (int i = 0; i < string.length(); i++) {
if (Character.isWhitespace(characters[i])) {
lastWhitespace = true;
} else if (lastWhitespace) {
return i;
} else {
break;
}
}
return -1;
}
6.2:获取出现次数最多的数值
/**
* 获取出现次数最多的数值
*
* @param arr 数值集合
* @return 出现次数最多的数值
*/
public static int findMostConsecutive(List<Integer> arr) {
int res = arr.get(0);
int count = 1;
int maxCount = 1;
for (int i = 1; i < arr.size(); i++) {
if ((arr.get(i) != -1) && (Objects.equals(arr.get(i), arr.get(i - 1)))) {
count++;
if (count > maxCount) {
maxCount = count;
res = arr.get(i);
}
} else {
count = 1;
}
}
return res;
}
结尾:
到此PDF文件、提纯就结束了、以上代码提供参考、也可直接用、直接复制粘贴过去即可、常量、用到的所有方法、均已给出、如果有问题可以直接评论、后面在补有问题的地方、如果有别的好办法也可以一起讨论一下哟。