Linux C++简单实现一个批量插入的sql脚本生成工具

之前项目上隔三差五地要手工批量导入一些数据,于是自己用C++写了这个sql脚本文件的生成工具。

Linux C++简单实现一个批量插入的sql脚本生成工具

一、需求说明

批量插入多个用户收费信息,也就是用户id不同,但是包括所属区域代号、收费模式和费率值等数据是相同的。如果使用手工拼接insert语句工作量很大,而且可能出现差错,所以改用程序来生成。

只需要配置好相关参数,然后直接执行程序就可以把生成的sql脚本交给DBA处理了。

二、C++程序

代码比较简单,从main函数中可以看出主要流程是先读取配置文件(见小节3)中的数据,然后把使用|分隔的用户id进行分解。经过一系列操作后,会拼接出所有的insert语句,最终把这些数据到写入到文件流中。

这里用到了C++文件流的读写、string中字符串的查找和删除、string数据切割成vector或者map对象等。当然代码还可以做很多优化,例如参数的过滤检查、用户数过多时考虑分成多个insert语句、数据量大时使用多线程等。

main.cpp
/** 
 * @FileName    linux_file_basic1_2.cpp
 * @Describe    Linux C++简单实现一个批量插入的sql脚本生成工具.
 * @Author      vfhky 2017-10-18 23:29 https://typecodes.com/cseries/genmysqlinsertsql.html
 * @Compile 	g++ linux_file_basic1_2.cpp -o linux_file_basic1_2 -std=c++11
 */
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <sstream>

#include <time.h>


typedef std::map<std::string, std::string> StringMap;


//去除string中所有的空格
void TrimAllSpace( std::string &s_str )
{
    size_t index = 0;
    if( !s_str.empty())
    {
        while( (index = s_str.find(' ',index)) != std::string::npos )
        {
            s_str.erase( index, 1 );
        }
    }
}

//切割字符串
void splitToMap( const std::string &s_orginal, const std::string &s_split_pattern, StringMap &StringMap_Obj )
{
    if( s_orginal.empty() )
    {
        throw( "s_Faccount_id is empty." );
    }
    
    //方便截取最后一段数据
    std::string s_strs = s_orginal + s_split_pattern;

    size_t i_pos = s_strs.find(s_split_pattern);
    size_t i_size = s_strs.size();

    while( i_pos != std::string::npos )
    {
        std::string s_key_val = s_strs.substr( 0, i_pos );
         
        size_t i_key_pos = s_key_val.find( "=" );
        if( i_key_pos != std::string::npos )
        {
        	std::string s_key = s_key_val.substr( 0, i_key_pos );
        	std::string s_val = s_key_val.substr( i_key_pos+1, s_key_val.size() );
        	
        	StringMap_Obj.insert( make_pair( s_key, s_val ) );
        }
        else
        {
        	throw( "config file is illegal." );
        }
        
        s_strs = s_strs.substr( i_pos+1, i_size );
        i_pos = s_strs.find( s_split_pattern );
    }
}

//切割字符串
void splitToVector( const std::string &s_orginal, const std::string &s_split_pattern, std::vector<std::string> &vect_str )
{
	vect_str.clear();

    if( s_orginal.empty() )
    {
        throw( "s_Faccount_id is empty." );
    }
    
    //方便截取最后一段数据
    std::string strs = s_orginal + s_split_pattern;

    size_t pos = strs.find(s_split_pattern);
    size_t size = strs.size();

    while( pos != std::string::npos )
    {
        std::string s_str = strs.substr(0,pos);
        TrimAllSpace( s_str );
        vect_str.push_back( s_str );
        strs = strs.substr(pos+1,size);
        pos = strs.find(s_split_pattern);
    }
}

void GetTime( std::string &s_date )
{
	time_t t = time(NULL);
	char p_date[20] = {0x00};
	strftime( p_date, sizeof(p_date), "%Y%m%d %H:%M:%S", localtime(&t) );
	s_date = p_date;
}



class CGenSqlFile
{
public:
	CGenSqlFile( const std::string &s_config_file ): s_config_file( s_config_file )
	{
		ReadFile();
	}
	
	virtual ~CGenSqlFile(){}
	
	
	//读取每行的数据,然后进行处理
	void ReadFile()
	{
	    std::ifstream inFile;

	    //以二进制可读的方式打开文件,也可以使用: inFile.open( s_config_file.data() );
	    inFile.open( s_config_file.c_str(), std::ios::in | std::ios::binary );

	    //Checks if the file stream has an associated file.
	    if( !inFile.is_open() )
		{
			inFile.close();
			throw( "Cannot open the config file." );
		}

	    //每一行的数据
	    std::string s_line_buf;

	    //读取一行内容    getline( inFile, s_line_buf )
	    while( getline( inFile, s_line_buf, inFile.widen('\n') ) )
	    {
	    	if( s_line_buf.empty() || s_line_buf[0] == '#' )
	    	{
	    		continue;
	    	}
	    	
	        //开始数据处理
	        splitToMap( s_line_buf, "&", StringMap_Obj );
	    }

	    //关闭文件流
	    inFile.close();
	}

	//打印所有的配置数据
	void ShowAllConf() const
	{
		std::cout << "=================== Config data begin. ===================" << std::endl;
		for( auto iter : StringMap_Obj )
		{
			std::cout << "[" << iter.first << "]=[" << iter.second << "]" << std::endl;
		}
		std::cout << "=================== Config data end. ===================" << std::endl;
	}
	
