提取PDF文件纯文本内容

目录

一、PDF文件流

二、文件流转换字节流   

三、通过 SpirePdf 解析内容

四、提取PDF纯文本信息并自动生成摘要返回

4.1:判断是否包含目录

五、过滤最终得到可用行内容集合

5.1: 以上代码所用到的其他方法如下

5.1.1:判断页面是否包含图片

5.1.2:判断是否有表格

5.1.3:该函数判断一个字符串是否包含标点符号(中文标点符号)

六、获取格式化后的文本内容

6.1:获取文本第一个分空格字符索引位置

6.2:获取出现次数最多的数值


        随着数字化时代的到来,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文件、提纯就结束了、以上代码提供参考、也可直接用、直接复制粘贴过去即可、常量、用到的所有方法、均已给出、如果有问题可以直接评论、后面在补有问题的地方、如果有别的好办法也可以一起讨论一下哟。

### Anaconda、CUDA 和 PyTorch 的 2025 年安装指南 随着技术的发展,到 2025 年,Anaconda、CUDA 和 PyTorch 的安装流程可能会有所变化。以下是基于当前趋势和技术预测的安装指南。 #### 创建虚拟环境 为了隔离不同项目的依赖关系,在 Anaconda 中创建一个新的虚拟环境是一个良好的实践方法。通过以下命令可以完成这一操作: ```bash conda create -n pytorch_env_2025 python=3.9 ``` 此命令会创建名为 `pytorch_env_2025` 的新环境并指定 Python 版本为 3.9[^2]。 激活该虚拟环境可以通过如下命令实现: ```bash conda activate pytorch_env_2025 ``` #### 安装 CUDA 工具包 假设 NVIDIA 到 2025 年已经发布了支持最新 GPU 架构的新版本 CUDA,则需要下载对应的操作系统的 CUDA Toolkit。访问官方 NVIDIA CUDA 下载页面获取最新的稳定版工具包链接[^3]。 对于 Windows 用户来说,可能需要执行类似于下面这样的指令来安装特定版本的 CUDA(这里假定为 CUDA 13.x): ```bash choco install cuda-toolkit --version=13.x ``` 如果使用的是 Linux 或 macOS 系统,则应遵循相应的文档说明进行配置和编译选项设置。 #### 配置 PyTorch 安装源 考虑到清华大学开源镜像站在未来几年仍将继续提供稳定的软件分发服务,因此可以从其提供的 URL 地址中找到适合目标平台架构 (win-64, linux-64 etc.) 的预构建二进制文件列表[^1]: ```plaintext https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ ``` 根据实际需求选择合适的 PyTorch 软件包名称后,利用 pip 命令即可快速完成安装过程。例如针对具有 GPU 加速功能的支持 CUDA 13.x 的 PyTorch 发布版本可运行如下脚本片段: ```bash pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu13x ``` 以上步骤综合考虑了截至至2025年可能出现的技术更新情况以及社区推荐的最佳做法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值