首页 > 解决方案 > 将单词直接插入到数据库中的表中,而不是使用 Java 存储在数组列表中

问题描述

我正在构建一个索引程序,在其中提取文件(PDF)并提取其中的所有单词并将其存储在arrayList. 同时,我必须定义我的单词标记以查看要索引的单词类型及其规则,因此我将它们存储在 arrayList 中,以便我可以替换正则表达式以满足我的需要。

代码:

public void index(String path) throws Exception {
    ArrayList<String> list = new ArrayList<String>();
    PDDocument document = PDDocument.load(new File(path));

    if (!document.isEncrypted()) {
        PDFTextStripper tStripper = new PDFTextStripper();
        String pdfFileInText = tStripper.getText(document);
        String lines[] = pdfFileInText.split("\\r?\\n");
        for (String line : lines) {
            String[] words = line.split(" ");

            for (String word : words) {
                //check if one/more special characters at end of string then remove OR
                //check special characters in beginning of the string then remove

                list.add(word.replaceAll("([\\W]+$)|(^[\\W]+)", ""));

            }
        }
    }

    String[] words1 = list.toArray(new String[list.size()]);
    String sql = "insert IGNORE into  test.indextable123 values (?,?)";
    preparedStatement = con.connect().prepareStatement(sql);

    for (int i = 1; i < words1.length; i++) {
        preparedStatement.setString(1, words1[i]);
        preparedStatement.setString(2, path);
        preparedStatement.addBatch();

        if (i % 1000 == 0) {
            preparedStatement.executeBatch();
            System.out.print("Add Thousand");
        }
    }

    if (words1.length % 1000 > 0) {
        preparedStatement.executeBatch();
        System.out.print("Add Remaining");
    }

    preparedStatement.close();
    System.out.println("Completed");
}

这里的问题是,如果我想索引一个超过 1000 万字的文件,那么存储在 arrayList 中的资源效率不高,而且它outofmemory exception也会抛出一个。

同时,我需要将它存储在一个数组中,以便用正则表达式“替换”,如代码所示。有没有办法在提取后立即将单词直接插入数据库,同时过滤单词以适应我需要的正则表达式?

标签: java

解决方案


我相信真正的问题是您如何阅读 PDF 文件。您调用 String pdfFileInText = tStripper.getText(document);which 将整个文件中的文本加载到字符串中。然后你遍历它并插入到数据库中。将文件的内容加载到字符串中可能会导致内存问题,这就是我们通常使用流(如输入流、输出流等)的原因。它们为您提供了一种在您阅读文件时处理文件的方法,而不是批量加载然后处理它。

如果您检查PDFTextStripper的工作原理,您可以看到 getText 方法:

 public  String getText( PDDocument doc ) throws IOException
    {
        StringWriter outputStream = new StringWriter();
        writeText( doc, outputStream );
        return outputStream.toString();
    }

它使用 writeText 方法,该方法使用输出流并将其收集到字符串中。所以你有几个选择:

  • 为了避免内存峰值,您需要编写自定义 PDFTextStripper 并覆盖一些方法。例如,您可以覆盖 writeText 方法并将其更改为写入数据库。

  • 您可以逐页处理 PDF,这样您就可以限制负载 - 我相信有一种 processPage 方法可以用于对您的代码进行一些修改

  • 您可以通过创建自定义 OutputStream 来制作超级酷的解决方案,将内容直接存储到数据库中并将其传递给 PDFTextStripper 的 writeMethod

我发现最后一种方法是最有趣的一种(即使逐页处理它可能更健壮)。因此,我将给出一个示例代码,您可以参考。它仍然需要一些修改才能正常工作:

首先创建一个自定义编写器。就像是:

class MyDatabaseWriter extends java.io.Writer{

    private StringBuilder lineBuilder=new StringBuilder();
    //DB stuff go here

    @Override
    public void close() throws IOException {
        //Close DB Connection 
    }

    @Override
    public void flush() throws IOException {
    }

    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        String newString=new String(cbuf, off, len);
        lineBuilder.append(newString);
        lineBuilder.toString().matches("\\r?\\n");
        String lines[] = lineBuilder.toString().split("\\r?\\n");
        writeLineToDatabase(lines[0]);
        lineBuilder=new StringBuilder(lines[1]);
    }

    private void writeLineToDatabase(String line) {
        // Process your line and add it to the database
    }

}

然后将所有数据库内容移至编写器,在您的主类中,您应该具有以下内容:

PDDocument document = PDDocument.load(new File(path));
PDFTextStripper tStripper = new PDFTextStripper();
tStripper.writeText(document, new MyDatabaseWriter());  //Or if you create an instance in another way

PDFTextStripper 扩展了 PDFStreamEngine (不是偶然:),因此它将读取的流传递给自定义编写器,您将直接将其发送到数据库。它仅将当前行存储在内存中。


推荐阅读