踩坑记:Poco库,MySql,解析大文本的bug

1 篇文章 0 订阅
1 篇文章 0 订阅

这两天在调试一个小功能,使用c++,读取MySql。使用的是Poco库。按照官网的写法:

    std::cout << "read normal data by poco recordset "<<std::endl;
	Poco::Data::MySQL::Connector::registerConnector();
	Poco::Data::Session session("MySQL", "host=127.0.0.1;port=3306;user=root;password=Citic@123;db=test");
	Poco::Data::Statement select(session);
	select << "select id,name from student ",now;//查询
	Poco::Data::RecordSet rs(select);//创建结果集
	bool more = rs.moveFirst();//第一条
	while (more)
	{
		int id = rs[0].convert<int>();//读取id
		std::string name = rs[1].convert<std::string>();//读取name
		std::cout << "id=" << id << " name=" << name << std::endl;//输出
		more = rs.moveNext();//下一条
	}

 代码没有问题,可以查询到数据,并输出:

换一张表,其中有一个字段是text,里面存储的数据比较大,4K个字符。结果就报错了。表结构如下:

查询代码:

	std::cout << "read normal data by poco recordset " << std::endl;
	Poco::Data::MySQL::Connector::registerConnector();
	Poco::Data::Session session("MySQL", "host=127.0.0.1;port=3306;user=root;password=Citic@123;db=test");
	Poco::Data::Statement select(session);
	select << "select uid,data1 from test ", now;
	Poco::Data::RecordSet rs(select);
	bool more = rs.moveFirst();
	while (more)
	{
		int id = rs[0].convert<int>();//读取id
		std::string data = rs[1].convert<std::string>();//读取data
		std::cout << "id=" << id << " data=" << data << std::endl;//输出
		more = rs.moveNext();//下一条
	}

两次代码一模一样,但是执行就报错了:

报错:ucrtbase.dll,处有未经处理的异常: 将一个无效参数传递给了将无效参数视为严重错误的函数。
这个dll属于系统C语言运行时。在网上搜相关的内容,基本上都是流读写或者关闭不正常导致。经在尝试各种方法以后,还是无法解决这个问题。

推测,应该是poco库本身在对mysql的大文本字段进行解析的时候,出bug了。只好另辟蹊径。偶然发现,RecordSet这个类,有一个copy(ostrem),大致意思是直接给recordset流拷贝一下。既然这样,那么就来尝试一下。

    std::cout << "read big data by ofstream" << std::endl;
	Poco::Data::MySQL::Connector::registerConnector();
	Poco::Data::Session session("MySQL", "host=127.0.0.1;port=3306;user=root;password=Citic@123;db=test");
	Poco::Data::Statement select(session);
	select << "select uid,data1 from test ", now;
	Poco::Data::RecordSet rs(select);//创建数据集,select对象给数据全部写入数据集
	std::ofstream outFile("data.txt");//创建一个文件流
	rs.copy(outFile);//流拷贝
	outFile.flush();//强制写入

这个代码的意思,创建一个文件流,输出到test.txt。然后直接从recordset把流拷贝走。如果成功的话,这个文件里面应该是mysql查询到的数据。

执行结果如上图所示。实际上,这就是mysql返回的查询结果,rescordset里面存储的就是这个。recordset再提供一些方法去从这个数据里面解析成一个个值。到此基本上发现了问题,就是Poco自带的解析方法,再解析大量数据的时候,出问题了。

所以:不想重新编译Poco库,那就想办法自己解析。解析办法,就是按字符串,一行一行去解析。代母如下:

	std::cout << "read big data by ostream" << std::endl;
	Poco::Data::MySQL::Connector::registerConnector();
	Poco::Data::Session session("MySQL", "host=127.0.0.1;port=3306;user=root;password=Citic@123;db=test");
	Poco::Data::Statement select(session);
	select << "select uid,data1 from test ", now;
	Poco::Data::RecordSet rs(select);//创建数据集,select对象给数据全部写入数据集
	std::stringstream  stm;//定义一个用于接收recoredset的字符串流
	rs.copy(stm);//结果集复制到字符串流
	stm.flush();//强制读取完毕
	char line[4500] = { 0 };//读取字符串流读取的缓存,要大于一行总字节长度
	stm.getline(line, 4500);//跳过标题 uid   ,  data1
	stm.getline(line, 4500);//跳过分割 --------------------
	int id = 0;//id变量
	std::string data = "";//data变量
	while (stm.getline(line, 4500))
	{
		sscanf(line, "%d ", &id);//解析id
		data = std::string(line + 17);//解析长文本
		std::cout << id << "-----" << data << std::endl;
	}

 

运行结果,解析成功,并且不报错。

解析原理就是把recordset里面的流先复制到字符串流,然后再一行一行的读取。前两行是标题和分隔符,后面是数据,按照自己定义的数据规则去读取。

line+17:因为uid是一个int,读取出来,占了16个字节,前面留空。uid和data1列之间有一个空格,所以,data1数据开始的下标是17。

到此问题解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值