SQLite官方下载页只提供SQLite3.def和SQlite3.dll文件的下载,若使用VC++编程的话,还需要SQLite3.lib库文件,才能调用编译成功。我们可以使用 Visual C++ 提供的 X:\Program Files\Microsoft Visual Studio 11.0\VC\bin\lib.exe 程序生成 SQLite3.lib 库文件。

官方下载地址:http://www.sqlite.org/download.html

一、下载 SQLite3.def和SQlite3.dll 文件,并解压到如 D:\SQLite3\ 目录下。

二、运行 CMD, 输入:
"D:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\lib.exe" /MACHINE:IX86 /DEF:D:\SQLite3\SQLite3.def /OUT:D:\SQLite3\SQLite3.lib
如下图所示:

三、回车后,成功生成 SQLite3.lib 和 SQLite3.exp 两个文件。如下图所示:

VS2015中配置使用SQLite3

下载SQLite源文件

  1. 新建Win32空项目。
  2. 项目属性
    a) 添加包含目录,即刚才下载解压后sqlite3.h所在路径。
    b) 添加库目录,即添加SQLite3.lib所在文件路径。

c) 链接器-输入-附加依赖项,输入SQLite3.lib。

注:SQLite3路径下文件如下图:

3.添加源文件,输入如下:

#include <stdio.h>
#include "sqlite3.h"

int main(int argc, char* argv[])
{
    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;

    rc = sqlite3_open("test.db", &db);

    if (rc) {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));

    }
    else {
        fprintf(stderr, "Opened database successfully\n");
    }
    sqlite3_close(db);

    return 0;
}

编译,不报错的话即环境配置正确。

今天在根据文件名的拓展名进行文件分类的时候,发现文件的拓展名是不区分大小写的,所以要根据拓展名进行简单分类的话,就需要一个string不区分大小写查找的方法。

size_t FindNoCase(string strSource, char* szTarget)
{
if(strSource.empty())
{
return string::npos;
}
string strSub = szTarget;
if (strSub.empty())
{
return string::npos;
}
for (string::iterator it = strSource.begin(); it != strSource.end(); ++it)
{
*it = tolower(*it);
}
for (string::iterator ite = strSub.begin(); ite != strSub.end(); ++ite)
{
*ite=tolower(*ite);//do not change szTarget context.
}
return strSource.find(strSub);
}

 

在C++中,system函数可以运行命令行,但是只能得到该命令行的int型返回值,并不能获得显示结果。例如system(“ls”)只能得到0或非0,如果要获得ls的执行结果,则要通过管道来完成的。首先用_popen(Linux下函数名为popen)打开一个命令行的管道,然后通过fgets获得该管道传输的内容,也就是命令行运行的结果。

VS2013下代码如下:

#include "stdafx.h"
#include<windows.h>
#include<string>
#include<iostream>
using namespace std;

std::string getCMD(char * cmd){
	FILE *file;
	char ptr[1024] = { 0 };
	char tmp[1024] = { 0 };
	strcat_s(ptr, cmd);
	string result = "";
	if ((file = _popen(ptr, "r")) != NULL)
	{
		while (fgets(tmp, 1024, file) != NULL){
			result = result + tmp;
		}
		_pclose(file);
	}
	return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
	cout << getCMD("ping www.maplefan.com");
	system("pause");
}

 

上列代码在执行诸如ping命令这种需要等待的命令行的时候,会知道ping指令完全结束才一次输出信息。

如果想要像ping命令一样即时输出的话,可以在while循环里想办法输出即可。

CLabelUI等控件可以实现高亮,实现方法如下:

(1)将该控件的SetShowHtml()设置为true,即使用html的方式进行解析。

(2)在字符中加入下列想要的格式即可。让它自己去解析,完成绘制。

 

//   Bold:             <b>text</b>
//   Color:            <c #xxxxxx>text</c>  where x = RGB in hex
//   Font:             <f x>text</f>        where x = font id
//   Italic:           <i>text</i>
//   Image:            <i x y z>            where x = image name and y = imagelist num and z(optional) = imagelist id
//   Link:             <a x>text</a>        where x(optional) = link content, normal like app:notepad or http:www.xxx.com
//   NewLine           <n>
//   Paragraph:        <p x>text</p>        where x = extra pixels indent in p
//   Raw Text:         <r>text</r>
//   Selected:         <s>text</s>
//   Underline:        <u>text</u>
//   X Indent:         <x i>                where i = hor indent in pixels
//   Y Indent:         <y i>                where i = ver indent in pixels

