一直采用C++作为主语言开发,最近遇到一个项目需要解析PDF文件中的文本内容,直接采用C++来做显得不是很方便,但用python来做就显得很简单了。
难点在于如何C++语言环境下调用python来实现功能。
Python版本为: 3.7.3(32 bit)版本。
QT版本为:Qt 5.12.2(MSVC 2017, 32bit)
QtCreator中项目的配置如下:
重要的事情(版本匹配):
请确保Python版本与QT版本的位数相一致,要么同为32bit,要么同为64bit。否则可能会出现代码能编译,但是debug的时候,会出现the cdb process terminated之类莫名其妙的错误。
这里确实是一个坑,最开始本人安装的是64bit版本的python,程序也选的是64bit编译器和64bitdebugger。
但由于QTcreator本身的版本(Based on Qt 5.12.2 (MSVC 2017, 32 bit))就是32bit的,就导致了莫名的错误。后来我都以QTcreator本身的版本为基准,全部换成32bit的,问题就解决了。
方便起见,本文将python 3.7的安装目录中的几个必要文件夹(文件)直接拷贝到工程目录下,方便使用相对路径,也方便后续直接制作安装包。
当然,采用绝对路径也是香的,看个人喜好吧。
在pro文件中:
添加
INCLUDEPATH += "./include"
LIBS += -L./libs -l_tkinter -lpython3 -lpython37
#消除中文乱码问题
msvc:QMAKE_CXXFLAGS += -execution-charset:utf-8
msvc:QMAKE_CXXFLAGS += -source-charset:utf-8
显然这里是使用了相对路径的方式。
#include "Python.h"
不出意外的话,这时候编译会出错!”error: C2238: 意外的标记位于“;”之前“.....
此外,还有 error: LNK1104: 无法打开文件“python36_d.lib”,甚至还有其他的...
莫慌,这里有一个很好的解决办法:
点这里错误解决,详细!
创建一个python文件,放在当前工程exe文件的同级目录下(又是绝对和相对路径的小问题),只要让你的程序能找的到就行,其余随意!
parsepdf.py:
# -*- coding: utf-8 -*-
def readPDF(pdfpath):
import pdfplumber
import re
pdf = pdfplumber.open(pdfpath)
result=''
for page in pdf.pages:
result+= page.extract_text() + "\n\n"#页与页之间靠"\n\n"做间隔.
pdf.close()
return (result)
在C++中调用:
/*
*pdfpathName: 所要解析的PDF的绝对路径,同时也是传给python脚本的参数(注意如何传参)
*/
int MainWindow::PDFParserAndGenerate(QString pdfpathName)
{
int ret = 0;
do
{
//尝试加载python模块
Py_SetPythonHome((const wchar_t *)(L"./Python37"));//发布时候加上这一句
Py_Initialize();//初始化py模块
if ( !Py_IsInitialized() )
{
ret = -1;
break;
}
QString dirpath = QApplication::applicationDirPath();
dirpath = QDir::toNativeSeparators(dirpath);
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");//当前路径
PyObject* pModule = PyImport_ImportModule("parsepdf");// parsepdf.py
if (!pModule)
{
qDebug()<< "Cant open python file!\n";
ret = -2;
break;
}
PyObject* pFunparse= PyObject_GetAttrString(pModule,"readPDF"); // 这里的readPDF就是python文件定义的函数
if(!pFunparse)
{
qDebug()<<"Get function readPDF failed";
ret = -3;
break;
}
// 创建元组设置参数
QTextCodec *tc = QTextCodec ::codecForName("UTF-8");
QByteArray ba = pdfpathName.toUtf8();
char *path = ba.data();
PyObject* arg1 = PyUnicode_FromString(path);
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, arg1);
PyObject* fshowc = PyObject_CallObject(pFunparse,args);//调用main函数
//PDFReportInfo reportInfo;
if (!fshowc)
{
qDebug()<<"PyObject_CallObject error";
ret = -4;
break;
}
QString resultPDF = PyUnicode_AsUTF8(fshowc);/*整份PDF文档解释出来的内容 */
QStringList pages =resultPDF.split("\n\n");/*分割出每页内容 */
QVector lines;
for (int i = 0; i < pages.size(); ++i)//遍历每一页
{
QStringList listContent =pages.at(i).split("\n");/*分割单页内容 */
for (QString s : listContent)
lines.append(s);//每一行
}
// ToDo ....这里加上自己的业务代码....
}
while(0);
//好同志总是在最后不忘释放资源!
if ( Py_IsInitialized() )
{
Py_Finalize();
}
return ret;
}
编译调试完毕之后,就要打包发布出去,搞到其他电脑上运行了。
注意发布包内的内容。Python37文件夹就是 “3 动手建立工程!”小节一上来就提到的部分。
保险起见,下图中的python3.dll python37.dll 和 vcruntime140.dll是重复放进来了(Python37文件夹)中也有。可能不重复也可以,但没去尝试。