利用Minidx Extract-Text Com组件从Word,Xls,Pdf……等文件中读取 后附C# 调用代码

不少人对Google,Baidu等搜索引擎可以“找到”你放在服务器上的Word的Doc,Excel的xls以及Pdf等各种文件而感到惊叹不已,也有不少人发来邮件询问我 Minidx文件管理器中从各种格式的文件中读取文本内容是如何实现的。Linux平台实现起来比较复杂一些,不过对于Windows用户来说,其实利用微软 Ifilter的Indexing service接口,可以比较容易的实现上面的功能。Minidx支持200多种文件格式,其实也是利用了 Ifilter的接口。实现的基本原理,就是写一个Com组件,去查找系统中相应文件格式的API接口所在的Dll路径,然后调用抽取文本。

在上面的微软Ifilter中有关于IFilter的基本概念的介绍,这里不再重复,另外在codeproject的Using IFilter in C#中,可以找到C#的实现并且可以下载到源代码。因为性能上的考虑,Minidx文件管理器的这一部分完全利用C++实现,并封装为Com组件,下面主要是对如何通过调用这一Com组件在自己的程序中实现Doc,Xls,Pdf,msg等等文本阅读等功能作一些说明。

●Demo压缩包构造(压缩包可以从这里下载

demo_vb.zip———–demo_vb.sln   整个解决方案(solution)文件

                     |———demo_vb.suo 用户配置文件

          |———demo_vb—-bin——–Debug—-demo_vb.exe   Debug版本目标文件

                                                        |———Release—demo_vb.exe  Release版本目标文件

                                                |—My Project(忽略)

                                                |—obj                       编译生成的临时文件

                                                |—demo_vb.vbproj      工程文件

                                                |—ExtractText.dll         文本抽取Com组件

                                                |—Form1.Designer.vb   Demo的GUI文件

                                                |—Form1.resx                资源文件

                                                |—Form1.vb                   Demo的源代码文件

                                                |—run.bat                      Com组件注册命令

●执行Demo

①、双击run.bat执行,注册Com组件

register

②、双击demo_vb\bin\Release或者demo_vb\bin\Debug目录下的demo_vb.exe

demo-run

③、点“File”,选择对象文件

select-english-file

④、选中文件,查看抽取文本结果。(下面分别是中日英Word的抽取结果)

result-engilsh select-Japanese-file select-Chinese-file

注意:抽取文本对象文件需要有读写权限,正在编辑中的文件抽取文本时可能会出错。

●Demo代码说明

首先当然需要导入Dll组件,下面的代码很简单,也都有注释,应该不难理解,这里不再累述,有什么不明白或者有什么问题的话,可以提出来一起探讨,:)

   1:  '-----------------------------
   2:  ' desc: Extract text from selected file
   3:  '-----------------------------
   4:  Private Sub SelectFileDialog_FileOk(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles SelectFileDialog.FileOk
   5:          On Error GoTo ErrH
   6:   
   7:          ' get selected full-path file name
   8:          Dim sFile As String = SelectFileDialog.FileName
   9:   
  10:          ' set file name
  11:          txtFilePath.Text = sFile
  12:   
  13:          ' set ExtractText module
  14:          Dim te As ExtractTextLib.TextExtractor = New ExtractTextLib.TextExtractor
  15:          Dim sText As String
  16:          sText = te.ExtractText(sFile, MAX_EXTRACT_TEXT_SIZE)
  17:          Err.Clear()
  18:   
  19:          'Me.FileLength.Text = Len(sText).ToString()
  20:          Me.ExtractText.Text = sText
  21:   
  22:          Exit Sub
  23:  ErrH:
  24:          MsgBox("Error extracting text from '" & sFile & "' Err=" & Err.Number & "-" & Err.Description & " in " & Err.Source)
  25:      End Sub

工程的DLL和Demo源代码从这里下载,C/C++调用的Demo还在制作中,有兴趣的高手们也可以制作Php,Delphi,C#等各种语言调用的Demo以供分享~~,实际上原理是一样的。有什么问题可以直接在这里留言或者在Minidx帮助论坛发贴寻找帮助。该模块可用于任何商业和非商业的用途,如果你愿意的话,可以发一个邮件给我告诉我这一模块被用在了你的项目中,那么当你取得成功的时候,我也可以向我的朋友们吹嘘一下,当然这不是必需的,:)

附C# 测试代码例子很简单一看就会

同上首先需要注册 regsvr32.exe 注册 ExtractText.dll

并从项目中引入该dll

核心代码如下:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog op = new OpenFileDialog();
            if (op.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
            {
                this.textBox2.Text = op.FileName;
            }
        }

       
        private void button2_Click(object sender, EventArgs e)
        {
            ExtractTextLib.TextExtractor extractor = new ExtractTextLib.TextExtractor();
            this.textBox1.Text=extractor.ExtractText(this.textBox2.Text,(int)new System.IO.FileInfo(this.textBox2.Text).Length);
        }
    }

