/*----------------------------------------------------------------------------------- 平台无关的INI文件读写函数 V1.65 张磊 2002.10.3 ----------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------- [编写及维护记录]: (1) V1.0 2002.8.13 仿照windows同类API的调用方式实现了其基本功能。 (2) V1.1 2002.9.13 修正了写配置文件函数中存在的两个BUG 1. 修改配置文件条目时出现部分乱码 2. 写配置文件时,文件尾部原先的超长部分没有被删掉,即没有按新的文件长度截断文件 (3) V1.2 2002.9.18 给QGetPrivateProfileInt()函数增加了16进制字符串识别功能 (4) V1.3 2002.9.19 修正了存在的两个BUG 1. 给StrTrimLeft, StrTrimRight两个函数增加删除TAB字符(ASC码为9)功能,这是由于Ini 文件中进行注释等操作时往往会用到TAB键 2. 从读取的一行文本中剔除";"即注释字符及其后面内容后,新增调用StrTrimAll()函数 删除剩下字符串右边往往存在的空格或TAB字符 (5) V1.4a 2002.9.20 按照一次将文件内容全部读入字符串再处理的算法对三个函数进行了优化 OpenPort()函数执行时间从原来的30秒降为16秒左右 (6) V1.41a 2002.9.23 对读函数继续优化,即连续对相同文件名进行读操作,第一次读文件 时将文件全部读入缓冲区,后面则直接从缓冲区中读取文件内容,不 再需要每次都读入文件全部内容,OpenPort()函数从16秒降到了14秒, 但这种方法和其它文件有些牵连,否则不能正确获知目标文件的变化而 重新装入,导致通用性变差, 最终决定不采用此算法,仍沿用打开-关闭模式 (7) V1.5 2002.9.23 执行速度有了极大提高,具体说明如下: 1. 重写了QGetPrivateProfileString()函数的段搜索部分, 算法为:直接在整个文件字符串 中查找段名,以快速定位段的位置,然后从该位置开始逐行读取,进行键的搜索。由于INI文 件中分段存储的本意就是根据段来快速定位键的位置,因此如果搜索段时也是逐行扫描, 且要找的段可能在INI文件尾部或不存在,将使效率将大大降低;另外对于键的搜索不能 采用这种直接查找字符串的办法,因为即使找到一个键也无法容易地判断它属于哪个段。 使用该算法后,OpenPort()函数执行时间骤降至3-4秒. 2. 由于前置机程序WritePrivateProfileString()函数的调用不超过10次,且都是在交互场合 下,对速度无严格要求,因此该函数中的段搜索及文件读写部分暂时没有优化。 (8) V1.6 2002.9.27 彻底实现了读取ini文件时平台文件格式无关性: 为保证提取一行的操作与平台文件格式无关,只查找换行符(0x0A),这种做法和fgets()类似 对于windows下的ini文件按此方法读出后还要去掉行字符串结尾的一个回车符(0x0D),这样 只查找0x0A,可保证windows下能正确访问unix格式的ini文件。 (9) V1.65 2002.10.3 针对provdata.ini读写时遇到的 1. 行内注释符在该文件中被特别定义成"//"而不是";" 2. 该配置文件中一行尾部有'\'续行符 因此做如下改动: 1. 增加续行功能(续行功能只会出现在键的读取上) 2. 给两个读函数增加两个参数,分别指定行内注释符(缺省为';')和续行符(缺省为'\') -----------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------- [使用说明]: (1) 因部分内容和操作系统相关,如换行符号,因此如在unix下运行要确保定义了OS_UNIX宏 (2) 采用全文件字符串搜索算法对INI文件格式有一个约束,既: 要求'['和']'必须和段名紧贴,之间不能留有空格。不允许INI文件中段名形式为"[ xxx ]", 只有"[xxx]"格式才能正确访问 -------------------------------------------------------------------------------------*/ #include "string.h" #include "stdio.h" #include "common.h" #include "inifile.h" #include //#include /*------------------------------------------------------------------------------------- 平台无关的INI文件读函数(字符串),和同类API相比,只是参数类型从const char*变成char* 返回值类型从DWORD改成int,这里返回值的意义从读取字节个数(同名API返回DWORD类型)变成 了读取成功与否标志(int类型),但并不影响使用 -------------------------------------------------------------------------------------*/ int QGetPrivateProfileString( const char* lpszSectionName, //段名 const char* lpszKeyName, //键名 const char* lpszDefault, //缺省字符串 char* lpszReturnedString, //结果字符串 u_32 nSize, //结果字符串长度 const char* lpszFileName, //ini文件名 const char* lpszRemarkInLineStr, //行内注释符 const char chContinueLineChar //续行符号 ) { FILE* IniFile = NULL; BOOL bSecFoundFlag = FALSE; BOOL bKeyFoundFlag = FALSE; BOOL bEOF = FALSE; char szLineBuf[MAX_LINE_BUF_LENGTH]; //行缓冲区 char szKeyItemBuf[MAX_LINE_BUF_LENGTH*4]; //最终内容缓冲区(因键所在行可能因使用续行符而占据多行) char *lpszFileText = NULL; //放置INI文件全部内容的字符串 char *lpszLineBegin = NULL; //一行开始指针 char *lpszTmp = NULL; i_32 lFileSize = 0; int iPos, iRetVal; BOOL bLineContinue = FALSE; //2002.10.3 续行标志 /*--------------------------------------------------------------------------------- char szLineEndStr[3]; //行末尾换行符号 memset(szLineEndStr, 0, 3); #ifdef OS_UNIX strcpy(szLineEndStr, "\n"); //UNIX文本文件行末只有一个换行符'\n' #else strcpy(szLineEndStr, "\r\n"); //Windows文本文件行末有一个回车符'\r',一个换行符'\n' #endif ----------------------------------------------------------------------------------*/ IniFile = fopen(lpszFileName, "rb"); //二进制可读方式打开(文件必须存在) if(!IniFile) { //qDebug("fopen error, %s", lpszFileName); return FILE_OPEN_ERROR; } fseek(IniFile, 0, SEEK_END); //定位到文件尾部 lFileSize = ftell(IniFile); //获取文件尾指针位置(该值=文件总长=文件指针位置最大值+1) lpszFileText = new char[lFileSize+1]; memset(lpszFileText, 0, lFileSize+1); fseek(IniFile, 0, SEEK_SET); //重新定位到开头 fread(lpszFileText, 1, lFileSize, IniFile); //将文件所有内容全部读入字符串 fclose(IniFile); //读入全部内容后立即关闭文件 lpszLineBegin = lpszFileText; //段搜索 /*---------------------------------------------------------------------------- //while(!feof(IniFile)) while(!bEOF) { //ReadLineFromFile(IniFile, szLineBuf, MAX_LINE_BUF_LENGTH); //读取一行 memset(szLineBuf, 0, MAX_LINE_BUF_LENGTH); lpszTmp = strstr(lpszLineBegin, szLineEndStr); //搜索换行符号来定位行末 if(lpszTmp != NULL) { strncpy(szLineBuf, lpszLineBegin, min(MAX_LINE_BUF_LENGTH-1, lpszTmp-lpszLineBegin)); lpszLineBegin = lpszTmp + strlen(szLineEndStr); } else //最后一行 { strncpy(szLineBuf, lpszLineBegin, min(MAX_LINE_BUF_LENGTH-1, lpszFileText+lFileSize-lpszLineBegin)); bEOF = TRUE; } StrTrimAll(szLineBuf); if( szLineBuf[0] == ';' || szLineBuf[0] == 0 ) continue; //注释行和空行不处理 //将一行尾部可能存在的";"注释部分剔除(这样key=value的value中不能含有;否则将被当成注释而删除 lpszTmp = strchr(szLineBuf, ';'); if(lpszTmp != NULL) *lpszTmp = 0; StrTrimAll(szLineBuf); //zl 2002.9.19 ";"和有用内容间必有空格或TAB字符,必须删除这些分割字符 iPos = strlen(szLineBuf) - 1; if(szLineBuf[0] == '[' && szLineBuf[iPos] == ']') //该行是一个段 { szLineBuf[iPos] = 0; //去掉"["和"]",便于比较 lpszTmp = &szLineBuf[1]; if( strcmp(lpszTmp, lpszSectionName) == 0 ) //指定的段找到了 { bSecFoundFlag = TRUE; break; } } } ----------------------------------------------------------------------------*/ //zl 2002.9.23 新的段搜索算法(直接在包含整个ini文件内容的字符串中定位段名) char szFindStr[MAX_LINE_BUF_LENGTH]; //搜索字符串 sprintf(szFindStr, "[%s]", lpszSectionName); while(!bEOF) { lpszTmp = strstr(lpszLineBegin, szFindStr); if( lpszTmp != NULL ) //找到段名,继续验证其是否合法 { lpszLineBegin = lpszTmp + strlen(szFindStr); //计算新的查找起始位置 if( lpszLineBegin > (lpszFileText + lFileSize -1) ) bEOF = TRUE; if( lpszTmp == lpszFileText ) //"[xxx]"是文件的开头内容,为合法段名 { bSecFoundFlag = TRUE; break; } /*---------------------------------------------------------------------------- 从'['向前查找,如果遇到换行符0xA,表明是合法段名,如果是空格或tab字符(ASC码9) 则继续向前搜索,如果是此外的其它任何字符,该段名都判为非法, ']'后面的内容不 予处理 ----------------------------------------------------------------------------*/ lpszTmp--; while( lpszTmp >= lpszFileText ) { if( *lpszTmp == 0xA ) { bSecFoundFlag = TRUE; break; } else if( *lpszTmp == ' ' || *lpszTmp == 9 ) lpszTmp--; else break; } if( bSecFoundFlag ) break; //退出外循环 } else break; //没找到段名,退出 } if( !bSecFoundFlag ) { iRetVal = SECTION_NOT_FOUND; goto func_end; } //继续键搜索(定位段后,对于所属键的查找为逐行读取扫描方式,键可能占用多行) while(!bEOF) { if( !bLineContinue ) memset(szKeyItemBuf, 0, sizeof(szKeyItemBuf)); //关键,控制多行连接 memset(szLineBuf, 0, MAX_LINE_BUF_LENGTH); //zl 2002.9.27 为保证提取一行的操作与平台文件格式无关,只查找换行符(0x0A),这和fgets()类似 lpszTmp = strchr(lpszLineBegin, 0x0A); if(lpszTmp != NULL) { strncpy(szLineBuf, lpszLineBegin, min(MAX_LINE_BUF_LENGTH-1, lpszTmp-lpszLineBegin)); //从字符串中提取一行 /* 读windows文本文件时,每行尾部多一个回车符(0x0D),必须去掉,否则后面连续出错 */ if( szLineBuf[strlen(szLineBuf)-1] == 0x0D ) szLineBuf[strlen(szLineBuf)-1] = 0; lpszLineBegin = lpszTmp + 1; //1表示"0x0A"一个字符 } else //最后一行 { strncpy(szLineBuf, lpszLineBegin, min(MAX_LINE_BUF_LENGTH-1, lpszFileText+lFileSize-lpszLineBegin)); bEOF = TRUE; } StrTrimAll(szLineBuf); //全注释行和空行不处理 if( szLineBuf[0] == ';' || szLineBuf[0] == 0 ) { if( bLineContinue && strlen(szKeyItemBuf) > 0 ) //2002.10.7 如果前面是续行,则继续处理szKeyItemBuf已有内容而不是continue重新读入一行,否则当前行(空行或注释行)上面的一个续行的全部内容将丢失 bLineContinue = FALSE; else { bLineContinue = FALSE; continue; } } else { //2002.10.3 判断续行,续行符为行末最后的一个非空格或TAB字符,缺省为'\' if( szLineBuf[strlen(szLineBuf)-1] == chContinueLineChar ) { bLineContinue = TRUE; szLineBuf[strlen(szLineBuf)-1] = 0; //去掉续行符 StrTrimAll(szLineBuf); strcat(szKeyItemBuf, szLineBuf); continue; } else //无续行字符行 { bLineContinue = FALSE; strcat(szKeyItemBuf, szLineBuf); } } //将一行尾部可能存在的行内注释部分(缺省行内注释符是";")剔除 //lpszTmp = strchr(szLineBuf, ';'); lpszTmp = strstr(szKeyItemBuf, lpszRemarkInLineStr); //2002.10.3 可以任意指定行内注释符 if(lpszTmp != NULL) *lpszTmp = 0; StrTrimAll(szKeyItemBuf); //zl 2002.9.19 ";"和有用内容间必有空格或TAB字符,必须删除这些分割字符 iPos = strlen(szKeyItemBuf)-1; if(szKeyItemBuf[0] == '[' && szKeyItemBuf[iPos] == ']') //又到了一个段,说明指定段下的指定key没有找到 { iRetVal = KEY_NOT_FOUND; goto func_end; } lpszTmp = strchr(szKeyItemBuf, '='); //定位'=' if(lpszTmp == NULL) continue; //不含等号的非段行被视为无效行 *lpszTmp = 0; //为便于操作,在'='位置将szLineBuf从中间截断, 将szLineBuf分成key和value两个部分 StrTrimAll(szKeyItemBuf); //此时szLineBuf是key if(strcmp(szKeyItemBuf, lpszKeyName) == 0) //找到了指定key { lpszTmp++; StrTrimAll(lpszTmp); //lpszTmp++是key对应的value if( strlen(lpszTmp) < nSize ) strcpy(lpszReturnedString, lpszTmp); //返回key对应的value else strncpy(lpszReturnedString, lpszTmp, strlen(lpszTmp)); iRetVal = 1; //1表示正确 bKeyFoundFlag = TRUE; break; } } if( !bKeyFoundFlag ) iRetVal = KEY_NOT_FOUND; func_end: delete [] lpszFileText; //如果出错同时缺省参数不为NULL,则返回缺省参数 if( iRetVal < 0 && lpszDefault != NULL ) { if( strlen(lpszDefault) < nSize ) strcpy(lpszReturnedString, lpszDefault); else strncpy(lpszReturnedString, lpszDefault, strlen(lpszDefault)); } return iRetVal; } /*------------------------------------------------------------------------------------- 平台无关的INI文件读函数(整数),和同类API相比,只是参数类型从const char*变成char*, 返回值类型从UINT改成int -------------------------------------------------------------------------------------*/ int QGetPrivateProfileInt( const char* lpszSectionName, //段名 const char* lpszKeyName, //键名 int nDefault, //缺省值 const char* lpszFileName, //ini文件名 const char* lpszRemarkInLineStr, //行内注释符 const char chContinueLineChar //续行符号 ) { int iRet, iResult; char szNumStr[256]; //数字字符串 iRet = QGetPrivateProfileString( lpszSectionName, //段名 lpszKeyName, //键名 NULL, //缺省字符串 szNumStr, //结果字符串 256, //结果字符串长度 lpszFileName, //INI文件名 lpszRemarkInLineStr, //行内注释符 chContinueLineChar //续行符号 ); //qDebug(lpszFileName); if(iRet < 0) return nDefault; //出错则返回缺省值 StrTrimAll(szNumStr); /*-------------------------------------------------------------------------------- 因原先未考虑ini中用"0x1801"这种16进制字符串,导致转换时出错,因为atoi不认识16进制串 2002.9.18 加入非十进制字符串识别支持,目前只支持16进制,字符串格式为"0x数字" --------------------------------------------------------------------------------*/ BOOL bIsHexFlag = FALSE; if( strlen(szNumStr) > 2 ) { if( szNumStr[0] == '0' && szNumStr[1] == 'x' ) bIsHexFlag = TRUE; } if( bIsHexFlag ) iRet = sscanf(&szNumStr[2], "%x", &iResult); //16进制 else iRet = sscanf(szNumStr, "%d", &iResult); //10进制 if( iRet != 1 ) //如数字字符串合法的话,应只转换一个field,故返回值不为1表示出错,此时返回缺省值 return nDefault; else return iResult; //正确返回 } /*------------------------------------------------------------------------------------- 平台无关的INI文件写函数,和同类API相比,只是参数类型从const char*改成了char* 目前该函数尚未对段搜索部分进行优化 -------------------------------------------------------------------------------------*/ BOOL QWritePrivateProfileString( const char* lpszSectionName, //段名 const char* lpszKeyName, //键名 const char* lpszString, //要写入的字符串 const char* lpszFileName //INI文件名 ) { FILE* IniFile; char szLineBuf[MAX_LINE_BUF_LENGTH]; char *lpszFrontBlock = NULL, *lpszRearBlock = NULL; //装入前后两块文件各自所占的内存区指针 char *lpszTmp = NULL; i_32 lPrePos = 0, lCurPos = 0; //前一次读取及本次读取的文件指针位置 bool bSecFoundFlag = FALSE; bool bKeyFoundFlag = FALSE; bool bMeetAnotherBlock = FALSE; //遇到另外一个段 i_32 lEndPos; int iPos; char szLineEndStr[3]; //行末尾换行符号 #ifdef OS_UNIX strcpy(szLineEndStr, "\n"); //UNIX文本文件行末只有一个换行符'\n' #else strcpy(szLineEndStr, "\r\n"); //Windows文本文件行末有一个回车符'\r',一个换行符'\n' #endif IniFile = fopen(lpszFileName, "r+b"); //二进制方式打开,可读可写(但文件必须存在) if(!IniFile) return false; fseek(IniFile, 0, SEEK_END); //定位到文件尾部 lEndPos = ftell(IniFile); //计算文件尾部指针位置(该值=文件总长=文件指针位置最大值+1) fseek(IniFile, 0, SEEK_SET); //重新定位到开头 //先找指定的section while(!feof(IniFile)) { lPrePos = lCurPos; //保留上次文件指针位置 ReadLineFromFile(IniFile, szLineBuf, MAX_LINE_BUF_LENGTH); //读取一行 lCurPos = ftell(IniFile); //当前文件指针位置 StrTrimAll(szLineBuf); if( szLineBuf[0] == ';' || szLineBuf[0] == 0 ) continue; //注释行和空行不处理 //将一行尾部可能存在的";"注释部分剔除(这样key=value的value中不能含有;否则将被当成注释而删除 lpszTmp = strchr(szLineBuf, ';'); if(lpszTmp != NULL) *lpszTmp = 0; StrTrimAll(szLineBuf); //zl 2002.9.19 ";"和有用内容间必有空格或TAB字符,必须删除这些分割字符 iPos = strlen(szLineBuf) - 1; if(szLineBuf[0] == '[' && szLineBuf[iPos] == ']') //该行是一个段 { szLineBuf[iPos] = 0; //去掉"["和"]",便于比较 lpszTmp = &szLineBuf[1]; if( strcmp(lpszTmp, lpszSectionName) == 0 ) //指定的段找到了 { bSecFoundFlag = TRUE; break; } } } //[情况一]:如果未找到指定段,则指定key肯定也不存在,因此即要新建section也要新建key, // 这些内容就直接追加到文件尾部,同时约定在文件尾部新建一个段时,默认在前面 // 追加换行符号以确保和前面的其它内容不在一行 if( !bSecFoundFlag ) { char szNewSection[2048]; memset(szNewSection, 0, 2048); //生成新的段字符串 sprintf(szNewSection, "%s[%s]%s%s = %s%s", szLineEndStr, lpszSectionName, szLineEndStr, lpszKeyName, lpszString, szLineEndStr); fseek(IniFile, 0, SEEK_END); fwrite(szNewSection, sizeof(char), strlen(szNewSection), IniFile); goto func_end; } //找到指定段后,再继续找指定key while(!feof(IniFile)) { lPrePos = lCurPos; //保留上次文件指针位置 ReadLineFromFile(IniFile, szLineBuf, MAX_LINE_BUF_LENGTH); //读取一行 lCurPos = ftell(IniFile); //保留当前文件指针位置 StrTrimAll(szLineBuf); if( szLineBuf[0] == ';' || szLineBuf[0] == 0 ) continue; //注释行和空行不处理 //将一行尾部可能存在的";"注释部分剔除(这样key=value的value中不能含有;否则将被当成注释而删除 lpszTmp = strchr(szLineBuf, ';'); if(lpszTmp != NULL) *lpszTmp = 0; StrTrimAll(szLineBuf); //zl 2002.9.19 ";"和有用内容间必有空格或TAB字符,必须删除这些分割字符 iPos = strlen(szLineBuf)-1; if(szLineBuf[0] == '[' && szLineBuf[iPos] == ']') { bMeetAnotherBlock = TRUE; //又到了一个段,说明指定段下的指定key没有找到 break; } lpszTmp = strchr(szLineBuf, '='); //定位'=' if(lpszTmp == NULL) continue; //不含等号的非段行被视为无效行 *lpszTmp = 0; //为便于操作,在'='位置将szLineBuf从中间截断, 将szLineBuf分成key和value两个部分 StrTrimAll(szLineBuf); //此时szLineBuf是key /* 找到了指定key */ if(strcmp(szLineBuf, lpszKeyName) == 0) //该比较大小写敏感 { char szKeyValBlock[2048]; memset(szKeyValBlock, 0, 2048); //重新组织key = value对,实现修改目的 sprintf(szKeyValBlock, "%s = %s%s", szLineBuf, lpszString, szLineEndStr); //注意:这里和下面的段前插入内容不同,这里是要将原有的key=value对去掉, //代之以新对,因此lPrePos和lCurPos之间的文件内容(即原有key=value对)不要, //该操作是替换而不是插入 lpszRearBlock = new char[lEndPos-lCurPos+1]; memset(lpszRearBlock, 0, lEndPos-lCurPos+1); fread(lpszRearBlock, sizeof(char), lEndPos-lCurPos, IniFile); //从当前位置开始读其后面的文件内容(长度为lEndPos-lCurPos, 注意lEndPos是end结尾符号的位置,等于文件长度,比最大有效文件指针位置大1) lpszFrontBlock = new char[lPrePos+1]; memset(lpszFrontBlock, 0, lPrePos+1); fseek(IniFile, 0, SEEK_SET); fread(lpszFrontBlock, sizeof(char), lPrePos, IniFile); //从头部开始读其后面的文件内容(长度为lPrePos,因为文件指针从0开始,lPrePos位置前的文件长度就是lPrePos) //先关闭文件再用可写方式打开以清空原有内容,最后依次写文件的各个部分,如要不关闭 //打开文件而是直接清空文件内容,因unix和windows中设置文件长度的调用不同,比较麻烦 fclose(IniFile); IniFile = fopen(lpszFileName, "wb"); //可写方式打开(如文件已存在,其所有内容被清空) if(!IniFile) return FALSE; fwrite(lpszFrontBlock, sizeof(char), lPrePos, IniFile); fwrite(szKeyValBlock, sizeof(char), strlen(szKeyValBlock), IniFile); fwrite(lpszRearBlock, sizeof(char), lEndPos-lCurPos, IniFile); delete [] lpszFrontBlock; delete [] lpszRearBlock; bKeyFoundFlag = TRUE; break; } } //如果在指定段下未找到指定key,则要新建key=value对 if( !bKeyFoundFlag ) { char szKeyBlock[2048]; memset(szKeyBlock, 0, 2048); if(bMeetAnotherBlock) //遇到了另外一个段,此时要在该段前面插入新建的key=value对 { sprintf(szKeyBlock, "%s = %s%s", lpszKeyName, lpszString, szLineEndStr); //以下以lPrePos为界线将文件拆成两部分,和上面替换操作中lPrePos和lCurPos间内容 //不要不同,这里是插入而不是替换, 因此直接以lPrePos为界将文件分开,进行插入操作 fseek(IniFile, lPrePos, SEEK_SET); //定位到lPrePos(这里lPrePos是另一个段的开始字符,如"[") lpszRearBlock = new char[lEndPos-lPrePos+1]; memset(lpszRearBlock, 0, lEndPos-lPrePos+1); fread(lpszRearBlock, sizeof(char), lEndPos-lPrePos, IniFile); //从当前位置开始读其后面的文件内容(长度为lEndPos-lPrePos) lpszFrontBlock = new char[lPrePos+1]; memset(lpszFrontBlock, 0, lPrePos+1); fseek(IniFile, 0, SEEK_SET); fread(lpszFrontBlock, sizeof(char), lPrePos, IniFile); //从头部开始读其后面的文件内容(长度为lPrePos) //先关闭文件再用可写方式打开以清空原有内容,最后依次写文件的各个部分,如要不关闭 //打开文件而是直接清空文件内容,因unix和windows中设置文件长度的调用不同,比较麻烦 fclose(IniFile); IniFile = fopen(lpszFileName, "wb"); //可写方式打开(如文件已存在,其所有内容被清空) if(!IniFile) return FALSE; fwrite(lpszFrontBlock, sizeof(char), lPrePos, IniFile); fwrite(szKeyBlock, sizeof(char), strlen(szKeyBlock), IniFile); fwrite(lpszRearBlock, sizeof(char), lEndPos-lPrePos, IniFile); delete [] lpszFrontBlock; delete [] lpszRearBlock; } else //如果连一个段都没有碰到,则肯定已到了文件尾部, 追加key=value块到尾部即可 { fseek(IniFile, 0, SEEK_END); /* 如当前位置前已有换行符号则无需另行增加,此举可防止新增部分和上面一行连在一起 */ if( IfBeforeHasRet(IniFile) ) sprintf(szKeyBlock, "%s = %s%s", lpszKeyName, lpszString, szLineEndStr); else sprintf(szKeyBlock, "%s%s = %s%s", szLineEndStr, lpszKeyName, lpszString, szLineEndStr); fwrite(szKeyBlock, sizeof(char), strlen(szKeyBlock), IniFile); } } func_end: fclose(IniFile); return true; } //辅助函数,判断文件中当前指针位置前面是否有换行符号,使用该函数前必须确保文件已经打开并将文件指针定位到应有的位置 bool IfBeforeHasRet(FILE* pFile) { char szTmp[3]; bool bBeforeHasRet = FALSE; i_32 lOldPos; memset(szTmp, 0, 3); lOldPos = ftell(pFile); //保存文件当前指针位置 /* 读当前位置前面的两个字节, 因为windows下是回车换行两个字节 */ fseek(pFile, -2, SEEK_CUR); fread(szTmp, sizeof(char), 2, pFile); #ifdef OS_UNIX if( szTmp[1] == 0x0a ) bBeforeHasRet = TRUE; #else if( szTmp[0] == 0x0d && szTmp[1] == 0x0a ) bBeforeHasRet = TRUE; #endif fseek(pFile, lOldPos, SEEK_SET); //恢复原先的文件指针位置 return bBeforeHasRet; } /*-----------------------------------工具函数----------------------------------*/ //从指定文件中读取一行 int ReadLineFromFile(FILE* IniFile, char* lpszLineBuf, int iLen) { int iPos; /*----------------------------------------------------------------------------- 注意:如果文件以t即text方式打开,则回车换行两个字符在input时被当作一个字符处理, 因此fgets()得到的一行字符串尾部只有一个换行符而没有回车符,而如果是binary 方式打开则一行字符尾部同时有回车换行两个字符,打开方式的不同直接影响fgets 读入一行后删除行尾部控制字符的操作,如果是text方式,只要删除换行符号即可, 如果是binary方式,则要删掉两个控制字符。以下对两个控制字符是否存在都进行 判断,可以同时适应text和binary两种模式,也就能同时适应Windows下和Unix两种 不同格式的文本文件(unix文本文件行末只有一个换行符,即0A,没有回车符) 后续记录: fopen()中打开模式里的"t"是微软自己定义的,Ansi C 只支持 "b"模式 ----------------------------------------------------------------------------*/ if(fgets(lpszLineBuf, iLen, IniFile) != NULL) //fgets()函数遇到第一个换行符(0x0A)后结束(包括换行符),然后在尾部追加一个NULL后返回,因此换行符总是倒数第二个字符 { iPos = strlen(lpszLineBuf) - 1; if( lpszLineBuf[iPos-1] == 0x0D ) lpszLineBuf[iPos-1] = 0; //删除尾部可能存在的回车符(0x0D) if( lpszLineBuf[iPos] == 0x0A ) lpszLineBuf[iPos] = 0; //删除尾部可能存在的换行符(0x0A) } else return -1; return 0; //0表示正确 } /*---------------------------------------------------------------------------- //从指定字符串中读取一行 int ReadLineFromString(char* lpszText, char* lpszLineBuf, int iLen) { int iPos; if(fgets(lpszLineBuf, iLen, IniFile) != NULL) //fgets()函数遇到第一个换行符(0x0A)后结束(包括换行符),然后在尾部追加一个NULL后返回,因此换行符总是倒数第二个字符 { iPos = strlen(lpszLineBuf) - 1; if( lpszLineBuf[iPos-1] == 0x0D ) lpszLineBuf[iPos-1] = 0; //删除尾部可能存在的回车符(0x0D) if( lpszLineBuf[iPos] == 0x0A ) lpszLineBuf[iPos] = 0; //删除尾部可能存在的换行符(0x0A) } else return -1; return 0; //0表示正确 } ---------------------------------------------------------------------------*/ //删除字符串的左侧空格, 2002.9.19增加删除TAB字符(ASC码为9)功能 void StrTrimLeft(char* szBuf) { char *szBak = NULL; char *p = szBuf; if(!szBuf) return; while(*p == ' ' || *p == 9) { p++; } //qDebug("StrtrimLeft:p=%s",p); if(p != szBuf) //头部有空格或TAB字符 { szBak = _strdup(p); //strcpy()中如source和dest重合(overlap),则copy结果不可预料,故另设一个缓冲区临时存放这个头部没有空格的字符串 strcpy(szBuf, szBak); //strcpy()肯定不会溢出,因p是szBuf的一个子集,同时strcpy会拷贝结尾的null,因此无须人工截断szBuf free(szBak); } } //删除字符串的右侧空格, 2002.9.19增加删除TAB字符(ASC码为9)功能 void StrTrimRight(char* szBuf) { char *szBak = NULL; int iPos = strlen(szBuf)-1; //最后一个非NULL字符的下标 if(!szBuf) return; char *p = &szBuf[iPos]; while(*p == ' ' || *p == 9) { p--; } if(p != &szBuf[iPos]) //尾部有空格 { p++; *p = 0; //直接尾部截断,比删除头部空格还简单 } } //删除左右两边所有空格 void StrTrimAll(char* szBuf) { StrTrimRight(szBuf); StrTrimLeft(szBuf); } //获取指定文件的长度 i_32 GetFileSize(char* lpszFileName) { FILE* pFile; pFile = fopen(lpszFileName, "rb"); //二进制方式 if(pFile == NULL) return -1; fseek(pFile, 0, SEEK_END); //定位到文件尾部 i_32 lFileSize = ftell(pFile); //获取文件指针当前位置(此时文件指针指向EOF结束标志)[文件指针是从0开始计数的] fclose(pFile); return lFileSize; }