MFC网页分析程序

        **开发一个网页分析程序,可以抓取特定网页的内容,加以分析之后将结果保存至数据库。**

  V1.5上线,保存至sqlite数据库用时2分13秒。         

网页分析程序具体要求描述如下: 1. 使用http技术获取一个博客的首页http://blog.csdn.net/jiangsheng 2. 分析这个网页的内容,从中找到博客中每一篇文章的链接。 3. 通过这些链接,获取文章的正文网页,从内容中提取文章的标题和文章的内容。 4. 将文章的标题与内容分别保存至数据库。 5. 布局要求:提供一个列表框和一个多行文本框。列表框中显示从数据库中获取的文章标题列表;当点击列表框中的某一篇文章时,在文本框中显示该文章的内容。

获取源码

因为需多次调用获取源码功能,将它放入一个函数中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
CString CGetWebDlg::DownloadCodes(CString path)
{
	CInternetSession session;
	CHttpFile *file = NULL;
	CString strURL = path;
	CString strHtml = _T(""); //存放网页数据
	try
	{
		file = (CHttpFile*)session.OpenURL(strURL);
	}
	catch (CInternetException *m_pException)
	{
		file = NULL;
		m_pException->m_dwError;
		m_pException->Delete();
		session.Close();
		MessageBox("网络连接错误!", "提示");
	}
	CString strLine;
	char sRecived[1024];
	if (file != NULL)
	{
		while (file->ReadString((LPTSTR)sRecived, 1024) != NULL)
		{
			strHtml += sRecived;
		}
	}
	else
	{
		AfxMessageBox(_T("失败!"));
	}
	session.Close();
	file->Close();
	delete file;
	file = NULL;

	strHtml = ConvertUtf8ToGBK(strHtml);//源码转换

	return strHtml;
}

编码问题

注意网页编码问题,因此需要格式转换,编写一个函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void  ConvertUtf8ToGBK(CString &strUtf8)
{
          
    int len=MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUtf8, -1, NULL,0);
	unsigned short * wszGBK = new unsigned short[len+1];
	memset(wszGBK, 0, len * 2 + 2);
	MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUtf8, -1, (LPWSTR)wszGBK, len);
 
	len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)wszGBK, -1, NULL, 0, NULL, NULL); 
	char *szGBK=new char[len + 1];
	memset(szGBK, 0, len + 1);
	WideCharToMultiByte (CP_ACP, 0, (LPCWSTR)wszGBK, -1, szGBK, len, NULL,NULL);
 
	strUtf8 = szGBK;
	 delete[] szGBK;
	 delete[] wszGBK;
}

显示源码

有些类似网页爬虫的感觉。

由于我刚开始是用VC6.0创建项目,现在用VS2013打开,因此,提示报错: error MSB8031: Building an MFC project for a non-Unicode character set is deprecated. You must change the project property to Unicode or download an additional library. See http://go.microsoft.com/fwlink/p/?LinkId=286820 for more information.

解决:用于多字节字符编码 (MBCS) 的 MFC 库 (DLL) 不再包含于 Visual Studio 中,但是可用作插件,可以在任何装有 Visual Studio Professional、Visual Studio Premium 或 Visual Studio Ultimate 的计算机上下载和安装。下载地址:https://www.microsoft.com/zh-cn/download/details.aspx?id=40770

解析

为了接下来的操作,我去学正则表达式了…… 还是学习html解析库 htmlcxx 官方 HtmlCxx用户手册

HTMLCXX

下载htmlcxx库

http://sourceforge.net/projects/htmlcxx/ 并解压。

编译

打开htmlcxx.vcproj,右键属性,配置属性-C/C++-代码生成-运行库:多线程调试 DLL (/ MDd)进行编译。编译会报错,将

1
const char *signature = "";  

改为

1
const char *signature = "\xEF\xBB\xBF";  

即可编译成功。

导入

把生成的htmlcxx.lib和html文件夹拷贝到所需的工程中。即: 在所开发项目文件夹中,新建”htmlcxx“文件,里面添加两个子文件夹”lib“和”include“。将编译好的htmlcxx.lib拷贝到lib文件夹,将html文件夹中所有的.h头文件和ParserSax.tcc添加到include文件夹。添加库文件htmlcxx.lib到项目中,具体说来:

