java - 将单词直接插入到数据库中的表中,而不是使用 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
也会抛出一个。
同时,我需要将它存储在一个数组中,以便用正则表达式“替换”,如代码所示。有没有办法在提取后立即将单词直接插入数据库,同时过滤单词以适应我需要的正则表达式?
解决方案
我相信真正的问题是您如何阅读 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 (不是偶然:),因此它将读取的流传递给自定义编写器,您将直接将其发送到数据库。它仅将当前行存储在内存中。