在 C# 中将 PDF 转换为文本
这是“在 C# 中将 PDF 转换为文本”的替代方案
介绍
本文演示了如何使用 iTextSharp .NET 库将 PDF 文件转换为文本。
背景
我似乎一直在寻找一种更好的方法来将 PDF 文件转换为文本(以便我可以编辑它,用正则表达式解析它等等)。而且我们谈论的不是几页 PDF - 我每天收到的 PDF 格式的报告长达 200-300 页。
我开始使用一个 Python 库来完成 PDF 到文本的转换。这似乎是一个不错的选择,因为我计划使用 Python 来解析 PDF。不幸的是,使用此方法转换单个 200 多页的 PDF 需要几分钟的时间(在相当快的机器上)。不可接受。
Code Project 来拯救!我的下一个解决方案是关于使用 PDFBox 的原始 PDF 到文本的文章。通过使用这种方法,我的 PDF 转换从几分钟缩短到大约 10 秒(同样适用于 200 多页的 PDF)。一切都好,对吧?
好吧... 肯定是一个巨大的进步。但是,知道代码依赖于 Java 虚拟机并且因此比 Java 版本慢,这让我感觉不太好。因此,再加上我是一个在空闲周末会变成超级书呆子的人,我决定重新审视原始文章中列出的潜在解决方案。我能够转换使用 iText 库的 Java 源代码并利用 iTextSharp 版本的相同库。最终结果是,我现在可以在不到一秒钟的时间内将 250 页的 PDF 文件转换为文本。
使用代码
这是我的项目的完整 C# 代码。正如我在介绍中提到的,我只是从 Java 转换过来的,所以你可能会看到一些设计上的怪异。尽管如此,代码非常短。而且生成的应用程序速度很快!
此代码引用了几个 iTextSharp dll。我已将它们包含在项目下载文件中,但您也可以在 sourceforge 上找到它们(用于将来的更新等)。
using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
public class ParsingPDF {
static string PDF;
static string TEXT2;
/**
* Parses the PDF using PRTokeniser
* @param src the path to the original PDF file
* @param dest the path to the resulting text file
*/
public void parsePdf(String src, String dest)
{
PdfReader reader = new PdfReader(src);
StreamWriter output = new StreamWriter(new FileStream(dest, FileMode.Create));
int pageCount = reader.NumberOfPages;
for (int pg = 1; pg <= pageCount; pg++)
{
// we can inspect the syntax of the imported page
byte[] streamBytes = reader.GetPageContent(pg);
PRTokeniser tokenizer = new PRTokeniser(streamBytes);
while (tokenizer.NextToken())
{
if (tokenizer.TokenType == PRTokeniser.TokType.STRING)
{
output.WriteLine(tokenizer.StringValue);
}
}
}
output.Flush();
output.Close();
}
/**
* Main method.
*/
static void Main(string[] args)
{
if (args.Length < 1 || args.Length > 2)
{
Console.WriteLine("USAGE: ParsePDF infile.pdf <outfile.txt>");
return;
}
else if (args.Length == 1)
{
PDF = args[0];
TEXT2 = Path.GetFileNameWithoutExtension(PDF) + ".txt";
}
else
{
PDF = args[0];
TEXT2 = args[1];
}
try
{
DateTime t1 = DateTime.Now;
ParsingPDF example = new ParsingPDF();
example.parsePdf(PDF, TEXT2);
DateTime t2 = DateTime.Now;
TimeSpan ts = t2 - t1;
Console.WriteLine("Parsing completed in {0:0.00} seconds.", ts.TotalSeconds);
}
catch (Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
} // class
public class MyTextRenderListener : IRenderListener
{
/** The print writer to which the information will be written. */
protected StreamWriter output;
/**
* Creates a RenderListener that will look for text.
*/
public MyTextRenderListener(StreamWriter output)
{
this.output = output;
}
public void BeginTextBlock()
{
output.Write("<");
}
public void EndTextBlock()
{
output.WriteLine(">");
}
public void RenderImage(ImageRenderInfo renderInfo)
{
}
public void RenderText(TextRenderInfo renderInfo)
{
output.Write("<");
output.Write(renderInfo.GetText());
output.Write(">");
}
} // class
} // namespace
兴趣点
再次看到一些 Java 代码很有趣。我五年多没有用 Java 做任何严肃的事情了,但让我印象深刻的是 Java 代码与 C# 代码的接近程度。这使得转换相对容易。
我最初计划加入任务并行库来尝试加速结果,但那是在我意识到非并行版本的性能不到半秒之前。只是为了好玩,我可能会尝试 TPL。这将是一个很好的学习练习,并且看看 TPL 的表现会很有趣。对于一个多页的 PDF 文档,我相信 TPL 不会启动数百个线程,例如。但它会启动多少个线程呢?
因此,如果我追求 TPL 版本,我将继续更新这篇文章。此外,我想在我的新宠儿:F# 中实现它。
历史
原始版本发布于 2012 年 10 月 22 日。