在VS工程中,添加c/c++工程中外部头文件及库的基本步骤: 1、添加工程的头文件目录:工程—属性—配置属性—c/c++—常规—附加包含目录:加上头文件存放目录。 2、添加文件引用的lib静态库路径:工程—属性—配置属性—链接器—常规—附加库目录:加上lib文件存放目录。 然后添加工程引用的lib文件名:工程—属性—配置属性—链接器—输入—附加依赖项:加上lib文件名。 3、添加工程引用的dll动态库:把引用的dll放到工程的可执行文件所在的目录下。 注意:第一步可以不用,直接在工程里加入动态库的头文件,在使用代码处引用这个头文件。

所开发的项目的头文件中添加以下内容:

1
2
3
4
5
6
7
#include <string>
#include "htmlcxx/include/ParserDom.h"
 
using namespace std;
using namespace htmlcxx;
 
#pragma comment(lib,"htmlcxx.lib")

测试

官方测试代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 #include <htmlcxx/html/ParserDom.h>
  ...
  
  //Parse some html code
  string html = "<html><body>hey</body></html>";
  HTML::ParserDom parser;
  tree<HTML::Node> dom = parser.parseTree(html);
  
  //Print whole DOM tree
  cout << dom << endl;
  
  //Dump all links in the tree
  tree<HTML::Node>::iterator it = dom.begin();
  tree<HTML::Node>::iterator end = dom.end();
  for (; it != end; ++it)
  {
  	if (it->tagName() == "A")
  	{
  		it->parseAttributes();
  		cout << it->attributes("href");
  	}
  }
  
  //Dump all text of the document
  it = dom.begin();
  end = dom.end();
  for (; it != end; ++it)
  {
  	if ((!it->isTag()) && (!it->isComment()))
  	{
  		cout << it->text();
  	}
  }

然而……

报错1

加入#include"iostream"头文件即可。

报错2

修改为:

1
2
3
4
5
6
if (it->tagName() == "A")
			{
				it->parseAttributes();
				std::pair<bool, std::string> pa = it->attribute("href");
				cout << pa.second;
			}

编译通过。

还有其他的库也可以用,比如使用MSHTML解析HTML页面 比如LIBXML2库使用指南 还可以用正则表达式写库……

突然发现 原来的计划里有COM组件 XML和HTML 数据库访问技术 都没怎么接触过 补补补 在填坑的路上不能止步…

参考

C++ 使用Htmlcxx解析Html内容(VS编译库文件) html与xml解析库htmlcxx使用过程中的若干问题及解决方案 c++ hmtlcxx 学习之旅

MSHTML

因为最近用过MSXML,就试试MSHTML。学有余力的话,htmlcxx之后还是想玩一下… https://msdn.microsoft.com/en-us/library/aa741317(v=vs.85).aspx 蒋晟-关于MSHTML https://msdn.microsoft.com/zh-cn/library/mshtml(v=vs.110).aspx

MSHTML导入

系统中自带了mshtml,和msxml一样,在C盘windows/system32中可找到。

如何导入

先看MSDN…… MSDN-MSHTML

再看各种搜集的文章 http://bbs.csdn.net/topics/330214041 http://www.cnblogs.com/speedmancs/archive/2010/08/11/1797442.html http://blog.csdn.net/jinyaba/article/details/17097323 https://social.msdn.microsoft.com/Search/zh-CN?query=MSHTML&pgArea=header&emptyWatermark=true&ac=4#refinementChanges=117&pageNumber=1&showMore=false https://wenku.baidu.com/view/d571abc4ec3a87c24028c4bb.html http://www.codeguru.com/cpp/i-n/ieprogram/article.php/c4385/Lightweight-HTML-Parsing-Using-MSHTML.htm http://www.yesky.com/403/1938403.shtml?qq-pf-to=pcqq.c2c http://www.bianceng.cn/Programming/vc/201411/46771.htm https://wenku.baidu.com/view/299bba4a336c1eb91a375df5.html

