2013-05-06 298 views
3

我使用下面的方法逐行提取pdf文本。但问题是,它不会读取单词和数字之间的空格。这可能是什么解决方案?如何使用带空格的itextsharp从pdf中提取文本?

我只是想创建一个字符串列表,列表对象中的每个字符串都有一个pdf文本行,因为它是包含空格的pdf。

public void readtextlinebyline(string filename) { 


     List<string> strlist = new List<string>(); 
     PdfReader reader = new PdfReader(filename); 
     string text = string.Empty; 
     for (int page = 1; page <= 1; page++) 
     { 

      text += PdfTextExtractor.GetTextFromPage(reader, page ,new LocationTextExtractionStrategy())+" "; 

     } 
     reader.Close(); 
     string[] words = text.Split('\n'); 
     foreach (string word in words) 
     { 
      strlist.Add(word); 
     } 

     foreach (string st in strlist) 
     { 
      Response.Write(st +"<br/>"); 
     } 

    } 

我已经尝试过将此策略更改为SimpleTextExtractionStrategy,但它也不适用于我。

+0

这[回答 “iText的java的PDF文本创建”](http://stackoverflow.com/questions/13644419/itext-java-pdf-to-text-creation/13645183#13645183)可以示出一个解决方案的原因和提示:复制文本extration策略并调整内部参数,在您的情况下,默认情况下将缺口的最小宽度识别为空格'renderInfo.getSingleSpaceWidth()/ 2f';使用'renderInfo.getSingleSpaceWidth()/ 4f'来回询问的人得到了改进的结果。 – mkl 2013-05-06 13:25:48

+0

@Pengu当你提供赏金时,你肯定会遇到这个问题。因此,您当然可以提供一个或多个样本PDF以作为提议解决方案的测试用例。这个问题的当前状态回答了纯粹的猜测。 – mkl 2013-11-11 16:19:46

+0

@mkl对于迟到的回复,我的连接断了,我很抱歉。我不喜欢的不是你的解决方案(它的工作原理) - 有什么不满的是,这个解决方案可能不可靠。 F.E:它可以与一个文件一起使用,但也可能在另一个文件中产生太多空格(原因是文档需要renderInfo.getSingleSpaceWidth()/ 2f或完全不同的分隔符)。我没有一个例子,但它的某些我可以想象它会发生。所以我要求一个“更多”可靠来源的答案。 – BudBrot 2013-11-18 07:53:20

回答

0

我有我自己的实现,它工作得很好。

/// <summary> 
    /// Read a PDF file and returns the string content. 
    /// </summary> 
    /// <param name="par">ByteArray, MemoryStream or URI</param> 
    /// <returns>FileContent.</returns> 
    public static string ReadPdfFile(object par) 
    { 
     if (par == null) throw new ArgumentNullException("par"); 

     PdfReader pdfReader = null; 
     var text = new StringBuilder(); 

     if (par is MemoryStream) 
      pdfReader = new PdfReader((MemoryStream)par); 
     else if (par is byte[]) 
      pdfReader = new PdfReader((byte[])par); 
     else if (par is Uri) 
      pdfReader = new PdfReader((Uri)par); 

     if (pdfReader == null) 
      throw new InvalidOperationException("Unable to read the file."); 

     for (var page = 1; page <= pdfReader.NumberOfPages; page++) 
     { 
      var strategy = new SimpleTextExtractionStrategy(); 
      var currentText = PdfTextExtractor.GetTextFromPage(pdfReader, page, strategy); 
      currentText = Encoding.UTF8.GetString(Encoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(currentText))); 
      text.Append(currentText); 
     } 

     pdfReader.Close(); 

     return text.ToString(); 
    } 
+2

当OP使用'LocationTextExtractionStrategy'时,您使用标准的iText(Sharp)文本提取机制和'SimpleTextExtractionStrategy'。虽然这肯定有所不同,但它们使用基本相同的启发式来确定分词符(空格字符或至少为空格字符一半的空位)。因此,这不会比原始代码做得更好。 – mkl 2013-11-18 13:16:27

+0

mkl是对的。这可能在某些情况下有效,但在像我这样的其他一些情况下会失败。 (由于编码不同,我得到了无法识别的字符)另外,SimpleTextExtractionStrategy在我的例子中没有正确插入'\ n',所以我必须构建自定义的RenderListener(因为我需要提取图像),并调整代码以满足我的要求,例如从orientationMagnitude == other更改检测新行的条件。对Math.Abs​​(orientationMagnitude - other.OrientationMagnitude)的OrientationMagnitude <10.显然它不会在所有情况下都有效。 – 2017-07-06 21:12:57

13

为什么的话,有时不能正常通过的iText(夏普),或者其他的PDF文本提取的认可之间的空间,已经在this answer to "itext java pdf to text creation"解释的背景:这些“空间”不必使用空格字符创建而是使用创建一个小差距的操作。这些操作也用于其他目的(不会打破单词),所以文本提取器必须使用启发式方法来确定这种间隔是否是分词符或不是...

