赞
踩
1. 打开数据库函数声明:
int sqlite3_open_v2(const char * filename , sqlite3 * ppDb , int flags , const char * zVfs);
1) filename数据库库文件名,
2) ppDb数据库句柄,
3) flag标志,若数据库文件不存在,且第三个参数包括SQLite_open _create标志,那么函数将打开一新数据库文件,如无 SQLite_open _create标志否则返回错误。flags包含向量SQLITE_OPEN_CREATE, SQLITE_OPEN_READONLY , SQLITE_OPEN_READWRITE 。当以只读和读写模式打开数据库的时候,这两个选项都是要求数据库文件已经存在,否则将返回 错误,是传统的open。参数flags也可结合SQLITE_OPEN_NOMUTEX、SQLITE_OPEN_FULLMUTE、SQLITE_OPEN_SHAREDCACHESQLITE_OPEN_PRIVATECACHE进一步控制数据库句柄的事务行为。
4) zVFS是将使用的VFS文件名。参数zVFs允许调用去重写默认的操作系统接口sqlite3_vfs方法。
打开完成后,sqlite3_open_v2( )将初始化有参数ppDb传递给他的可称为隐式句柄的结构体,本句柄代表一个到数据库的连接。该连接可能附加了好多个数据库,代表事务上下文环境。
int sqlite3_open( const char * filename , sqlite3 * ppDb) ;
int sqlite3_open( const void * filename , sqlite3 * ppDb) ;
2. 关闭数据库函数声明:
int sqlite3_close( sqlite3 *) ; 顺利关闭数据库的前提是完成与连接相关的所有查询,只要有一个没完成,就会返回 sqlite_busy 错误并且提示。若是连接上有打开的事务,事务自动回滚。
3. 执行查询:
函数:sqlite3_exec(),称为便捷函数,封装了好多任务。
函数声明:
int sqlite3_exec( sqlite * , const char * sql , sqlite_callback , void *data , char ** errmmsg) ;
1) sqlite* 表示打开的数据库;
2) sql 为执行的sql语句;
3) callback回调函数;
4) void *data为回调函数的第一个参数指向提供给回调函数的应用程序特定的数据,也是回调函数的第一个参数;
5) errmsg 为错误信息,是指向错误消息字符串的指针 。
注意:sqlite_exec() 有两个错误消息来源,返回值错误和可读的字符串errmsg。
注意:如果提供 了errmsg,用来创建错误消息的内存是在堆上分布的。 故在调用后,应该检查一下是否为null值,如果有错误发生,使用sqlite3_free()释放errmsg占用的内存。
在简单命令中使用sqlite3_ exec( ),实例代码:
int main( int argc , char ** argv )
{
sqlite3 *db;
char *zErr ;
nt rc ;
char *sql;
rc = sqlite3_open_v2( "test.db" , &db);
if( rc) {
fprintf(stderr , " Can't open database :%s\n" , sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
sql="create table episodes(id int , name text ) ";
rc = sqlite3_exec(db , sql, NULL , NULL , &zErr ) ;
if(rc ! = SQLITE_OK){
if( zErr != NULL ) {
fprintf( stderr , " SQL error : %s\n " , zErr);
sqlite3_free(zErr) ;
}
}
sql = "insert into episodes values ( 10,'the dinner party ' )";
rc = sqlite3_exec(db , sql , NULL ,NULL , &zErr);
sqlite3_close(db) ;
return 0 ;
}
回调函数的声明:
typedef int ( * sqlite3_callback ) (void * , int , char ** ,char ** );其中void *是为sqlite3_exec()第四个参数提供的数据,int代表字段的数目,char** 便是行中字段名称的字符串的数组,char**表示字段ing成的字符串数组。
sqlite3_exec( )允许执行一批命令,并可以通过回调接口收集所有的返回数据。
准备查询的优点:
不需要回调接口,编码简单、清晰;关联了提供信息的函数,可以获得列的存储类型、声明类型、模式名称、表名、数据库名;sqlite3_exec( )的接口只是提供列的名称;准备查询提供一种文本外的获取字段、列值的方法,可以以C数据类型获取;准备查询能重新运行,可以重用已经编译的SQL;准备查询支持参数化的SQL语句
检查变化:
可以从sqlite3_changes()获得有多少记录受到影响,提供最后一次执行的更新、插入、删除语句影响的记录数目。不包含原始命令触发器的结果。而最后的字段信息可以通过sqlite3_last_inert_rowid( )获得该值。
获取表查询:
函数:sqlite3_get_table( )返回单独函数调用中一个命令的结果集。封装了sqlite3_exec( )且尽可能的返回数据,获取记录更便捷。
函数声明:
int sqlite3_get_table( sqlite * , /*打开的数据库*/
const char * sql , /*将要执行的sql语句*/
char ***resultp , /* 结果写入该指针指向的char *[ ]*/
int *nrow, /* 结果集中行的数目*/
int * ncolumn, /*结果集中列的数目*/
char ** errmsg /*error message*/
);
函数接收sql语句中返回的所有记录,使用堆上锁声明的内存,使用sqlite3_malloc( )存储在resultp中。必须使用sqlite3_free_table()释放内存,该函数的唯一参数就是resultp指针。
查询准备:
每个查询函数都有自己的行和列的获取方式。相对于准备查询来说,sqlite3_exec()和sqlite3_get_table()提供的功能还是比较少的;值得一提的是sqlite3_exec()对于运行修改数据库的命令的很合适,一个函数调用就可以完成工作。
准备查询使用一组特别函数进行列和行的访问。列值可以使用sqlite3_column_xxx()函数获取,xxx为返回值的数据类型;
准备查询的三个步骤:编译、执行、完成。
sqlite3_prepare_v2()编译查询,由sqlite3_step()分布执行查询,sqlite3_finalizer()完成关闭查询。sqlite3_reset()可以重用编译。
编译:
SQL语句 → VBDE字节码。由sqlite3_prepare_v2()完成。
函数声明:int sqlite3_prepare_v2(
sqlite* db , /*打开的数据库*/
const char *zSQl , /*sql文本*/
int nBYTES , /*zsql字节长度*/
sqlite3_stmt **ppStmt , /*语句句柄*/
const char **pzTail /*指向zSQl未使用的部分*/
);
分配执行该语句的所需的所有资源,将其字节码关联到单个语句句柄,参数ppStmt (是sqlite3_stmt的结构体)来指定。这种数据结构包含了命令的字节码、绑定的参数、B-tree游标、执行上下文、以及sqlite3_step( )在执行过程中查询状态所需的其他数据。sqlite3_prepare_v2()不会对连接或者数据库产生任何影响,也不启动事务或者获取锁。他可以直接通过编译器工作,未执行查询准备。 语句句柄高度依赖于他所被编译的数据库模式 ,数据库模式的更改对于准备查询语句会有失败的影响,此时可能会开启自动重新编译,而当无法重新编译的时候,sqlite3_step()函数会返回sqlite_schema错误。此时,检查错误需用sqlite3_errmsg() 。
执行:
前提是查询语句准备就绪,利用sqlite3_step()执行,声明:int sqlite3_step( sqlite3_stmt *pstmt);
sqlite3_step()直接接受语句句柄与VDBE通信,生成vdbe字节码。
the first time 调用sqlite3_step()d的时候,vdbe会获取一个执行指令的数据库锁。如不能获取锁的时候,且无繁忙处理程序,比如说发邮件服务什么的,就会返回sqlite_busy错误。而有繁忙处理程序的时候,就会调用该程序。
若SQL不返回数据,那么第一次调用就完成工作,返回完成结果的指示性代码;若要返回数据,那么第一次调用将语句定位在第一个记录的B-tree游标上,后续调用的sqlite3_step()将游标定位在结果集的后续记录上。到达末尾前,sqlite3_step()将为结果集的每个记录返回sqlite_row. 如果返回的是sqlite_down()表示游标到达结果集的末尾。
完成与重置:
函数: int sqlite3_finalize(sqlite3_stmt *pStmt) ;
int sqlite3_reset( sqlite3_stmt *pStmt) ;
sqlite3_finalize() 关闭语句,释放资源并提交或者回滚任何隐式事务,清除日志文件并释放相关联的锁。
sqlite3_reset()通过重置重复使用语句,保持已编译的sql语句,但是会将目前语句的变化提交到数据库。启动自动提交后,会释放并且锁定清除日志文件。相对于sqlite3_finalize()来说,他保留了与语句相关的资源,可重复执行,避免了多次对于sqlite3_prepare()的调用。
函数:sqlite3_complete( )是提示符提出的工具。
获取记录:
函数:sqlite3_column_count( ) 和sqlite3_data_count( ) ;
函数声明:
int sqlite3_column_count(sqlite3_stmt *pStmt ); 返回语句句柄相关的字段数;
int sqlite3_column_count(sqlite3_stmt *pStmt ); sqlite3_step()返回SQLITE_ROW后,该函数返回当前记录的列数,但是要求语句句柄上有活动游标。
获取字段信息:
函数sqlite3_column_name( );
函数声明: const char *sqlite_column_name(
sqlite3_stmt * , /* 语句句柄*/
int iCol ,/*字段的次序*/
);
获取当前记录中所有字段的关联的存储类:
函数声明:int sqlite3_column_type(
sqlite3_stmt * , /* 语句句柄*/
int iCol ; ,/*字段的次序*/
);
该函数返回5个存储类对应的整数值(原生数据类型或者存储类):
#define SQLITE_INTEGER 1;
#define SQLITE_FLOAT 2;
#define SQLITE_TEXT 3;
#define SQLITE_BLOB 4;
#define SQLITE_NULL 5;
可以使用sqlite3_column_decltype( )函数获取字段在表模式定义中声明的数据类型,函数声明:
const char *sqlite3_column_decltype(
sqlite3_stmt * , /* 语句句柄*/
int ; ,/*字段的次序*/
);
获取字段值:
sqlite3_column_xxx()返回字段值。通用形式:
xxx sqlite3_column_xxx(
sqlite3_stmt * , /* 语句句柄*/
int ,/*字段的次序*/
);
最常用的sqlite3_column_xxx()函数:
int sqlite3_column_int( sqlite3_stmt * , int iCol );
double sqlite3_column_double( sqlite3_stmt * , int iCol );
long long int sqlite3_column_int64( sqlite3_stmt * , int iCol );
const void *sqlite3_column_blob( sqlite3_stmt * , int iCol );
const unsigned char *sqlite3_column_text( sqlite3_stmt * , int iCol );
const void *sqlite3_column_text16( sqlite3_stmt * , int iCol );
列类型之间可以相互转换,具体的转换规则参照《SQLite权威指南》page187.
其中,对于blob列,复制时要指明其长度。获取其长度后,利用sqlite3_column_blob()复制二进制数据。
首先要注意句柄关联。其次,开始看关于查询参数化的内容。
查询参数化:
API支持在SQL语句中绑定参数,允许在后面为参数提供值,绑定的参数与sqlite3_prepare( ) 一起使用。如果没有绑定参数,那么sqlite3_step()默认使用null作为该参数的值。
准备语句过后,将使用sqlite3_bind_xxx( ) 函数绑定参数值。
函数的声明:sqlite3_bind_xxx(
sqlite3_stmt * , /*语句句柄*/
int i, /*参数个数*/
xxx value, /*绑定的值*/
) ;
不同类型参数的绑定声明:
int sqlite3_bind_xxx(sqlite3_stmt * , int , xxx) ,其中xxx代表int 、double、int64、null、zeroblob 、blob、text、text16,其中前四个用于标量值,后三个用于数组。sqlite3_mprintf()函数自动转移引号字符的功能在sqlite3_bind_text() 体现出。使用blob变种类型:
int sqlite3_bind_bolb(
sqlite3_stmt * ; /* 语句句柄 */
int , /*次序*/
const void * , /*指向blob数据*/
int n , /**数据的字节长度/
void (*(void * ) ) , /*清除字节程序*/
) ;
API为清理句柄提供的两个特殊含义的预定义值:
#define SQLITE_STATIC ( (void (*)(void *))0 ) 指示帮顶函数数组内存驻留在非托管空间,SQLite不会试图清理该空间
#define SQLITE_TRANSIENT ( (void (*)(void *))-1) 指示数组内存经常变化,SQLite需要自己的数据副本,副本在语句结束是自动清除
清除函数:void cleanup_fn( void *) .如果提供了自动清理函数,在语句结束时会自动调用。
参数绑定完成,sqlite3_step()接受参数帮顶值,替换sql文本,执行语句。
四种参数绑定办法的不同:就是参数的表示方式(位置参数,显式定义函数编号,字母数字化的数字名称)、参数分配编号的方式。
参数编号:允许为参数指定编号,不使用内部给定的序列。参数编号的语法是问号后紧跟一个数字。
比如:insert into table_name values(?num,?num,?num……);其中num的值在1——999之间而且是越小越好,当num一样,可以节省时间。
参数命名:在指定参数编号的地方给参数绑定一个名称。给参数编号的方法就是前缀为一个问号,表示命名的参数是在前面加上一个冒号(:)或者加上@符号。
比如:insert into table_name values(:value, : value , @value);sqlite3_prepare()为命名的参数自动分配编号,虽然编号未知,但是可以使用函数sqlite3_bind_parameter_index( )获得编号。
TCL参数:使用$符号。
错误与异常:
错误处理:
API可能返回错误的整数结果代码。比如:sqlite3_open_v2() 、sqlite3_prepare()sqlite3_exec()、sqlite3_bind_xxx()、sqlite3_close() 、sqlite3_create_collation()、sqlite3_collation_needed()、sqlite3_create_function()、sqlite3_finalize()、sqlite3_sqlite3_get_table()、sqlite3_reset() 、sqlite3_step() 等。
获取错误的函数信息函数:sqlite3_errmsg().
声明:const char * sqlite3_errmsg(sqlite3 *) ; 语句句柄是唯一参数,返回该链接上API调用产生的最近的错误,无错误才返回“not an error”.
SQLite 结果代码:《SQLite权威指南》page195.
繁忙情况处理:sqlite3_busy_handler() , sqlite3_busy_timeout( ).注意应急计划的启动,设置合理的超时时间。
模式改变处理:从锁定的角度来看,模式改变的时间在sqlite3_prepare()调用和sqlite3_step()调用之间。应对的方法就是处理改变重新开始。导致SQLITE_SCHEMA存在的原因:分离数据库、修改或者安装用户自定义的函数或者聚合、修改或者安装用户自定义的排序规则、修改或者安装授权函数、清理数据库空间等。这种错误情况与VDBE有关。
追踪SQL:使用函数sqlite3_trace( ) .
函数声明:void *sqlite3_trace(sqlite* , void(*xTrace)(void * , const char *) , void * );和窃听器的功能类似。
操作控制:
基本都是监视数据库的连接和事务的函数。
提交钩子:sqlite3_commit_hook()监视连接上的事务提交事件。
声明: void * sqlite_commit_hook(
sqlite * cnx , /*数据库句柄*/
int (*xCallback )(void * data) , /*回调函数*/
void *data , /*应用程序数据*/
);
回滚钩子:sqlite3_rollback_hook()监视连接上的事务提交事件。
声明: void * sqlite_rollback_hook(
sqlite * cnx , /*数据库句柄*/
int (*xCallback )(void * data) , /*回调函数*/
void *data , /*应用程序数据*/
);
注意自动回滚无法触发回调函数。
更新钩子:sqlite3_update_hook()监视连接上的事务提交事件。
声明: void * sqlite_update_hook(
sqlite * cnx , /*数据库句柄*/
int (*xCallback )(void * data) , /*回调函数*/
void *data , /*应用程序数据*/
);
此时,回调函数的行式:void callback( void * data, /*更新钩子的第三个参数*/
int operation_code, /*与sqlite_update、sqlite_insert、sqlite_delete操作对应*/
char const *db_name,
char const *table_name,
sqlite_int64 rowid,
);
授权函数:
事件过滤器:sqlite3_set_authorizer()
声明: int sqlite3_set_authorizer(
sqlite3 * ,
int (*xAuth)(void *,int ,
const char * , const char * ,
const char * , const char * ,
) ,
void *pUserData;
);
注意:授权函数是存在回调函数的:
int auth(
void *, /*用户数据*/
int, /*事件代码*/
const char *, /*事件具体相关的参数*/
const char *, /*事件具体相关的参数*/
const char *, /*数据库名称*/
const char *, /*触发器或者视图名称*/
)
授权函数的返回值:SQLITE_OK、SQLITE_DENY(终止整个sql语句)、SQLITE_IGNORE。注意交互式程序的帮助。page209。
线程:
SQLite的很多版本支持多线程。
共享缓存模型:
允许一个进程中的多个连接使用共同的页缓存。用于嵌入式服务器的情况比较多。
Thread →Server→con→Page Cache→database。
共享缓存模式中,线程依赖于服务器线程帮助管理数据库连接。线程通过某种通信机制想服务器发送SQL语句,服务器使用线程分配的连接执行他们,返回结果。线程可以继续发出命令控制自己的事务,只是实际连接存在于其他线程中并有其他的线程管理。
共享缓存模式的连接使用不同的并发模型和隔离级别。注意同族连接的更改内容。
读未提交隔离级别:
编译指示read_uncommited,连接就是读未提交隔离级别。
解锁通知:
新版本的SQLite的包含的函数:sqlite3_unlock_notify();
声明:
int sqlite3_unlock_notify(
sqlite3 * pBlocked, /*等待的连接*/
void (* xNotify ) (void **apArg , int nArg) /*要触发的回调函数*/
void *pNotifyArg /*传递给xNotify 的参数*/
);
如果因为代码竞争没有获得共享锁,那么将要返回SQLITE_LOCKED;此时调用函数sqlite3_unlock_notify(),在阻塞连接上(第一个参数)上注册回调函数XNotify;
回调函数可以有任何参数。
拥有阻塞锁的连接将会触发xNotify回调,作为完成事务的sqlite3_step( )或者sqlite3_close()的一部分。多线程调用 sqlite3_unlock_notify()时,事务可能已经完成了。此时,回调函数将会从sqlite3_unlock_notify()内初始化。每个阻塞连接上只会有一个注册解锁/通知回调函数;不可重入,回调函数中应咩有其他函数调用;使用Drop的时候,可能会出差错。
线程与内存管理:
与内存管理相关联的函数(指定堆的大小、限定堆栈):
void sqlite3_soft_heap_limit(int N) ; 将软堆设置为N字节。如果使用的内存超过了N,将会调用下一个函数sqlite3_release_memory(int N) ,返回实际释放的字节数。
int sqlite3_release_memory(int N) ;
默认这些函数是空操作,除非编译SQLite时,启动预处理命令指令:SQLITE_ENABLE_MEMORY_MANAGENENT.
扩展 C API
实现函数、聚合、排序的基本方法是使用回调函数,并在程序中注册,然后在SQL中使用。
函数和聚合使用相同的注册函数和类似的回调函数。用户自定义聚合、函数和排序规则的生命周期是很短的,基于连接注册,不存储在数据库中,需要确保应用程序夹杂了定制扩展并在连接中注册。
扩展必须在每一个使用它们的连接上注册。
注册函数:
可以使用sqlite3_create_function()在连接中注册和聚合。(UTF-8和UTF-16版本)
int sqlite3_create_function(
sqlite * cnx , /*连接句柄*/
const char *zFunctionName, /*在sql语句中使用的函数聚合名称*/
int nArg , /*参数的格式,-1表示参数的个数不稳定*/
int eTextRep, /*编码格式*/
void *pUserData , /*传递个回调函数的应用程序数据*/
void ( *xFunc )(sqlite3_context * , int , sqlite3_value * ) ,
void ( *xStep)(sqlite3_context * , int , sqlite3_value * ) ,
void ( *xFinal)(sqlite3_context * , int , sqlite3_value * )
);
int sqlite3_create_function16(
sqlite * cnx , /*连接句柄*/
const char *zFunctionName, /*在sql语句中使用的函数聚合名称*/
int nArg , /*参数的格式,-1表示参数的个数不稳定*/
int eTextRep, /*编码格式*/
void *pUserData , /*传递个回调函数的应用程序数据*/
void ( *xFunc )(sqlite3_context * , int , sqlite3_value * ) ,
void ( *xStep)(sqlite3_context * , int , sqlite3_value * ) ,
void ( *xFinal)(sqlite3_context * , int , sqlite3_value * )
);
步骤函数:
他和自定义函数的回调函数一样声明如下:
void fn(
sqlite3_context * ctx , /*聚合的上下文环境,接受sqlite3_create_function()提供的应用程序数据参数*/
int nargs , /*包含传递给函数的参数个数*/
sqlite3_value **values
);
可以使用sqlite3_user_data()获得用户数据:void *sqlite3_user_data(sqlite3_context * ) ;
聚合通过sqlite3_aggregate_context()为每个实例分配状态: void *sqlite3_aggregate_context( sqlite3_context * , int nBytes);
返回值:
参数value是sqlite3_value结构体数组,是SQLite实际参数值的句柄。这些值的实际数据可以通过sqlite3_value_xxx()获取:
xxx sqlite3_value_xxx(sqlite3_value* value) ;
注意他获取标量值和数组值的方式。
函数返回值五种形式。
函数:page221.
返回值:page222;
数组与内存清理器、错误处理、返回输入值、聚合、注册函数。
注意排序规则的定义等等。
扩展的许多规则和原来核心API的工作、定义基本一样。
实例:
#include <iostream> using namespace std; #include "sqlite/sqlite3.h" int callback(void*,int,char**,char**); int main() { sqlite3* db; int nResult = sqlite3_open("test.db",&db); if (nResult != SQLITE_OK) { cout<<"打开数据库失败:"<<sqlite3_errmsg(db)<<endl; return 0; } else { cout<<"数据库打开成功"<<endl; } char* errmsg; nResult = sqlite3_exec(db,"create table fuck(id integer primary key autoincrement,name varchar(100))",NULL,NULL,&errmsg); if (nResult != SQLITE_OK) { sqlite3_close(db); cout<<errmsg; sqlite3_free(errmsg); return 0; } string strSql; strSql+="begin;\n"; for (int i=0;i<100;i++) { strSql+="insert into fuck values(null,'heh');\n"; } strSql+="commit;"; //cout<<strSql<<endl; nResult = sqlite3_exec(db,strSql.c_str(),NULL,NULL,&errmsg); if (nResult != SQLITE_OK) { sqlite3_close(db); cout<<errmsg<<endl; sqlite3_free(errmsg); return 0; } strSql = "select * from fuck"; nResult = sqlite3_exec(db,strSql.c_str(),callback,NULL,&errmsg); if (nResult != SQLITE_OK) { sqlite3_close(db); cout<<errmsg<<endl; sqlite3_free(errmsg); return 0; } sqlite3_close(db); return 0; } int callback(void* ,int nCount,char** pValue,char** pName) { string s; for(int i=0;i<nCount;i++) { s+=pName[i]; s+=":"; s+=pValue[i]; s+="\n"; } cout<<s<<endl; return 0; }
修改上篇的例子,使用sqlite3_get_table,来去的结果集:
#include <iostream> using namespace std; #include "sqlite/sqlite3.h" int callback(void*,int,char**,char**); int main() { sqlite3* db; int nResult = sqlite3_open("test.db",&db); if (nResult != SQLITE_OK) { cout<<"打开数据库失败:"<<sqlite3_errmsg(db)<<endl; return 0; } else { cout<<"数据库打开成功"<<endl; } char* errmsg; nResult = sqlite3_exec(db,"create table fuck(id integer primary key autoincrement,name varchar(100))",NULL,NULL,&errmsg); if (nResult != SQLITE_OK) { sqlite3_close(db); cout<<errmsg; sqlite3_free(errmsg); return 0; } string strSql; strSql+="begin;\n"; for (int i=0;i<100;i++) { strSql+="insert into fuck values(null,'heh');\n"; } strSql+="commit;"; //cout<<strSql<<endl; nResult = sqlite3_exec(db,strSql.c_str(),NULL,NULL,&errmsg); if (nResult != SQLITE_OK) { sqlite3_close(db); cout<<errmsg<<endl; sqlite3_free(errmsg); return 0; } strSql = "select * from fuck"; //nResult = sqlite3_exec(db,strSql.c_str(),callback,NULL,&errmsg); char** pResult; int nRow; int nCol; nResult = sqlite3_get_table(db,strSql.c_str(),&pResult,&nRow,&nCol,&errmsg); if (nResult != SQLITE_OK) { sqlite3_close(db); cout<<errmsg<<endl; sqlite3_free(errmsg); return 0; } string strOut; int nIndex = nCol; for(int i=0;i<nRow;i++) { for(int j=0;j<nCol;j++) { strOut+=pResult[j]; strOut+=":"; strOut+=pResult[nIndex]; strOut+="\n"; ++nIndex; } } sqlite3_free_table(pResult); cout<<strOut<<endl; sqlite3_close(db); return 0; } /* int callback(void* ,int nCount,char** pValue,char** pName) { string s; for(int i=0;i<nCount;i++) { s+=pName[i]; s+=":"; s+=pValue[i]; s+="\n"; } cout<<s<<endl; return 0; }*/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。