我希望在我的程序中实现apache-poi的文本提取以及稍后的摘要信息功能。也就是说,poi为.doc,docx,.xls和.xlsx文件使用不同的库。
为了隐藏调用poi的函数的这种复杂性,我创建了以下类和方法,我可以调用它来从4种doc类型中的任何一种中提取文本:
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'。 我不禁想到必须有一种更好,完全不同的做法,或者是一些多态的诡计...
这些私有变量是以前在这个类中的尝试遗留下来的,所以请随意将它们扔到你建议的任何soln中。
由于
答案 0 :(得分: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的作者显然需要有两种截然不同的实现,具体取决于文件类型。
他们是如何在没有大量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语句放在一个地方,即工厂创建方法。显然,您可以通过简单地编写类的代码并对create方法进行简单更改来创建尽可能多的DocExtractor派生类。
答案 1 :(得分:2)
如果您想支持各种Office文件格式的文本提取,那么最好的办法是使用Apache Tika,而不是在前面编写自己的包装器。 Apache Tika是一个文本和元数据提取工具包/ library / 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中的一个,你都会得到纯文本内容。
答案 2 :(得分:1)
您可以抽象加载excel / word /任何文件和getText部分。
为这两种方法创建一个commune接口,然后为每个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
}
}
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)从代码中提取它。