思路:下载源码和获取链接是两个独立函数,会被多次调用。先获取首页源码,div id=“archive_list” 遍历该div获取各月份归档链接,再使用多线程(48个线程???)进入每个归档链接里下载源码,获取源码中h1的每篇文章标题,保存到数据库。

虽然有两种方法,一种通过归档获得链接,一种通过翻页获得链接,但根据本html特点,明显通过翻页要简洁方便一些,因为翻页的链接是有规律的,可通过循环搞定。每页5篇直接获得正文链接,比从归档获得少一层。两核4个逻辑处理器,所以是开2个线程好还是4个好呢……

获取每篇正文链接,下载源码,解析得正文,保存到数据库。最后从数据库中提取标题和正文显示到对应窗口(使用ADO)。

解析过程

创建

1.使用CoCreateInstance创建一个接口

1
	HRESULT hr = CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_IHTMLDocument2, (void**)&pDoc);

2.创建一个COM中的数组,将HTML字符串写到数组中

a)SafeArrayCreateVector:函数用来创建一个对应的数组结构。函数有三个参数,第一个参数表示数组中元素类型,一般给VT_VARIANT表示它是一个自动类型,第二个参数数组元素起始位置的下标,对于VC来说,数组元素总是从0开始,所以这个位置一般给0,第三个参数是数组的维数,在这将它作为一个字符数组,所以是一个一维数组。 b)SafeArrayAccessData:允许用户操作这个数组,在需要读写这个数组时都需要调用这个函数,以便获取这个数组的操作权。它有两个参数,第一个参数是数组变量,第二个参数是一个输出参数,当调用这个函数成功,会提供一个缓冲区,操作这个缓冲区就相当于操作了这个数组。 c)SafeArrayUnaccessData:每当操作数组完成时需要调用这个函数,函数与SafeArrayAccessData配套使用,用来回收这个权限,并使对数组的操作生效。

  1. 调用接口的write方法,将接口与HTML字符串绑定
1
2
3
4
5
6
7
8
	SAFEARRAY* psa = SafeArrayCreateVector(VT_VARIANT, 0, 1);
	VARIANT *param;
	bstr_t bsData = (LPCTSTR)strHtml;
	hr = SafeArrayAccessData(psa, (LPVOID*)&param);
	param->vt = VT_BSTR;
	param->bstrVal = (BSTR)bsData;
	hr = pDoc->write(psa);
	hr = SafeArrayUnaccessData(psa);

目标:

1
2
3
4
5
        <span class="link_title"><a href="/jiangsheng/article/details/9870241">
        选择剪贴板格式顺序            
        </a>
        </span>
 

整个 <span> </span> 是元素, <span> 是标签,class是属性名,link_title是属性值,“选择剪贴板格式顺序”是文本。

元素遍历

至少两种方法: 法一: 获取了HTML文档的IID_IHTMLDocument2接口后,开始遍历: 1.get_all方法获取所有标签节点,这个函数通过一个输出参数输出IHTMLElementCollection类型的接口指针 2.用IHTMLElementCollection接口的get_length方法获取标签的总数量,据此写一个循环,在循环进行元素的遍历 3.循环中用IHTMLElementCollection接口的item方法进行迭代,获取各元素对应的IDispatch接口指针 4.调用IDispatch接口指针的QueryInterface方法生成对应的IHTMLElement接口。通过这个接口获取元素的各种信息

以下已能成功获取标题:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void CGetWebDlg::EnumElements(IHTMLDocument2* pDoc)
{
	CComPtr<IHTMLElementCollection> pCollection;
	pDoc->get_all(&pCollection);
	if (NULL == pCollection)
	{
		return;
	}
	VARIANT varName;
	CString strText;
	long len = 0;
	pCollection->get_length(&len);
	for (int i = 0; i < len; i++)
	{
		varName.vt = VT_I4;
		varName.lVal = i;
		CComPtr<IHTMLElement> pElement;
		CComPtr<IDispatch> pDisp;
		pCollection->item(varName, varName, &pDisp);
		if (NULL == pDisp)
		{
			continue;
		}

		pDisp->QueryInterface(IID_IHTMLElement, (LPVOID*)&pElement);
		if (NULL != pElement)
		{
			BSTR bstrClass;	
			pElement->get_className(&bstrClass);
			CString strClass = _com_util::ConvertBSTRToString(bstrClass);
			if (strClass.Compare("link_title") == 0)
			{
					BSTR bstrText = NULL;
					pElement->get_innerText(&bstrText);
					strText = bstrText;
					m_list.InsertItem(i, strText);
			}			
		}
	}
}