	std::string GenSql()
	{
		const std::string s_genonesql_comm = GenOneSqlComm();
		
		//所有的商户/渠道id
		std::vector<std::string> vect_Faccount_id;
		splitToVector( StringMap_Obj["Faccount_id"], "|", vect_Faccount_id );
		
		//拼装sql
		std::string s_sql_content;
		for( auto iter : vect_Faccount_id )
		{
			s_sql_content += GenOneSql( iter, s_genonesql_comm );
		}
		
		//把末尾的逗号去掉
		std::string s_sql_contents = s_sql_content.erase( s_sql_content.length()-2, 2 );
		
		//当前日期
		std::string s_date;
		GetTime( s_date );
		std::string s_sql_illustartion = "-- --------------------------------------------------------------------\n"
										 "-- Auto Generated on " + s_date + " --------------------------------\n"
										 "-- --------------------------------------------------------------------\n";
		
		return ( s_sql_illustartion + StringMap_Obj["INSERT_HEADER"] + "\n" + s_sql_contents + "\n;\n\n\n" );
	}
	
	
	//生成SQL数据
	std::string GenOneSql( const std::string &s_Faccount_id, const std::string &s_genonesql_comm )
	{
		return ( "( '" + s_Faccount_id + s_genonesql_comm );
	}
	
	std::string GenOneSqlComm()
	{
		std::string s_sql = "', '" + StringMap_Obj["Fpay_channel_id"] + "', '" + StringMap_Obj["Fpay_method"] + "', '" + StringMap_Obj["Frate_mode"] + "', '" + StringMap_Obj["Frate_value"] + "', '" + StringMap_Obj["Frate_value_base"] + "', '1'),\n";
		
		return s_sql;
	}
	
	void OpenFile()
	{
		//追加
		if( "1" == StringMap_Obj["Ffile_mode"] )
		{
			ostream.open( StringMap_Obj["sql_file"].c_str(), std::ofstream::out | std::ofstream::binary | std::ofstream::app );
		}
		//覆盖
		else if( "2" == StringMap_Obj["Ffile_mode"] )
		{
			ostream.open( StringMap_Obj["sql_file"].c_str(), std::ofstream::out | std::ofstream::binary );
		}
		else
		{
			throw( "Ffile_mode is illegal." );
		}
		
		
		if( !ostream.is_open() )
		{
			ostream.close();
			throw( "Cannot open the file." );
		}
	}

	void CloseFile()
	{
		ostream.close();
	}
	
	//获取配置数据
	const std::string GetConfByKey( std::string &s_config_key ) const
	{
		StringMap::const_iterator iter = StringMap_Obj.find( s_config_key );
		if( iter != StringMap_Obj.end() )
		{
			return iter->second;
		}
		else
		{
			return "";
		}
	}

	//把数据写入到文件流中
	void WriteToFile( const std::string &s_file_contents )
	{
		ostream << s_file_contents;
	}
	
protected:
	StringMap StringMap_Obj;
	
	
private:
	std::string s_config_file;
	std::ofstream ostream;
	
};


int main( const int argc, const char * const * argv )
{
	static const std::string s_config_file = "./LINUX_FILE_BASIC1_2.txt";
	
	
	try
	{
		CGenSqlFile t( s_config_file );
		
		//显示所有的配置
		t.ShowAllConf();
		
		//打开文件
		t.OpenFile();
		
		//生成SQL数据
		std::string s_sql_contents = t.GenSql();
		
		//写入到文件
		t.WriteToFile( s_sql_contents );
		
		//关闭文件
		t.CloseFile();
		
		std::string s_config_key = "sql_file";
		std::cout << "\nGenerate sql file=[" << t.GetConfByKey( s_config_key ) << "] success." << std::endl;
	}
	catch( const char *p_errstr )
	{
		std::cout << p_errstr << std::endl;
	}

	return 0;
}

三、配置文件

上面的C++程序会读取这个linux_file_basic1_2.txt的配置文件,可以使用#符号给一些必要的数据配备注释文字。

需要说明的是Ffile_mode参数表示sql文件的写入方式,当值为2时表示每次都把生成的数据覆盖原文件中的数据;为1时表示支持把多次生成的SQL数据写入到同一个SQL脚本文件中。

index.html
#  
# @FileName    linux_file_basic1_2.txt
# @Describe    Linux C++简单实现一个批量插入的sql脚本生成工具的配置文件.
# @Author      vfhky 2017-10-18 23:29 https://typecodes.com/cseries/genmysqlinsertsql.html



# 用户账号id,多个账号使用 | 符号分隔
Faccount_id=10001342|  10001447

# 所属区域代号
Fpay_channel_id=GDSZ01

# 支付方式:
# 支付宝服务窗支付		ALIPAY.JSAPI
# 支付宝刷卡			ALIPAY.MICROPAY
# 支付宝扫码支付		ALIPAY.NATIVE
# 
# 微信公众号			WXPAY.JSAPI
# 微信刷卡支付			WXPAY.MICROPAY
# 微信扫码支付			WXPAY.NATIVE
Fpay_method=WXPAY.NATIVE

# 收费模式,1固定比例,2单笔计费
Frate_mode=1

# 费率值,单位:1/10000
Frate_value=50

# 基本费用,单位:分
Frate_value_base=0


# 生成的sql文件名
sql_file=user_account.sql

# 生成的sql文件模式,1追加,2覆盖
Ffile_mode=2

# insert语句的头部
INSERT_HEADER=INSERT INTO `USER_ACCOUNT_DB`.`t_mch_rate` ( `Faccount_id`, `Fpay_channel_id`, `Fpay_method`, `Frate_mode`, `Frate_value`, `Frate_value_base`, `Fstatus`) VALUES 

四、编译执行

使用《Linux C/C++工程中可生成ELF、动/静态库文件的通用Makefile》文中的Makefile编译:

Linux C++简单实现一个批量插入的sql脚本生成工具

然后执行生成MySql脚本如下:

Linux C++简单实现一个批量插入的sql脚本生成工具

评论

评论加载中…