前段时间研究了如何在Linux上读取配置文件,基于手头上的项目也成功实现了,主要通过在C++文件中用定义需从配置文件读取的配置属性为全局变量,然后通过读取函数来更新配置属性。
其中也附有日志等级函数的打印输出。
————————————————————————
目录
- 用宏定义配置文件的路径
- 将配置属性定义为全局变量
- 相关全局变量
- 配置文件读取函数及其相关函数的初始化
- 函数的具体定义
- 配置文件作用方法
- 配置文件config.ini内容
—————————————————————————
1. 用宏定义配置文件的路径
//***********[START]相关宏定义[START]***************
//配置文件路径
#define CONFIG_FILE_PATH "./config.ini"
//日志等级的宏定义
#define LOG_ERR -1
#define LOG_INF 0
#define LOG_DBG 1
//配置文件关键字长度限制
#define KEYVALLEN 256
//是(1)否(0)使用线程锁,调试发现不使用锁就不会有延迟
#define USE_PTHREAD_LOCK 0
//*************[END]相关宏定义[END]*****************
2. 将配置属性定义为全局变量
//*****[START]从配置文件中获取的全局变量及其默认值[START]******
//------日志配置参数------
//日志路径,因为一般将结果保存在“result”文件夹中,所以要使用“result/***.log”的格式
char gconf_szLogPath[256] = "result/testResult.log";
//设置程序日志等级:1 表示打印所有(调试,一般,错误信息),0 表示打印运行(一般,错误)信息,-1 表示仅打印错误信息.
int gconf_nLogLevel = 1;
//设置程序日志大小上限(单位为bytes,0表示不设置),超过后日志清空,最大2,147,483,647,建议不要超过104,857,600(即100*1024*1024)
int gconf_lMaxLogSize = 104857600;
//程序开始运行时,日志是(1)否(0)清空
int gconf_nLogReset = 1;
//------其余配置参数------
//设置程序的运行时间,达到时间后终止程序,方便调试.(单位为秒,0表示一直运行),原为DURATION_TIME
int gconf_nProgramDurationTime = 0;
//*****[END]从配置文件中获取的全局变量及其默认值[END]******
3. 相关全局变量
//********[START]相关全局变量[START]****************
//程序首次运行标记
bool g_bFirstRun = true;
//配置文件加载次数
int g_nCountLoadConfig = 0;
//用于获取配置文件属性
struct stat g_statConfigFile = {0};
//定义车牌及其数目容器
static vector<string> gs_szVecLicense;
static vector<int> gs_nVecCount;
//时间戳的全局变量
clock_t g_clockLast;
//线程ID
pthread_t g_pthreadRunIPCameraId;
//线程锁
pthread_mutex_t mutex;
//**********[END]相关全局变量[END]******************
4. 配置文件读取函数及其相关函数的初始化
//**********[START]函数的初始化[START]**************
//-------------结果目录创建函数----------------
//-功能: 处理输入的结果路径,截取相应的目录名,在工作目录上创建相应目录,用于保存结果.
int CreateDir(char* szPath,char* szDirPath);
//-------------日志打印函数--------------------
//-功能: 把日志信息保存到日志文件中,路径为从配置文件中获取的全局变量gconf_szLogPath所定义.
void PrintLOG(int nType,const char* ms, ... );
//-------------结果输出函数--------------------
//-功能: 将结果识别结果存放到文本文件中,路径为从配置文件中获取的全局变量gconf_szPlateResultPath所定义.
void PrintResult(const char* ms, ... );
//---------配置文件读取辅助函数,删左空格-------
//-功能: 删除从配置文件中读取到的字符串的左空格.
char * l_trim(char * szOutput, const char *szInput);
//---------配置文件读取辅助函数,删右空格-------
//-功能: 删除从配置文件中读取到的字符串的右空格.
char *r_trim(char *szOutput, const char *szInput);
//---------配置文件读取辅助函数,删两边空格-------
//-功能: 删除从配置文件中读取到的字符串的两边空格.
char * a_trim(char * szOutput, const char * szInput);
//---------------配置文件检查函数-------------
//-功能: 检查配置文件是否被修改,如果被修改则返回1,没变返回0,错误返回-1.
static int CheckConfigFileSizeTime();
//---------------配置值读取函数----------------
//-功能: 从配置文件中读取对应配置值.
int GetConfigValue(char *szLabelInfoName, char *szKeyName, char *szKeyVal );
//---------------配置值读取函数(重载)---------
//-功能: 从配置文件中读取对应配置值,为int型重载.
int GetConfigValue(char *szLabelInfoName, char *szKeyName, int *pnKeyVal );
//---------------配置文件作用函数--------------
//-功能: 从配置文件中读取配置,并使之生效.
int LoadConfigFile();
//-------------配置文件重新作用函数------------
//-功能: 从配置文件中重新读取配置,并通过线程的重启来生效.
// (此函数功能并未完善,程序也未调用此函数)
int ReloadConfigFile();
//************[END]函数的初始化[END]****************
5. 函数的具体定义
//**********[STAT]函数具体定义[STAT]**************
//-------------结果目录创建函数----------------
//-功能: 处理输入的结果路径,截取相应的目录名,在工作目录上创建相应目录,用于保存结果.
//-Input: 结果路径
//-Output: 创建相应目录
//-Return: 如果成功返回1,错误返回-1
//--------------------------------------------
int CreateDir(char* szPath,char* szDirPath)
{
char* pTmpPathStart;
char* pTmpPathEnd;
pTmpPathStart = szPath;
while(pTmpPathEnd = strchr(pTmpPathStart,'/'))
{
memset(szDirPath,0,sizeof(szDirPath));
strncpy(szDirPath,szPath,(size_t)(pTmpPathEnd-szPath));
pTmpPathStart = pTmpPathEnd + 1;
//printf("%s\n",szDirPath);
if(access(szDirPath,0)!=0)
{
if(mkdir(szDirPath,S_IRWXU)==-1)
{
//PrintLOG(LOG_INF,"创建相应目录出错:%d!请检查路径是否正确.",errno);
return -1;
}
}
}
return 1;
}
//-------------日志打印函数--------------------
//-功能: 把日志信息保存到日志文件中,路径为从配置文件中获取的全局变量gconf_szLogPath所定义.
//-Input: 1.日志信息类型
// 2.与printf函数相似的格式化字符串
//-Output: 打印到日志文件的结果
//-Return: 无
//--------------------------------------------
void PrintLOG(int nLogType,const char* ms, ... )
{
char wzLog[1024] = {0};
char buffer[1024] = {0};
char szNULL[256] = "";
va_list args;
va_start(args, ms);
vsprintf( wzLog ,ms,args);
va_end(args);
struct stat statLogFile = {0};
time_t now;
time(&now);
struct tm *local;
struct timeval tv;
local = localtime(&now);
gettimeofday(&tv, NULL);
//mkdir("result",S_IRWXU);
CreateDir(gconf_szLogPath,szNULL);
FILE* file = fopen(gconf_szLogPath,"a+");
if(gconf_nLogReset == 1 && g_bFirstRun)
{
fclose(file);
unlink(gconf_szLogPath);
file = fopen(gconf_szLogPath,"a+");
g_bFirstRun = false;
}
if (stat (gconf_szLogPath, &statLogFile) == -1)
{
PrintLOG(LOG_ERR,"获取日志文件(%s)状态失败:%s.\n", gconf_szLogPath, strerror (errno));
return;
}
if (S_ISDIR(statLogFile.st_mode))
{
PrintLOG(LOG_ERR,"此日志文件路径(%s)是文件夹.错误代码为:%s!", gconf_szLogPath, strerror (errno));
return;
}
if (S_ISREG(statLogFile.st_mode))
{
;//PrintLOG(LOG_INF,"日志文件(%s)大小为:%ld bytes, 最近修改时间为: %s.",gconf_szLogPath, statLogFile.st_size, ctime (&statLogFile.st_mtime));
}
if(statLogFile.st_size >= gconf_lMaxLogSize && gconf_lMaxLogSize != 0){
fclose(file);
//printf("日志文件已满,程序终止/n.");
//exit(1);
unlink(gconf_szLogPath);
file = fopen(gconf_szLogPath,"a+");
}
if(gconf_nLogLevel == 1){
if(nLogType == 1){
sprintf(buffer,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld][DBG] %s\n",local->tm_year+1900, local->tm_mon,local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec,tv.tv_usec/1000,wzLog);
}
if(nLogType == 0){
sprintf(buffer,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld][INF] %s\n",local->tm_year+1900, local->tm_mon,local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec,tv.tv_usec/1000,wzLog);
}
if(nLogType == -1){
sprintf(buffer,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld][ERR] %s\n",local->tm_year+1900, local->tm_mon,local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec,tv.tv_usec/1000,wzLog);
}
}
if(nLogType <= gconf_nLogLevel){
fwrite(buffer,1,strlen(buffer),file);
printf("%s",buffer);
fclose(file);
}
// syslog(LOG_INFO,wzLog);
return ;
}
//-------------结果输出函数--------------------
//-功能: 将结果识别结果存放到文本文件中,路径为从配置文件中获取的全局变量gconf_szPlateResultPath所定义.
//-Input: 1.与printf函数相似的格式化字符串
//-Output: 打印到结果文件的车牌识别结果
//-Return: 无
//--------------------------------------------
void PrintResult(const char* ms, ... )
{
char wzLog[1024] = {0};
char buffer[1024] = {0};
char szNULL[256] = "";
va_list args;
va_start(args, ms);
vsprintf( wzLog ,ms,args);
va_end(args);
time_t now;
time(&now);
struct tm *local;
struct timeval tv;
local = localtime(&now);
gettimeofday(&tv, NULL);
sprintf(buffer,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld]%s\n",local->tm_year+1900, local->tm_mon,local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec,tv.tv_usec/1000,wzLog);
printf("%s",buffer);
//mkdir("result",S_IRWXU);
CreateDir(gconf_szPlateResultPath,szNULL);
FILE* file = fopen(gconf_szPlateResultPath,"a+");
fwrite(buffer,1,strlen(buffer),file);
fclose(file);
return ;
}
//---------配置文件读取辅助函数,删左空格-------
//-功能: 删除从配置文件中读取到的字符串的左空格.
//-Input: 1.被处理后的字符串指针
// 2.待处理的字符串指正
//-Output: 处理完的字符串
//-Return: 字符拷贝函数的返回结果
//--------------------------------------------
char * l_trim(char * szOutput, const char *szInput)
{
assert(szInput != NULL);
assert(szOutput != NULL);
assert(szOutput != szInput);
for (NULL; *szInput != '\0' && isspace(*szInput); ++szInput){
;
}
return strcpy(szOutput, szInput);
}
//---------配置文件读取辅助函数,删右空格-------
//-功能: 删除从配置文件中读取到的字符串的右空格.
//-Input: 1.被处理后的字符串指针
// 2.待处理的字符串指正
//-Output: 处理完的字符串
//-Return: 字符拷贝函数的返回结果
//--------------------------------------------
char *r_trim(char *szOutput, const char *szInput)
{
char *p = NULL;
assert(szInput != NULL);
assert(szOutput != NULL);
assert(szOutput != szInput);
strcpy(szOutput, szInput);
for(p = szOutput + strlen(szOutput) - 1; p >= szOutput && isspace(*p); --p){
;
}
*(++p) = '\0';
return szOutput;
}
//---------配置文件读取辅助函数,删两边空格-------
//-功能: 删除从配置文件中读取到的字符串的两边空格.
//-Input: 1.被处理后的字符串指针
// 2.待处理的字符串指正
//-Output: 处理完的字符串
//-Return: 字符拷贝函数的返回结果
//--------------------------------------------
char * a_trim(char * szOutput, const char * szInput)
{
char *p = NULL;
assert(szInput != NULL);
assert(szOutput != NULL);
l_trim(szOutput, szInput);
for (p = szOutput + strlen(szOutput) - 1;p >= szOutput && isspace(*p); --p){
;
}
*(++p) = '\0';
return szOutput;
}
//---------------配置文件检查函数-------------
//-功能: 检查配置文件是否被修改,如果被修改则返回1,没变返回0,错误返回-1.
//-Input: 无
//-Output: 无
//-Return: 如果被修改则返回1,没变返回0,错误返回-1
//--------------------------------------------
static int CheckConfigFileSizeTime ()
{
struct stat statTmpConfigFile = {0};
if (stat (CONFIG_FILE_PATH, &statTmpConfigFile) == -1)
{
PrintLOG(LOG_ERR,"获取配置文件(%s)状态失败:%s.\n", CONFIG_FILE_PATH, strerror (errno));
return (-1);
}
if (S_ISDIR(statTmpConfigFile.st_mode))
{
PrintLOG(LOG_ERR,"此配置文件路径(%s)是文件夹. 错误代码为:%s!", CONFIG_FILE_PATH, strerror (errno));
return (-1);
}
if (S_ISREG (statTmpConfigFile.st_mode))
{
;//PrintLOG(LOG_INF,"配置文件(%s)大小为:%ld bytes, 最近修改时间为:%s",CONFIG_FILE_PATH, statTmpConfigFile.st_size, ctime (&statTmpConfigFile.st_mtime));
}
//如果配置文件未被修改,则返回0
if(statTmpConfigFile.st_size == g_statConfigFile.st_size && statTmpConfigFile.st_mtime == g_statConfigFile.st_mtime){
return 0;
}
//如果配置文件被且是首次运行程序修改,则打印相应提示,且调用配置文件读取函数读取相应配置值
else if(g_bFirstRun){
PrintLOG(LOG_INF,"加载配置文件.");
g_statConfigFile = statTmpConfigFile;
return 1;
}
//如果配置文件被且不是首次运行程序修改,则打印提示,且调用配置文件读取函数读取相应配置值
else{
PrintLOG(LOG_INF,"重新加载被修改的配置文件.");
g_statConfigFile = statTmpConfigFile;
return 1;
}
}
//---------------配置值读取函数----------------
//-功能: 从配置文件中读取对应配置值.
//-Input: 1.配置文件中待读取的标签信息名
// 2.配置文件中指定标签中的待读取关键字名
// 3.配置文件中指定标签和关键字的对应配置值(char型指针)
//-Output: 相应配置值
//-Return: 如果读取成功返回1,错误返回-1
//--------------------------------------------
int GetConfigValue(char *szLabelInfoName, char *szKeyName, char *szKeyVal )
{
char szTmpLabelInfoName[32],szTmpKeyName[32];
char *buf,*c;
char buf_i[KEYVALLEN], buf_o[KEYVALLEN];
FILE *fp;
int found=0; /* 1 szLabelInfoName 2 szKeyName */
if( (fp=fopen( CONFIG_FILE_PATH,"r" ))==NULL ){
printf( "openfile [%s] error [%s]\n",CONFIG_FILE_PATH,strerror(errno) );
return(-1);
}
fseek( fp, 0, SEEK_SET );
memset( szTmpLabelInfoName, 0, sizeof(szTmpLabelInfoName) );
sprintf( szTmpLabelInfoName,"[%s]", szLabelInfoName );
while( !feof(fp) && fgets( buf_i, KEYVALLEN, fp )!=NULL ){
l_trim(buf_o, buf_i);
if( strlen(buf_o) <= 0 )
continue;
buf = NULL;
buf = buf_o;
if( found == 0 ){
if( buf[0] != '[' ) {
continue;
} else if ( strncmp(buf,szTmpLabelInfoName,strlen(szTmpLabelInfoName))==0 ){
found = 1;
continue;
}
} else if( found == 1 ){
if( buf[0] == '#' ){
continue;
} else if ( buf[0] == '[' ) {
break;
} else {
if( (c = (char*)strchr(buf, '=')) == NULL )
continue;
memset( szTmpKeyName, 0, sizeof(szTmpKeyName) );
sscanf( buf, "%[^=|^ |^\t]", szTmpKeyName );
if( strcmp(szTmpKeyName, szKeyName) == 0 ){
sscanf( ++c, "%[^\n]", szKeyVal );
char *szTmpKeyVal_o = (char *)malloc(strlen(szKeyVal) + 1);
if(szTmpKeyVal_o != NULL){
memset(szTmpKeyVal_o, 0, sizeof(szTmpKeyVal_o));
a_trim(szTmpKeyVal_o, szKeyVal);
if(szTmpKeyVal_o && strlen(szTmpKeyVal_o) > 0)
strcpy(szKeyVal, szTmpKeyVal_o);
free(szTmpKeyVal_o);
szTmpKeyVal_o = NULL;
}
found = 2;
break;
} else {
continue;
}
}
}
}
fclose( fp );
if( found == 2 )
return(1);
else
return(-1);
}
//---------------配置值读取函数(重载)---------
//-功能: 从配置文件中读取对应配置值,为int型重载.
//-Input: 1.配置文件中待读取的标签信息名
// 2.配置文件中指定标签中的待读取关键字名
// 3.配置文件中指定标签和关键字的对应配置值(int型指针)
//-Output: 相应配置值
//-Return: 如果读取成功返回1,错误返回-1
//--------------------------------------------
int GetConfigValue(char *szLabelInfoName, char *szKeyName, int *pnKeyVal )
{
char szTmpLabelInfoName[32],szTmpKeyName[32],szTmpKeyVal[32];
char *buf,*c;
char buf_i[KEYVALLEN], buf_o[KEYVALLEN];
FILE *fp;
int found=0; /* 1 szLabelInfoName 2 szKeyName */
if( (fp=fopen( CONFIG_FILE_PATH,"r" ))==NULL ){
printf( "openfile [%s] error [%s]\n",CONFIG_FILE_PATH,strerror(errno) );
return(-1);
}
fseek( fp, 0, SEEK_SET );
memset( szTmpLabelInfoName, 0, sizeof(szTmpLabelInfoName) );
sprintf( szTmpLabelInfoName,"[%s]", szLabelInfoName );
while( !feof(fp) && fgets( buf_i, KEYVALLEN, fp )!=NULL ){
l_trim(buf_o, buf_i);
if( strlen(buf_o) <= 0 )
continue;
buf = NULL;
buf = buf_o;
if( found == 0 ){
if( buf[0] != '[' ) {
continue;
} else if ( strncmp(buf,szTmpLabelInfoName,strlen(szTmpLabelInfoName))==0 ){
found = 1;
continue;
}
} else if( found == 1 ){
if( buf[0] == '#' ){
continue;
} else if ( buf[0] == '[' ) {
break;
} else {
if( (c = (char*)strchr(buf, '=')) == NULL )
continue;
memset( szTmpKeyName, 0, sizeof(szTmpKeyName) );
sscanf( buf, "%[^=|^ |^\t]", szTmpKeyName );
if( strcmp(szTmpKeyName, szKeyName) == 0 ){
sscanf( ++c, "%[^\n]", szTmpKeyVal );
char *szTmpKeyVal_o = (char *)malloc(strlen(szTmpKeyVal) + 1);
if(szTmpKeyVal_o != NULL){
memset(szTmpKeyVal_o, 0, sizeof(szTmpKeyVal_o));
a_trim(szTmpKeyVal_o, szTmpKeyVal);
if(szTmpKeyVal_o && strlen(szTmpKeyVal_o) > 0)
strcpy(szTmpKeyVal, szTmpKeyVal_o);
free(szTmpKeyVal_o);
szTmpKeyVal_o = NULL;
}
found = 2;
break;
} else {
continue;
}
}
}
}
*pnKeyVal = atoi(szTmpKeyVal);
fclose( fp );
if( found == 2 )
return(0);
else
return(-1);
}
//---------------配置文件作用函数--------------
//-功能: 从配置文件中读取配置,并使之生效.
//-Input: 无
//-Output: 相应配置值
//-Return: 如果成功返回1,错误返回-1
//--------------------------------------------
int LoadConfigFile()
{
bool bGetLogPath = false;
bool bGetLogLevel = false;
bool bGetMaxLogSize = false;
bool bGetLogReset = false;
int ret = 0;
ret = CheckConfigFileSizeTime();
if(ret != 1){
return ret;
}
//从配置文件中获取日志信息中的日志路径
if(GetConfigValue((char*)"Log_Info",(char*)"log_path",gconf_szLogPath) == -1){
PrintLOG(LOG_ERR,"获取日志路径失败!");
}
else
{
bGetLogPath = true;
//PrintLOG(LOG_INF,"日志路径为:%s",gconf_szLogPath);
}
//从配置文件中获取日志信息中的程序日志级别
if(GetConfigValue((char*)"Log_Info",(char*)"log_levle",&gconf_nLogLevel) == -1){
PrintLOG(LOG_ERR,"获取程序日志级别失败!");
}
else
{
bGetLogLevel = true;
//PrintLOG(LOG_INF,"程序日志级别为:%d",gconf_nLogLevel);
}
//从配置文件中获取日志信息中的日志大小上限
if(GetConfigValue((char*)"Log_Info",(char*)"log_max_size",&gconf_lMaxLogSize) == -1){
PrintLOG(LOG_ERR,"获取日志大小上限失败!");
}
else
{
bGetMaxLogSize = true;
//PrintLOG(LOG_INF,"日志大小上限为:%d",gconf_lMaxLogSize);
}
//从配置文件中获取日志信息中的日志是(1)否(0)清空
if(GetConfigValue((char*)"Log_Info",(char*)"log_reset",&gconf_nLogReset) == -1){
PrintLOG(LOG_ERR,"获取日志清空配置信息失败!");
}
else
{
bGetLogReset = true;
//PrintLOG(LOG_INF,"日志清空配置为:%d",gconf_nLogReset);
}
if(g_nCountLoadConfig == 0 &&
bGetCameraIP && bGetUserName && bGetCameraPassword && bGetCameraPtzTurn && bGetCameraPtzSpeed &&
bGetPtzTurnLevel && bGetAccuracyFrequency && bGetMaxPlateVectorSize && bGetIntervalOfClearPlateVector && bGetPlateResultPath &&
bGetLogPath && bGetLogLevel && bGetMaxLogSize && bGetLogReset){
g_nCountLoadConfig ++;
PrintLOG(LOG_INF,"全部配置信息均从配置文件获取.");
}
return 1;
}
//************[END]函数具体定义[END]****************
6. 配置文件作用方法
暂时是在主函数中设置循环,不断调用配置文件读取函数,使其不断生效后执行相关操作。
7. 配置文件config.ini内容
[Log_Info]
#日志路径,因为一般将结果保存在“result”文件夹中,所以要使用“result/***.log”的格式
log_path = result/testResult.log
#设置程序日志等级:1 表示打印所有(调试,一般,错误信息),0 表示打印运行(一般,错误)信息,-1 表示仅打印错误信息.
log_levle = 1
#设置程序日志大小上限(单位为bytes,0表示不设置),超过后日志清空,最大2147483647,建议不要超过104857600(100*1024*1024)
log_max_size = 104857600
#程序开始运行时,日志是(1)否(0)清空
log_reset = 1;