法二: 利用IHTMLDocument2将字符串形式的HTML转换为DOM对象,利用IHTMLDocument3的getElementByTagName等方法来操作DOM对象。 以下已能成功获取标题:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	MSHTML::IHTMLDocument3Ptr pDoc3;
	 	MSHTML::IHTMLElementCollectionPtr pCollection;
	 	MSHTML::IHTMLElementPtr pElement;

	pDoc3 = pDoc;
	pCollection = pDoc3->getElementsByTagName(L"span");
	for (long i = 0; i < pCollection->length; i++)
	{
		pElement = pCollection->item(i, (long)0);
		if (pElement != NULL)
		{
			BSTR bstrClass;
			pElement->get_className(&bstrClass);
			CString strClass = _com_util::ConvertBSTRToString(bstrClass);
			if (strClass.Compare("link_title") == 0)
			{
				BSTR bstrText = NULL;
				pElement->get_innerText(&bstrText);
				CString strText = bstrText;
				m_list.InsertItem(i, strText);
			}
		}
	}

其实两种方法大同小异,相较而言可能数据量大的话,法二效率高些吧。因为法一是直接遍历所有的元素寻找class相同的,而法二是先定位span,然后在span中找寻class。(getElementsByTagName只有IHTMLDocument3Ptr)

使用MSHTML解析HTML页面 变体VARIANT MSDN-DOM https://msdn.microsoft.com/en-us/library/ms766487(v=vs.85).aspx 使用MSHTML接口获取链接

晚上几个小时做完了一半,抵了之前一两个月。 数据库这块没来得及做,没加多线程,很多细节还得调。但比起之前心有余而力不足的感觉还是好多了。 学过msxml后,学习mshtml确实强一点,比一个月前完全不知道怎么下手好很多了。

今晚总算做出来个半成品 半成品

正文解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
void CGetWebDlg::ArticleParse(CString strArticle, Blog* blog, int iRow)
{
	IHTMLDocument2* pDoc;
	MSHTML::IHTMLDocument3Ptr pDoc3;
	MSHTML::IHTMLElementCollectionPtr pCollection;
	MSHTML::IHTMLElementPtr pElement;

	HRESULT hr = CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_IHTMLDocument2, (void**)&pDoc);


	//将代码放入安全数组并写入文档
	SAFEARRAY* psa = SafeArrayCreateVector(VT_VARIANT, 0, 1);
	if (psa == NULL || pDoc == NULL)
	{
		MessageBox(_T("创建Document2对象失败!"));
	}
	VARIANT *param;
	bstr_t bsData = (LPCTSTR)strArticle;
	hr = SafeArrayAccessData(psa, (LPVOID*)&param);
	param->vt = VT_BSTR;
	param->bstrVal = (BSTR)bsData;
	hr = pDoc->write(psa);
	//hr = pDoc->close();

	//SafeArrayDestroy(psa);
	hr = SafeArrayUnaccessData(psa);
	_bstr_t href;
	pDoc3 = pDoc;
	pCollection = pDoc3->getElementsByTagName(L"div");
	for (long k = 0; k < pCollection->length; k++)
	{
		pElement = pCollection->item(k, (long)0);
		if (pElement != NULL)
		{
			BSTR bstrClass;
			pElement->get_id(&bstrClass);
			CString strClass = _com_util::ConvertBSTRToString(bstrClass);
			if (strClass.CompareNoCase("article_content") == 0)
			{
				BSTR bstrText = NULL;
				pElement->get_innerText(&bstrText);
				CString strText = bstrText;
				blog->article = strText;
				m_list.SetItemData(iRow, (DWORD_PTR)blog);							
			}
		}
	}
}

