Jan 10

folder-find-48x48利用Minidx Extract-Text Com组件从Word,Xls,Pdf……等文件中读取文本内容》中具体的说明了Vb.Net中调用Minidx Extract-Text Com组件对Word,Excel,Pdf等各种文件进行文本抽取的用法。结果很多人都发邮件过来询问C++中如何调用(一些邮件会被Gmail判断为垃圾邮件……强烈建议有问题直接在本文后面留言或在这里提问,这样也可以减轻一点我的工作量,不必挨个回复)。抽空作了一个VC的Demo,工程用VS2005创建的,Unicode版本。下面对Demo稍微做一些说明,一些基本原理直接参照《利用Minidx Extract-Text Com组件从Word,Xls,Pdf……等文件中读取文本内容》就可以了,这里不再重复。


●Demo(VC++)源代码从这里下载(相关文档资料分类中的“Doc,Xls,Pdf等文件中抽取文本的Com组件及Demo(VC++)源代码”)

●执行Demo

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

DllRegisterServer

②、双击demo_vc\release或者demo_vc\debug目录下的demo_vc.exe

demo-Vc

③、点“File”,选择对象文件(ex: 从demo_vc\test-data中选择)

select-File

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

Chinese Japanese

English

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

●实际调用方法:

①、复制下面三个文件到自己工程的相应目录下

・ExtractText.dll
・ExtractText.h
・ExtractText_i.c

②、在需要的文件中用下面的代码引入

#include “ExtractText.h”
#include “ExtractText_i.c”

③、抽取文本部分代码:

   1:  ITextExtractor *te = NULL;
   2:  // Declare and HRESULT and a pointer to the Simple_ATL interface
   3:  HRESULT            hr;
   4:   
   5:  // Now we will intilize COM
   6:  hr = CoInitialize(0);
   7:  BSTR fileName = ::SysAllocString(strFileName);
   8:  if(SUCCEEDED(hr))
   9:  {
  10:  hr = CoCreateInstance( CLSID_TextExtractor, NULL, CLSCTX_INPROC_SERVER,
  11:  IID_ITextExtractor, (void**) &te);
  12:   
  13:  hr = te->ExtractText(fileName, (long)lFileSize, &cval);
  14:  if( cval ) txt_extract_text.SetWindowText(cval);
  15:              
  16:  hr = te->Release();
  17:  }
  18:  // Uninitialize COM
  19:  CoUninitialize();

当然,也可以通过GetProcAddress的方式导入函数,函数的接口请参照ExtractText.h,LoadLibrary方式的使用方法也很简单,有兴趣的可以自己做着试试看,这里不再重复说明了。同样的,有什么问题可以直接在这里留言或者在Minidx帮助论坛发贴寻找帮助。该模块可用于任何商业和非商业的用途,如果你愿意的话,可以发一个邮件给我告诉我这一模块被用在了你的项目中,那么当你取得成功的时候,我也可以向我的朋友们吹嘘一下,当然这不是必需的,:)

written by Minidxer  |  tags: , , , , , , , , , ,

Related Post

