公司以前的项目中有的是用fscanf来读取INI文件,这会产生不少问题,键名部分重合便是一个不常出现,但可能出现的问题。
这里指的键名部分重合是指在同一节(Section)中,键名间出现类似如下情况:
NameAltitude=100
Sort=10
NameOffsetY=20
以上3个字段均是可选项,其中NameAltitude和NameOffsetY的第一个单词出现了重合。NameOffsetY是后来功能需要再添加上去。使用fscanf读取INI文件数据相关代码如下:
bool bSucRead = true;
// ...
// ...
// ...
// 读取必选键值-Begin
bSucRead &= fscanf(fp, "Name=%s\n", pInfo->szName);
bSucRead &= fscanf(fp, "SizeAdd=%d\n", &pInfo->nSizeAdd);
// ...
// ...
// ...
// 读取必选键值-End
// 读取可选键值-Begin
// ...
// ...
// ...
if (!fscanf(fp, "NameAltitude=%d\n", &pInfo->nNameAltitude))
{
pInfo->nNameAltitude = 0;
}
if (!fscanf(fp, "Sort=%d\n", &pInfo->nSort))
{
pInfo->nSort = 0;
}
if (!fscanf(fp, "NameOffsetY=%d\n", &pInfo->nNameOffsetY))
{
pInfo->nNameOffsetY = 0;
}
// ...
// ...
// ...
// 读取可选键值-End
当有NameOffsetY前面至少有NameAltitude存在时,如下:
[003]
Name=monster
SizeAdd=23
...
...
...
NameAltitude=123
Sort=14
NameOffsetY=20
或者
[022]
Name=monster
SizeAdd=23
...
...
...
NameAltitude=123
NameOffsetY=20
此时数据读取正常。
而当从NameAltitude到NameOffsetY之前的字段都不存在时,如下:
[041]
Name=monster
SizeAdd=23
...
...
...
NameOffsetY=25
NameOffsetY读取失败,pInfo->nNameOffsetY被赋予默认值0,而不是25。
当文件指针走到"NameOffsetY"前时,fscanf却在准备读取"NameAltitude",结果前面的"Name"被读取了,然而到后面才发现读取错误,但文件指针已经走到"Altitude"前了。fscanf继续往后判断,读取"NameOffsetY"必然是失败的,因此pInfo->nNameOffsetY被赋予默认值0。
解决方案一:使用GetPrivateProfileInt、GetPrivateProfileInt等WIN32 API函数来读取INI文件。
解决方案二:使用现有框架下的CMyIni读取INI文件。
解决方案三:在发现读取错误后,让文件指针退会正确位置,可使用ftell和fseek来实现:
// 调整文件指针位置
inline bool AdjustFilePtrPos(FILE* fp, long lPosBeforeOffset)
{
if (NULL != fp)
{
long lOffset = ftell(fp) - lPosBeforeOffset;
if (0 < lOffset)
{
fseek(fp, -lOffset, SEEK_CUR);
return true;
}
}
return false;
}
//....
//....
//....
long lPosBeforeOffset = ftell(fp); // 记录文件指针出错前位置
if (!fscanf(fp, "NameAltitude=%d\n", &pInfo->nNameAltitude))
{
pInfo->nNameAltitude = 0;
AdjustFilePtrPos(fp, lPosBeforeOffset); // 调整文件指针位置
}
//....
//....
//....