建立了一个结构体用来存放每篇文章的标题和正文,方便点击列表控件项时读取对应的结构体。

1
2
3
4
5
struct Blog
	{
		CString title;
		CString article;
	};

列表控件和编辑框交互

列表控件初始化
1
2
3
4
	CRect rectLocal;
	m_list.GetClientRect(rectLocal); //获得当前客户区信息
	m_list.InsertColumn(0, "序号", LVCFMT_LEFT, rectLocal.Width() / 6, 0);
	m_list.InsertColumn(1, "文章列表", LVCFMT_LEFT, rectLocal.Width() / 6 * 5, 1);

添加多列后,单击只能选中第一列,这时需要修改风格 LVS_EX_FULLROWSELECT 表示整行。

1
	m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT);
添加数据

单纯添加一项的话: m_list.InsertItem(项的索引, 数据); 但要指定列的话: m_list.InsertItem(项的索引, “”); m_list.SetItemText(行, 列, 数据);

OnLvnItemchangedList

试了多种方法,只有这一种成功了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void CGetWebDlg::OnLvnItemchangedList1(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	// TODO:  在此添加控件通知处理程序代码
// 	m_edit.SetWindowText("");
// 	int index = m_list.GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);
// 	Blog *pStructure = (Blog*)m_list.GetItemData(index);
// 	m_edit.SetWindowText(pStructure->article);
	if (pNMLV->uNewState == (LVIS_SELECTED | LVIS_FOCUSED))
	{
		m_edit.SetWindowText("");
		Blog *pStructure = (Blog*)m_list.GetItemData(pNMLV->iItem);
		m_edit.SetWindowText(pStructure->article);
	}
	*pResult = 0;
}

实现列表和编辑框交互后,可以说这已经是一个可以完整运行的程序了,V1.0版本出炉。 实测解析时间:1分41秒 能够成功获取该博客111篇文章,能够正确显示正文(包括清晰显示代码)。现在的运行图: V1.0 该版本未添加多线程、未和数据库关联。

V2.0实现数据库操作

使用sqlite数据库

下载导入

官网下载sqlite Source Code sqlite-amalgamation-3200000.zip 有三个东西 shell.c sqlite3.c sqlite3.h 根据VS2010下SQLite3生成lib库文件 文章方法生成sqlite3.lib

Precompiled Binaries for Windows sqlite-dll-win32-x86-3200000.zip 有一个所需 sqlite3.dll

将lib和dll放入项目工程里,在.cpp开头加上

1
2
#include "sqlite3.h"
#pragma comment(lib,"sqlite3.lib")

保存至sqlite数据库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int CGetWebDlg::Database(std::vector<Blog*> &vecBlog)
{
	sqlite3 * pDB;
	char* errMsg;
	// 连接SQLite数据库
	int res = sqlite3_open("test.db", &pDB);
	if (res != SQLITE_OK)
	{
		MessageBox(_T("数据库打开失败,请检查后再操作!"), NULL, MB_ICONSTOP);
		sqlite3_close(pDB);
		return -1;
	}

	// 创建表
    string strSQL = "create table blog (title text, article text);";
    res = sqlite3_exec(pDB, strSQL.c_str(), 0, 0, &errMsg);
    if (res != SQLITE_OK)
    {
        MessageBox(_T("数据库打开失败,请检查后再操作!"), NULL, MB_ICONSTOP);
        //return -1;
    }
	
		// 插入数据
		vector<Blog*>::iterator iter;
		for (iter = vecBlog.begin(); iter != vecBlog.end(); iter++)
		{
			
			CString cstrTitle = (*iter)->title;
			CString cstrArticle = (*iter)->article;
			cstrTitle = ConvertGBKToUtf8(cstrTitle);
			cstrArticle = ConvertGBKToUtf8(cstrArticle);

			char *p1 = cstrTitle.GetBuffer(cstrTitle.GetLength() + 1);
			cstrTitle.ReleaseBuffer();
			char *p2 = cstrArticle.GetBuffer(cstrArticle.GetLength() + 1);
			cstrArticle.ReleaseBuffer();
			char *strSQL = sqlite3_mprintf("INSERT INTO blog VALUES('%q','%q')", p1, p2);
			sqlite3_exec(pDB, strSQL, 0, 0, &errMsg);
		}
}