24 Responses to “利用Minidx Extract-Text Com组件从doc,Xls,Pdf……等读取文本内容VC Demo”

  1. sofish Says:

    有时候这里访问变得很慢…

  2. Minidxer Says:

    @sofish
    国内的国际带宽限制吧。
    我这里一直都是很快的……非常快

  3. !CnSoLoer Says:

    绝对的高手,因为我一点都看不懂,啊哈哈,服务器跟人都在日本?

  4. Minidxer Says:

    @!CnSoLoer
    失败……绝对的失败,呵呵
    服务器在美国,Godaddy的,开始的时候拜托朋友们测试过一些国家和地区的访问速度,都说速度很快……
    人在东京

    @sofish
    你那里每次访问这里速度都很慢吗?

  5. sorryle Says:

    我国内网通的,一直都很快。

  6. !CnSoLoer Says:

    东京好啊,听同学说日本人从来不穿衣服的,因为丫长这么大还没有见过穿衣服的日本人,啊哈哈

  7. Minidxer Says:

    @sorryle
    网通的国际出口好像比较快,我访问网通的站点速度也不错.

    @!CnSoLoer
    从来不穿衣服…美女的话当然没问题,dafeipo或者laotaitai,那可让人受不了……

  8. Qipeng Tian Says:

    不晓得windows 2003是否支持,我在xp上测试没问题,但是在windows 2003上开发时,报以下错误

    未处理 System.Runtime.InteropServices.COMException
    Message=”检索 COM 类工厂中 CLSID 为 {A2083621-FD63-4F37-ABF3-A4A4FEB0EFD4} 的组件时失败,原因是出现以下错误: 80040154。”
    Source=”TQP.ExtractText”
    ErrorCode=-2147221164
    StackTrace:
    在 TQPExtractText.MyIFilter..ctor()
    在 DataDig.MainFrm.运行ToolStripMenuItem1_Click(Object sender, EventArgs e) 位置 D:\Mike的程序\数据挖掘\DataDig\DataDig\MainFrm.cs:行号 122
    在 System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
    在 System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e)
    在 System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
    在 System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
    在 System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
    在 System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
    在 System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
    在 System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea)
    在 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
    在 System.Windows.Forms.Control.WndProc(Message& m)
    在 System.Windows.Forms.ScrollableControl.WndProc(Message& m)
    在 System.Windows.Forms.ToolStrip.WndProc(Message& m)
    在 System.Windows.Forms.ToolStripDropDown.WndProc(Message& m)
    在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
    在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
    在 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
    在 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
    在 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
    在 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
    在 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
    在 System.Windows.Forms.Application.Run(Form mainForm)
    在 DataDig.Program.Main() 位置 D:\Mike的程序\数据挖掘\DataDig\DataDig\Program.cs:行号 17
    在 System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
    在 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
    在 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
    在 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
    在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    在 System.Threading.ThreadHelper.ThreadStart()

  9. Minidxer Says:

    @Qipeng Tian
    看错误信息应该是找不到这个Com组件,上面的第一步注册时是否正常?

  10. pp Says:

    正在生成代码…
    正在编译…
    ExtractText_i.c
    e:\lab\\ExtractText_i.c(66) : warning C4005: “MIDL_DEFINE_GUID” : 宏重定义
    e:\lab\ExtractText_i.c(43) : 参见“MIDL_DEFINE_GUID”的前一个定义
    e:\lab\ExtractText_i.c(83) : fatal error C1010: 在查找预编译头指令时遇到意外的文件结尾
    在vc2005上测试没有问题,在2003上却冒出这个问题。
    不知道哪里没设置好。包含”stdfax.h”也不行。

  11. Minidxer Says:

    这个错误一般就是没有包含#include “stdafx.h”吧。
    cpp文件中都已经 #include “stdafx.h” 还是一样的错误的话,那在 ExtractText_i.c 中也加入试试看。

  12. Minidxer Says:

    还有一个方法是
    工程的 “创建\使用预编译头”属性改为“不使用预编译头”

  13. pp Says:

    谢谢,vc2003中加入stdafx.h后,ExtractText_i.c(23):fatal error C1853: “Debug/desktop-search4w.pch”预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
    是不是vs2003中少了什么设置,ExtarctTetx_i.c这个文件在vs2005中貌似不参加编译的,因为build中没有complie选项,但是在vs2003中却参加了编译。不知是怎么回事。

  14. Minidxer Says:

    “不使用预编译头”的设置呢?也不可行吗?
    这个其实纯粹是编译器上的区别,和Com组件应该没有关系的……
    还是不行的话过两天我抽空做一个VS2003的DEMO放出来吧。

  15. Minidxer Says:

    VS2003的工程请参照:
    http://blog.minidx.com/2008/03/29/655.html

  16. pp Says:

    我在程序中使用抽取文本的com组件在函数中能能够能够使用,因为要多次调用现在我想在一个类中封装使用以减少初始化的开销
    ITextExtractor *te = NULL;
    HRESULT hr;
    hr = CoInitialize(0);

    if(SUCCEEDED(hr))
    {
    hr = CoCreateInstance( CLSID_TextExtractor, NULL, CLSCTX_INPROC_SERVER, IID_ITextExtractor, (void**) &te);
    }
    被封装进类的构造函数中,hr,te为成员变量
    CoUninitialize();
    hr = te->Release();
    被封装进析构函数中。
    但是我在其它成员函数中调用 hr = te->ExtractText(fileName, (long)lFileSize, &cval);失败,调试的时候te指向一个地址。但是抽取
    不了内容,不知为何。
    我猜想是不是CoCreateInstance()调用后,te指向的地址是不是变动了,是不是需用重新使用什么queryinterface()函数?还望指教?谢谢!

  17. Minidxer Says:

    是不是你的封装类中stack和heap问题?
    方便的话把你写的这个部分代码让我看看

  18. pp Says:

    CScan::CScan(void)
    {
    m_pTE = NULL;
    m_hr = CoInitialize(0);

    if(SUCCEEDED(m_hr))
    {
    m_hr = CoCreateInstance( CLSID_TextExtractor, NULL, CLSCTX_INPROC_SERVER,IID_ITextExtractor, (void**) &m_pTE);
    }
    }
    CScan::~CScan(void)
    {
    m_hr =m_pTE->Release();
    // Uninitialize COM
    CoUninitialize();
    }
    提取文本的成员函数:
    bool CScan::extractText(const tchar* path,char** content)
    {
    CString strFileName(path);

    BSTR fileName = strFileName.AllocSysString();
    BSTR cval = NULL; // extracted text
    // Now we will intilize COM
    m_hr = m_pTE->ExtractText(fileName, (long)1000, &cval);
    if (cval)
    {
    *content = _com_util::ConvertBSTRToString(cval);
    SysFreeString(cval);
    return true;
    }
    else return false;
    }
    调试到m_hr = m_pTE->ExtractText(fileName, (long)1000, &cval)时
    cval是空指针。

  19. Minidxer Says:

    问题应该出在CoCreateInstance上吧,CoCreateInstance是系统只保存一个COM实例的时候使用的,并且Com组建只要初始化一次就可以了。创建多个的应该是CoGetClassObject,或者你可以用单一模式/单态模式(Singleton)实现试试看

    上面的分析我没有动手实际确认过,所以期待你的反馈。有时间的话我会做一个封装类的Sample发布。

  20. pp Says:

    强烈赞扬老大这种精神。
    我就是用的单件模式实现cscan类的。实例通过静态public函数返回
    static CScan* getInstance()
    {
    static CScan INSTANCE;
    return &INSTANCE;
    }
    现在就是说:
    bool CScan::extractText(const tchar* path,char** content)
    {
    CString strFileName(path);
    BSTR fileName = strFileName.AllocSysString();
    BSTR cval = NULL; // extracted text

    m_pTE = NULL;
    m_hr = CoInitialize(0);
    if(SUCCEEDED(m_hr))
    {
    m_hr = CoCreateInstance( CLSID_TextExtractor, NULL, CLSCTX_INPROC_SERVER, IID_ITextExtractor, (void**) &m_pTE);
    }
    m_hr = m_pTE->ExtractText(fileName, (long)1000, &cval);

    m_hr =m_pTE->Release();
    // Uninitialize COM
    CoUninitialize();
    if (cval)
    {
    *content = _com_util::ConvertBSTRToString(cval);
    SysFreeString(cval);
    return true;
    }
    else return false;
    }
    这样是可以成功的,但是由于扫描调用函数太频繁。由于com对象不断创建释放。慢的难以忍受。
    所以想一次初始化,重复使用。从道理上讲是可以的,因为dll已经载入到进程内。但是简单把初始话封装到构造和析构函数行不通。问题肯定是出在cocreateInstance()函数身上。可能就是每次调用函数结束,cocreateInstance()返回的函数指针指向的空间被释放了?也就是上述中m_pTE指向的地址被释放了?
    不知道有没有其他的api防止这一点。
    还请指教。

  21. Minidxer Says:

    @pp
    参考下面的文章吧:

    http://blog.minidx.com/2008/04/13/712.html
    重复使用并没有问题.

  22. Zuckle8181 Says:

    我使用它,对eml中的文本信息进行提取,中文效果很好,可是日文,却显示乱码,请问怎么能使日文正常显示

  23. Minidxer Says:

    @Zuckle8181
    显示的时候你是用什么编码显示的?
    抽取出来的文本是Unicode编码的,ASCII模式下的话将会是乱码

Trackbacks

Leave a Reply