按照上述样式,可进行绘制操作。

比如构造一个字符串设置颜色:

<c #ff00ff>test</c>

构造成类似这种字符串,控件会自动以HTML格式解析绘制。

最近Win32编程被编码折磨得很惨。。。记录一下两个转化函数。

wchar_t*转std::string:

std::string wchar_tToString(wchar_t *wchar){
	std::string szDst;
	wchar_t* wText = wchar;
	DWORD dwNum = WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, NULL, 0, NULL, FALSE);
	char *psText;
	psText = new char[dwNum];
	WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, psText, dwNum, NULL, FALSE);
	szDst = psText;
	delete[]psText;
	return szDst;
}

std::string转wchar_t*:

wchar_t* stringToWchar_t( std::string str){
	std::string temp = str;
	int len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)temp.c_str(), -1, NULL, 0);
	wchar_t *wszUtf8 = new wchar_t[len + 1];
	memset(wszUtf8, 0, len * 2 + 2);
	MultiByteToWideChar(CP_ACP , 0 , (LPCSTR)temp.c_str() , -1 , (LPWSTR)wszUtf8 , len);
	return wszUtf8;
}

 

最近的文件搜索项目使用到了SQLite3作为数据库来缓存本地的文件,而通过USN读取到NTFS盘上的文件(开发机器上大概40w个文件/文件夹)在Intel Core i7-6700上大概就需要7s左右的时间,而通过常规的SQLite提供的C++ API 将文件的这些信息插入到数据库的表中,大概需要30s的时间,于是开始摸索是否有办法可以提高插入的效率进而减少我们程序的后台文件搜索耗时线程所花费的时间。

慢速:直接使用SQLite的C++ API

int sqlite3_exec(  sqlite3*,    const char *sql,   int (*callback)(void*,int,char**,char**),   void *,   char **errmsg)

中速:显式开启事务

所谓”事务“就是指一组SQL命令,这些命令要么一起执行,要么都不被执行。在SQLite中,每调用一次sqlite3_exec()函数,就会隐式地开启了一个事务,如果插入一条数据,就调用该函数一次,事务就会被反复地开启、关闭,会增大IO量。如果在插入数据前显式开启事务,插入后再一起提交,则会大大提高IO效率,进而加数据快插入速度。

开启事务只需在上述代码的前后各加一句开启与提交事务的命令即可:

开启事务:

sqlite3_exec(db,"begin;",0,0,0);

提交事务:

sqlite3_exec(db,"commit;",0,0,0);

高速:关闭写同步(synchronous)

之前的速度仍然不能够接受,在有关讲解SQLite配置的资料中,看到了“写同步”选项。

在SQLite中,数据库配置的参数都由编译指示(pragma)来实现的,而其中synchronous选项有三种可选状态,分别是full、normal、off。这篇博客以及官方文档里面有详细讲到这三种参数的设置。简要说来,full写入速度最慢,但保证数据是安全的,不受断电、系统崩溃等影响,而off可以加速数据库的一些操作,但如果系统崩溃或断电,则数据库可能会损毁。

SQLite3中,该选项的默认值就是full,如果我们再插入数据前将其改为off,则会提高效率。如果仅仅将SQLite当做一种临时数据库的话,完全没必要设置为full。在代码中,设置方法就是在打开数据库之后,直接插入以下语句:

sqlite3_exec(db,"PRAGMA synchronous = OFF; ",0,0,0);

极速:

虽然写同步设为off后,速度又有小幅提升,但是仍然较慢。我又一次踏上了寻找提高SQLite插入效率方法的道路上。终于,我发现,SQLite执行SQL语句的时候,有两种方式:一种是使用前文提到的函数sqlite3_exec(),该函数直接调用包含SQL语句的字符串;另一种方法就是“执行准备”(类似于存储过程)操作,即先将SQL语句编译好,然后再一步一步(或一行一行)地执行。如果采用前者的话,就算开起了事务,SQLite仍然要对循环中每一句SQL语句进行“词法分析”和“语法分析”,这对于同时插入大量数据的操作来说,简直就是浪费时间。因此,要进一步提高插入效率的话,就应该使用后者。

