1

私は apache-poi のテキスト抽出を実装し、後で要約情報機能をプログラムに実装しようとしています。そうは言っても、poi は .doc、docx、.xls、および .xlsx ファイルに異なるライブラリを使用します。

この複雑さを 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 つの「if」

そして方法:

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

...さらに「if」

これは機能し、実装を非表示にします。

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

しかし、私はすべての「if」が嫌いです。これを行うためのより良い、完全に異なる方法、またはおそらくいくつかのポリモーフィックなトリックがあるに違いないと私は思わずにはいられません...

これらのプライベート変数は、このクラスでの以前の試みから残っているようなものなので、提案したソルンに自由に放り込んでください。

ありがとう

4

3 に答える 3

2

さまざまな Office ファイル形式からのテキスト抽出をサポートしたい場合は、独自のラッパーを前もって作成するのではなく、Apache Tikaを使用することをお勧めします。Apache Tika は、テキストおよびメタデータ抽出ツールキット / ライブラリ / モノです。

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、またはサポートされている他の多くの形式の1 つであっても、プレーン テキスト コンテンツを取得できます。

于 2012-06-10T20:52:35.647 に答える
2

これは、非常に一般的な Java プログラミングの問題の例です。これは通常、Java Factory Design Pattern と呼ばれるものを使用して解決されます。次のリンクには、Factory パターンの簡単な説明があります - http://www.allapplabs.com/java_design_patterns/factory_pattern.htm

他にも参考になるデザインパターンがたくさんあります。それらについて読むと、多くの Java プログラマーが一般的に発生する問題をどのように解決しているかについての洞察が得られます。同じ著者が、 http://www.allapplabs.com/java_design_patterns/java_design_patterns.htmで、一般的な設計パターンのほとんどを説明しています。

さて、あなたの特定の問題について。まず、POI 作成者は Factory デザイン パターンを使用します。たとえば、次のコードを見てください。

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 ファイルのどちらを処理しているかに関係なく、同じプロパティとメソッドを使用して同じ Workbook オブジェクトを処理します。ただし、POI の作成者は、ファイルの種類に応じて 2 つの非常に異なる実装を用意する必要があることは明らかです。

コードの内容など、多数の 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 ファイル用の 2 つのクラスを定義します。

// 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
    }
}

単一の静的 create メソッドを使用して 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 ステートメントを 1 か所 (factory create メソッド) に配置しただけです。また、クラスのコードを記述し、create メソッドを簡単に変更するだけで、必要な数の DocExtractor 派生クラスを作成できることは明らかです。

于 2012-06-09T14:53:36.687 に答える
1

Excel/Word/whatever ファイルと getText パーツの読み込みを抽象化できます。

これらのメソッドの両方に対してコミューン インターフェイスを作成し、次に各 if に対してこれら 2 つのメソッドを実装します。

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
    }
}

以前の実装を利用する Doc Extractor

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();
    }
}

このようにして、各形式のファイルの読み込みとテキスト取得部分を抽象化します。新しい形式をサポートする必要がある場合は、Extractorインターフェイスを実装してマップに追加するか、コメントで提案されているように、 Spring などの依存性注入ライブラリ/フレームワークを使用して、コードからそれを抽出します。

于 2012-06-08T07:18:44.943 に答える