测试中发现对于pdf处理需要下载ifilter 相对于pdf 的关联 才能识别本地的adobe

ifilter pdf 下载地址

http://download.csdn.net/detail/wxw6782829/4107606

处理这么大的文件,需要考虑到内存的限制,因此我们可以采用“外排序”的思路,即将文件分割成多个小文件,对每个小文件进行排序,最终将所有排序好的小文件合并成一个大文件。 以下是一个参考实现: ```c++ #include <iostream> #include <fstream> #include <algorithm> #include <vector> #include <queue> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1e7 + 5; // 每个小文件最多存储的数据量 const int MAXM = 1e6 + 5; // 外排的缓冲区大小 int N; // 数据总量 char inputPath[100], outputPath[100]; // 读取一个小文件并将其的数据排序 void sortFile(int fileIndex) { char inputName[100], outputName[100]; sprintf(inputName, "tmp%d.txt", fileIndex); sprintf(outputName, "tmp%d_sorted.txt", fileIndex); ifstream fin(inputName); vector<int> v; while (!fin.eof()) { int x; fin >> x; // 读入一个数 v.push_back(x); if (v.size() == MAXN) { sort(v.begin(), v.end()); // 对该小文件的数据进行排序 ofstream fout(outputName, ios::app); // 将排序好的结果写入新文件 for (int i = 0; i < v.size(); ++i) { fout << v[i] << endl; } fout.close(); v.clear(); } } if (!v.empty()) { sort(v.begin(), v.end()); // 对该小文件的数据进行排序 ofstream fout(outputName, ios::app); // 将排序好的结果写入新文件 for (int i = 0; i < v.size(); ++i) { fout << v[i] << endl; } fout.close(); v.clear(); } fin.close(); } // 从多个小文件选择最小的数 int getMin(vector<ifstream*>& fin, vector<int>& curVal) { int minVal = INT_MAX, minIdx = -1; for (int i = 0; i < fin.size(); ++i) { if (!fin[i]->eof() && curVal[i] < minVal) { minVal = curVal[i]; minIdx = i; } } return minIdx; } // 将所有排序好的小文件合并成一个大文件 void mergeFiles() { vector<ifstream*> fin; vector<int> curVal; priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // 小根堆维护每个文件当前最小的数 // 打开所有排序好的小文件 for (int i = 0; i < N / MAXN + 1; ++i) { char fileName[100]; sprintf(fileName, "tmp%d_sorted.txt", i); fin.push_back(new ifstream(fileName)); curVal.push_back(-1); } // 初始化小根堆 for (int i = 0; i < fin.size(); ++i) { if (!fin[i]->eof()) { int x; *fin[i] >> x; curVal[i] = x; pq.push(make_pair(x, i)); } } // 逐个输出最小值,并将对应文件的下一个数插入小根堆 ofstream fout(outputPath); while (!pq.empty()) { pair<int, int> p = pq.top(); pq.pop(); fout << p.first << endl; int fileIdx = p.second; if (!fin[fileIdx]->eof()) { int x; *fin[fileIdx] >> x; curVal[fileIdx] = x; pq.push(make_pair(x, fileIdx)); } } // 关闭所有打开的文件 for (int i = 0; i < fin.size(); ++i) { fin[i]->close(); } } int main() { cout << "请输入文件路径:" << endl; cin >> inputPath; cout << "请输入输出文件路径:" << endl; cin >> outputPath; // 统计数据总量 ifstream fin(inputPath); int x; while (!fin.eof()) { fin >> x; ++N; } fin.close(); // 将大文件分割成多个小文件 ifstream fin2(inputPath); vector<int> v; int fileIndex = 0; while (!fin2.eof()) { fin2 >> x; v.push_back(x); if (v.size() == MAXN) { sort(v.begin(), v.end()); // 对该小文件的数据进行排序 char fileName[100]; sprintf(fileName, "tmp%d.txt", fileIndex++); ofstream fout(fileName); for (int i = 0; i < v.size(); ++i) { fout << v[i] << endl; // 将排序好的结果写入新文件 } fout.close(); v.clear(); } } if (!v.empty()) { sort(v.begin(), v.end()); // 对该小文件的数据进行排序 char fileName[100]; sprintf(fileName, "tmp%d.txt", fileIndex++); ofstream fout(fileName); for (int i = 0; i < v.size(); ++i) { fout << v[i] << endl; // 将排序好的结果写入新文件 } fout.close(); v.clear(); } fin2.close(); // 对每个小文件进行排序 for (int i = 0; i < N / MAXN + 1; ++i) { sortFile(i); } // 将所有排序好的小文件合并成一个大文件 mergeFiles(); cout << "排序完成!" << endl; return 0; } ``` 注:以上代码仅供参考,具体实现可能需要根据实际情况进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值