“执行准备”主要分为三大步骤:

  1. 调用函数sqlite3_prepare_v2(),并且声明一个指向sqlite3_stmt对象的指针,该函数对参数化的SQL语句zSql进行编译,将编译后的状态存入ppStmt中。
  2. 调用函数 sqlite3_step(),这个函数就是执行一步(本例中就是插入一行),如果函数返回的是SQLite_ROW则说明仍在继续执行,否则则说明已经执行完所有操作。
  3. 调用函数 sqlite3_finalize(),关闭语句。
  4. 样例代码如下:
    int _tmain(int argc, _TCHAR* argv[])
    {
    	const int nCount = 500000;
    	sqlite3* db;
    	sqlite3_open("testdb.db", &db);
    	sqlite3_exec(db, "PRAGMA synchronous = OFF; ", 0, 0, 0);
    	sqlite3_exec(db, "drop table if exists t1", 0, 0, 0);
    	sqlite3_exec(db, "create table t1(id integer,x integer,y integer ,weight real)", 0, 0, 0);
    	clock_t t1 = clock();
    
    	sqlite3_exec(db, "begin;", 0, 0, 0);
    	sqlite3_stmt *stmt;
    	const char* sql = "insert into t1 values(?,?,?,?)";
    	sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0);
    
    	for (int i = 0; i<nCount; ++i)
    	{
    		sqlite3_reset(stmt);
    		sqlite3_bind_int(stmt, 1, i);//bind第一个数据
    		sqlite3_bind_int(stmt, 2, i * 2);//bind第二个数据
    		sqlite3_bind_int(stmt, 3, i / 2);//bind第三个数据
    		sqlite3_bind_double(stmt, 4, i*i);//bind第四个数据
    		sqlite3_step(stmt);
    	}
    	sqlite3_finalize(stmt);
    	sqlite3_exec(db, "commit;", 0, 0, 0);
    	clock_t t2 = clock();
    	sqlite3_close(db);
    	std::cout << "cost time: " << (t2 - t1) / 1000. << "s" << std::endl;
    
    	system("pause");
    	return 0;
    }

     

综上所述啊,SQLite插入数据效率最快的方式就是:事务+关闭写同步+执行准备(存储过程),如果对数据库安全性有要求的话,就开启写同步。

在Win32中调试程序时经常需要输出调试信息以追踪数据流及程序运行状态。

当然,我们可以通过输出日志、文件等方式得到信息,但是控制台或许更方便、直观。

之前在使用基于Win32的ACLlib进行开发的时候,一直都可以显示一个控制台(也可以隐藏),感觉很方便。

那么,如何方便地在带GUI的Win32程序中,比如基于Duilib的Win32GUI程序中使用控制台进行调试输出?

具体操作流程如下:

  1. 打开控制台
  2. 重定向输出流至控制台
  3. 执行调试信息输出操作.

完整代码如下:

AllocConsole();
freopen("CONOUT$", "w", stdout);
std::cout << "This is a test info" << std::endl;

 

相关函数说明:

AllocConsole函数的功能是为当前的窗口程序申请一个Console窗口,其原型为

BOOL AllocConsole(void);

 

函数调用成功,返回非零值,调用不成功则返回0。

freopen函数用来替换一个流,或者说重新分配文件指针,以实现重定向。可重定向的流有:标准输入流、标准输出流或者标准错误流。其函数原型为

FILE *freopen(const char *path, const char *mode, FILE *stream);

其中”CONOUT$”是指代当前console的特殊字符串,”w”表明以written模式打开这个console,stdout指代标准输出流。

 

补充:

AllocConsole函数不能改变控制台窗口在屏幕上的位置、尺寸等属性。以下函数可以控制&获取控制台相关信息。

GetConsoleScreenBufferInfo   // 检索窗口大小,屏幕缓冲区大小及颜色属性
SetConsoleWindowInfo       // 改变控制台窗口大小
SetConsoleScreenBufferSize   // 改变控制台屏幕缓冲区大小
SetConsoleTextAttribute      // 设置颜色属性
SetConsoleTitle          // 设置控制台窗口标题
GetConsoleTitle          // 获取控制台窗口标题