这特别意味着您从来没有得到100%安全的分词检测。

虽然你可以做的是改善启发式。

iText和iTextSharp标准文本提取策略,例如,假设在一个线的字断如果

一个)有一个空格字符或

b)中有作为半空格字符的间隙至少宽。

项目a确实是命中,但项目b在密集设置文本的情况下可能经常失败。问题的OP到answer referenced above用空格字符的宽度的四分之一取得了相当好的结果。

您可以通过复制和更改您选择的文本提取策略来调整这些标准。

SimpleTextExtractionStrategy你觉得这个标准嵌入renderText方法:

if (spacing > renderInfo.GetSingleSpaceWidth()/2f){ 
    AppendTextChunk(' '); 
} 

LocationTextExtractionStrategy的情况下,该标准同时已投入了自己的方法:

/** 
* Determines if a space character should be inserted between a previous chunk and the current chunk. 
* This method is exposed as a callback so subclasses can fine tune the algorithm for determining whether a space should be inserted or not. 
* By default, this method will insert a space if the there is a gap of more than half the font space character width between the end of the 
* previous chunk and the beginning of the current chunk. It will also indicate that a space is needed if the starting point of the new chunk 
* appears *before* the end of the previous chunk (i.e. overlapping text). 
* @param chunk the new chunk being evaluated 
* @param previousChunk the chunk that appeared immediately before the current chunk 
* @return true if the two chunks represent different words (i.e. should have a space between them). False otherwise. 
*/ 
protected bool IsChunkAtWordBoundary(TextChunk chunk, TextChunk previousChunk) { 
    float dist = chunk.DistanceFromEndOf(previousChunk); 
    if(dist < -chunk.CharSpaceWidth || dist > chunk.CharSpaceWidth/2.0f) 
     return true; 
    return false; 
} 

的将其纳入其自身方法的意图仅仅是要求对策略进行简单的子类别划分,并重写此方法来调整启发式标准。这适用于等效的iText Java类的情况,但在iTextSharp的端口中很不幸,没有virtual已被添加到声明中(从版本5.4.4开始)。因此,iTextSharp仍然需要复制整个策略。

@布鲁诺你可能想告诉iText - > iTextSharp移植团队这件事。

虽然您可以在这些代码位置上对文本提取进行微调,但您应该知道在这里不会找到100%的条件。原因如下:

  • 密集设置的文本中的单词之间的间隙可能小于字距内的某些光学效应的字距或其他间隔。因此,这里没有一刀切的因素。
  • 在PDF中根本不使用空格字符(因为您始终可以使用空格,这是可能的),“空格字符的宽度”可能是一些随机值或根本无法确定!
  • 有些有趣的PDF滥用空格字符宽度(可随时单独拉伸以供操作遵循),以在使用空格打破分词时执行一些表格格式。在这样的PDF中,空格字符的当前宽度的值不能被严重用于确定分词符。
  • 有时,您会发现一行中的单词在打印间隔为强调。这些可能会被大多数启发式算法解析为一个字母单词的集合。

你可以得到比iText的启发,更好地那些使用其他常量考虑到实际的视觉自由空间中的所有字符之间(使用PDF渲染或字体信息分析机制)它衍生出来的,但对于一个感知的改进你必须投入很多时间。

+0

优秀的写作。@mkl,你可能想在iText bug跟踪器中打开一个关于iTextSharp端口的问题(不知道布鲁诺是否会看到这个问题)。 – 2014-02-25 04:31:28

+1

据我所知,iTextSharp Port同时为'LocationTextExtractionStrategy'方法增加了'virtual'。其实不仅仅是这种方法,实际上也是每一种“公共”方法。 – mkl 2014-02-25 05:16:18

+0

明智的答案。确切地说,我需要的信息非常全面和清晰。非常感谢。 – Jansky 2016-03-07 11:13:06

0
using (PdfReader reader = new PdfReader(path)) 
      { 
       StringBuilder text = new StringBuilder(); 
       StringBuilder textfinal = new StringBuilder(); 
       String page = ""; 
       for (int i = 1; i <= reader.NumberOfPages; i++) 
       { 
        text.Append(PdfTextExtractor.GetTextFromPage(reader, i)); 
        page = PdfTextExtractor.GetTextFromPage(reader, i); 
        string[] lines = page.Split('\n'); 
        foreach (string line in lines) 
        { 
         string[] words = line.Split('\n'); 
         foreach (string wrd in words) 
         { 

         } 
         textfinal.Append(line); 
         textfinal.Append(Environment.NewLine); 
        } 
        page = ""; 
       } 
      } 
+0

你想用该代码表达什么? – mkl 2016-04-28 13:11:30

相关问题