当然,也可以事先把数据库和表创建好…… 创建数据库和表

因为sqlite数据库是UTF-8格式存储,于是,需要一个转换函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
CString  ConvertUtf8ToGBK(CString &strUtf8)
{
          
    int len=MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUtf8, -1, NULL,0);
	unsigned short * wszGBK = new unsigned short[len+1];
	memset(wszGBK, 0, len * 2 + 2);
	MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUtf8, -1, (LPWSTR)wszGBK, len);
 
	len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)wszGBK, -1, NULL, 0, NULL, NULL); 
	char *szGBK=new char[len + 1];
	memset(szGBK, 0, len + 1);
	WideCharToMultiByte (CP_ACP, 0, (LPCWSTR)wszGBK, -1, szGBK, len, NULL,NULL);
 
	strUtf8 = szGBK;
	 delete[] szGBK;
	 delete[] wszGBK;
	 return strUtf8;
}

出现的问题

  1. 如图,数据少时并不会出现。 Q1 Q2

2.好像是运行了脚本 Q3 据查将“script”修改成别的字符便不会运行脚本,并没尝试成功。

效果

下载了一个SQLiteStudio可视化管理工具。可见数据已成功保存至数据库。 sqlite

从sqlite数据库读取数据

新建工程。 主要用到该函数:

1
2
3
4
5
6
7
8
9
int sqlite3_get_table(
  sqlite3 *db,          /* An open database */
  const char *zSql,     /* SQL to be evaluated */
  char ***pazResult,    /* Results of the query */
  int *pnRow,           /* Number of result rows written here */
  int *pnColumn,        /* Number of result columns written here */
  char **pzErrmsg       /* Error msg written here */
);
void sqlite3_free_table(char **result);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void CGetDataFromSqliteDlg::OnBnClickedBtnreaddata()
{
	// TODO:  在此添加控件通知处理程序代码
	sqlite3 * pDB;
	char* errMsg;
	// 连接SQLite数据库
	int res = sqlite3_open("test.db", &pDB);
	if (res != SQLITE_OK){
		MessageBox(_T("数据库打开失败,请检查后再操作!"), NULL, MB_ICONSTOP);
		sqlite3_close(pDB);
	}
	
	string strSql = "select * from blog";
	char** pResult;
	int nRow;
	int nCol;
	int nResult;
	nResult = sqlite3_get_table(pDB, strSql.c_str(), &pResult, &nRow, &nCol, &errMsg);
	if (nResult != SQLITE_OK)
	{
		sqlite3_close(pDB);
		cout << errMsg << endl;
		sqlite3_free(errMsg);
	}

	std::vector<Blog*>vecBlog;

	string strOut;
	int nIndex = 2;
	for (int i = 0; i < nRow; i++)
	{
		Blog* blog = new Blog;
		string tempTitle = pResult[nIndex];
		string tempArticle = pResult[nIndex + 1];
		CString number;
		number.Format("%d", i + 1);

		m_list.InsertItem(nRow, "");
		CString strTitle(tempTitle.c_str());
		CString strArticle(tempArticle.c_str());
		ConvertUtf8ToGBK(strTitle);
		ConvertUtf8ToGBK(strArticle);
		blog->title = strTitle;
		blog->article = strArticle;
		vecBlog.push_back(blog);
		m_list.SetItemText(i, 0, number);
		m_list.SetItemText(i, 1, strTitle);
		m_list.SetItemData(i, (DWORD_PTR)blog);

		nIndex = nIndex + 2;
	}
	sqlite3_free_table(pResult);
	sqlite3_close(pDB);
}

同样地,注意格式转码。

成品

V1.5

V1.5成功实现从sqlite数据库读取数据并反映在列表和编辑框中。

To be continued…

Licensed under CC BY-NC-SA 4.0
最后更新于 0001-01-01 00:00 UTC
使用 Hugo 构建
主题 StackJimmy 设计