2012-06-08 38 views
1

我期待實現Apache的POI的文本提取,後來,在我的計劃概要信息的功能。也就是說,poi爲.doc,docx,.xls和.xlsx文件使用不同的庫。如何抽象POI功能

婉婷隱藏這從調用POI功能的複雜性,我做了下面的類和方法,我可以打電話從任何文檔4種提取文本:

public class DocExtractor { 
private WordExtractor w = null; 
private XWPFWordExtractor wx = null; 
private ExcelExtractor x = null; 
private XSSFExcelExtractor xx = null; 

public DocExtractor(File f){ 
     String fileExtension = FilenameUtils.getExtension(f.toString()); 
     if (fileExtension.equals("doc")){ 
      try{ 
       FileInputStream is = new FileInputStream(f.getAbsolutePath()); 
       HWPFDocument doc = new HWPFDocument(is); 
       w = new WordExtractor(doc); 
      } 
      catch (Exception e){e.printStackTrace();} 
     } 

... 3個 'IFS' 在構造函數中

和方法:

public String getText(){ 
    String text =""; 
    if(this.w != null){ 
     String[] texted = w.getParagraphText(); //for .doc 
     text = this.joiner(texted); 
    } 

...但更多的 'IFS'

這工作,並隱藏了實現,

DocExtractor dm = new DocExtractor(doFile); 
    text = dm.getText(); 

但我討厭所有的 'IFS'。 我不禁想到必須有一個更好的,完全不同的方式來做到這一點,或者可能是一些多態的欺騙......

這些私有變量是從這個類的以前的嘗試遺留下來的,所以感覺自由把它們扔在你建議的任何東西中。

感謝

+0

爲什麼不使用[Apache Tika](http://tika.apache.org/)? Tika使用POI從辦公室文件中提取文本,但是以一種常用的方式完成所有文本 – Gagravarr

回答

2

這是一個非常常見的Java編程問題的例子。它通常使用所謂的Java工廠設計模式來解決。下面的鏈接對工廠模式有一個很好的簡單解釋 - http://www.allapplabs.com/java_design_patterns/factory_pattern.htm

還有很多其他的設計模式,您可能會發現它很有用。閱讀這些內容可以讓您深入瞭解大量Java程序員如何解決常見問題。同一作者在http://www.allapplabs.com/java_design_patterns/java_design_patterns.htm

解釋了大部分常見設計模式,現在就您的具體問題。首先,POI作者使用工廠設計模式。例如,看一下下面的代碼:

Workbook wb1 = WorkbookFactory.create(new FileInputStream("myXlsFile.xls")); 
Workbook wb2 = WorkbookFactory.create(new FileInputStream("myXlsxFile.xlsx")); 
// this prints "wb1 class = org.apache.poi.xssf.usermodel.XSSFWorkbook" 
System.out.println("wb1 class = " + wb1.getClass().getName()); 
// this prints "wb2 class = org.apache.poi.hssf.usermodel.HSSFWorkbook" 
System.out.println("wb2 class = " + wb2.getClass().getName()); 

所以,作爲POI的用戶,你處理具有相同的屬性和方法相同的工作簿對象無論你正在處理XLS文件或XLSX文件。然而,根據文件類型的不同,POI的作者顯然需要兩種截然不同的實現。

他們是如何做到這一點,而沒有太多的if語句,比如你的代碼中有什麼?我會重做你的榜樣,告訴你如何完成同樣的事情。

你會做的第一件事是定義一個類DocExtractor如下:

public abstract class DocExtractor { 

    // constructor 
    public DocExtractor(File f) { 
     poiFile = f; 
    } 

    // the getText method must be defined by all derived classes 
    public abstract String getText(); 

    // this protected field is visible to all classes which extend DocExtractor 
    protected File poiFile; 

} 

的原因,我建議你做DocExtractor抽象的原因是你不想代碼能夠創建一個DocExtractor類。你使getText方法抽象的原因是你想確保擴展DocExtactor的類將定義它們自己的getText版本。希望這個推理在您閱讀時會變得清晰。

您現在可以定義DocExtractor的派生類(它們「擴展」DocExtractor)。在這個例子中,我將定義兩個類,一個用於doc文件,一個用於xls文件。

// this handles doc files 
public class DocExtractorDoc extends DocExtractor { 

    // constructor 
    public class DocExtractorDoc(File f) { 
     // this calls the DocExtractor constructor which has common code for all constructors 
     super(f); 
     // put code specific to the DocExtractorDoc constructor here 
    } 

    // concrete implementation of the getText method specific to doc files 
    public String getText() { 
     // getText code for doc files goes here 
    } 
} 

// this handles xls files 
public class DocExtractorXls extends DocExtractor { 

    // constructor 
    public class DocExtractorXls(File f) { 
     // this calls the DocExtractor constructor which has common code for all constructors 
     super(f); 
     // put code specific to the DocExtractorXls constructor here 
    } 

    // concrete implementation of the getText method specific to xls files 
    public String getText() { 
     // getText code for xls files goes here 
    } 
} 

現在定義有一個靜態一個DocExtractorFactory類中創建方法:

public class DocExtractorFactory { 

    public static DocExtractor create(File f) { 
     // create the appropriate DocExtractor derived class based on the file extension 
     String extension = FilenameUtils.getExtension(f.getName()); 
     if (extension.equals("doc") { 
      return new DocExtractorDoc(f); 
     } else if (extension.equals("xls") { 
      return new DocExtractorXls(f); 
     } else { 
      // error handling code here -- perhaps throw an exception 
     } 
    } 
} 

最後,這裏是它使用上述類

// this actually creates a DocExtractorDoc object (but you don't care) 
DocExtractor de1 = DocExtractorFactory.create(new File("myDocFile.doc")); 
// this actually uses DocExtractorDoc.getText (but again you don't care) 
String s1 = de1.getText(); 
// this actually creates a DocExtractorXls object 
DocExtractor de2 = DocExtractorFactory.create(new File("myDocFile.xls")); 
// this actually uses DocExtractorXls.getText 
String s2 = de2.getText(); 

所以一些代碼,我們有什麼基本完成的只有一個地方有if語句,工廠創建方法。您可以根據需要創建儘可能多的DocExtractor派生類,只需編寫該類的代碼並對create方法進行簡單更改即可。

+0

非常清晰且易於遵循的答案。我也會研究這些鏈接。非常感謝。 – grooble

+0

所以,看起來Apache的設置與他們的ExtractorFactory: (http://poi.apache.org/apidocs/org/apache/poi/extractor/ExtractorFactory。html) ......這正是我想要的。儘管如此,這是一次很好的學習經歷。 – grooble

1

你可以抽象的Excel /字/任何文件和gettext的部件的負荷。

創造了這兩種方法公社界面,然後執行爲每個IFS的這兩種方法。

interface Extractor { 
    public void setInputStream(FileInputStream fis); 
    public String getText(); 
} 

實施的Word

class ConcreteWordExtractor implements Extractor { 
    private WordExtractor w; 

    public void setInputStream(FileInputStream fis) { 
     HWPFDocument doc = new HWPFDocument(fis); 
     this.w = new WordExtractor(doc); 
    } 

    public String getText() { 
     String[] texted = this.w.getParagraphText(); 
     // rest of your logic for word 
    } 
} 

實施爲Excel

class ConcreteExcelExtractor implements Extractor { 
    private ExcelExtractor x; 

    public void setInputStream(FileInputStream fis) { 
     // load the Excel workbook from input stream 
     this.x = new ExcelExtractor(...); 
    } 

    public String getText() { 
     // your logic for Excel 
    } 
} 

的文件提取,利用先前實施的

public class DocExtractor { 
    private final Extractor extractor; 
    // you could use spring or any injector to create this and avoit it being in your code 
    private final Map<String, Extractor> extractors = new HashMap<String, Extractor>() {{ 
      put("doc", new ConcreteWordExtractor()); 
      put("xls", new ConcreteExcelExtractor()); 
    }}; 

    public DocExtractor(File f) { 
     String extension = FilenameUtils.getExtension(f.getName()); 
     if (!this.extractors.containsKey(extension)) 
      throw new IllegalArgumentException("No such extractor for extension `" + extension + "`."); 
     this.extractor = this.extractors.get(extension); 
     try { 
      FileInputStream fis = new FileInputStream(f); 
      extractor.setInputStream(fis); 
     } catch (Exception e) { 
      // do what you want 
     } 
    } 

    public String getText() { 
     return extractor.getText(); 
    } 
} 

這樣你抽象的第l爲每種格式和文本檢索部分提供文件,當您需要支持新格式時,您必須實現Extractor接口並將其添加到地圖中,或者如評論中所建議的,您可以使用任何依賴注入庫/框架(如Spring)從代碼中提取。

2

如果您想支持各種Office文件格式的文本提取,那麼最好不要在前面編寫自己的包裝,而應該使用Apache Tika。 Apache Tika是一個文本和元數據提取工具包/庫/ thingy。

要從Microsoft Office文件中提取文本,Tika調用Apache POI以完成實際工作。但是,它可以在內部完成,併爲您隱藏不同格式的複雜性。相反,你所做的只是把它交給一個文件,Tika計算出它是什麼,要調用哪個庫,執行文本提取,並將文本提供給你。

使用Apache Tika時,可以選擇純文本或HTML。假設你想純文本(因爲這是所有低級別POI提取提供),你想要的東西,如:

Tika tika = new Tika(); 
Metadata metadata = new Metadata(); 
metadata.set(Metadata.RESOURCE_NAME_KEY, "myfile.name"); 
String text = tika.parseToString(new File("myfile.name")); 

就是這樣。無論您是否擁有.xls,.ppt或甚至是其中一個many other supported formats,都會收回純文本內容。

+0

我喜歡上面的答案,但我最終可能會與Tika一起進行多功能性。感謝您的高舉。 – grooble