在上篇文章开发 windows mobile 上的今日插件时,我发现 wince 平台上不支持例如 GetPrivateProfileString 等相关 API 函数。在网络上我并没有找到令我满意的相应代码,因此我手工自己写了相应的方法。命名规则是,在 PC API 函数的名称前面加上 “Ce” 前缀,这是为了在 PC 平台上调试和使用时,不和系统的 API 函数发生冲突。值得注意的是,在写 CeWritePrivateProfileString 方法时,如果改写后的 ini 文件应该比改写前的文件小,文件尾部将会是一些不确定内容(来自于原来文件)。在 PC 上我们可以通过 中的 _chsize 函数重新设置文件大小,但是很遗憾的是,这些底层的文件操作函数在 wince 平台上依然不被支持,但是幸运的是,可以使用 coredll.dll 中提供的 SetEndOfFile 函数去完成相同功能(感谢88上的 kghost 的提示)。
在上篇文章开发 windows mobile 上的今日插件时,我发现 wince 平台上不支持例如 GetPrivateProfileString 等相关 API 函数。在网络上我并没有找到令我满意的相应代码,因此我手工自己写了相应的方法。命名规则是,在 PC API 函数的名称前面加上 “Ce” 前缀,这是为了在 PC 平台上调试和使用时,不和系统的 API 函数发生冲突。值得注意的是,在写 CeWritePrivateProfileString 方法时,如果改写后的 ini 文件应该比改写前的文件小,文件尾部将会是一些不确定内容(来自于原来文件)。在 PC 上我们可以通过 <io.h> 中的 _chsize 函数重新设置文件大小,但是很遗憾的是,这些底层的文件操作函数在 wince 平台上依然不被支持,但是幸运的是,可以使用 coredll.dll 中提供的 SetEndOfFile 函数去完成相同功能(感谢88上的 kghost 的提示)。
另外我额外提供了一个函数:CeGetPrivateProfileKeyNames,用于读取某个 section 的所有 key 名称。
当然,如果是在 PC 平台,我们就没有必要使用这里我所提供的代码,因为有系统 API 可以调用。
需要注意的是,我提供的代码和 PC 端 API 相比,基本功能,参数意义完全相同,但具有以下一些额外要求:
(1)大小写敏感。(当然也可以通过修改代码,令其大小写不敏感)
(2)每一行,section, key, value, “=” 的前后不允许有空格。
(3)注释行用英文分号“;"起始。允许存在空行。
(4)每一行的字符数不能超过 260 字符(取决于代码中的宏定义)。
(5)函数代码同时适用 unicode 和多字节字符串 的环境。
(6)由于采用标准文件操作函数,因此 CeGetPrivateProfileSectionNames 函数并不保证原子性。(这一点和 PC API 不同)
下面是相关函数代码:
(a) IniFile.h
=============================
...
nSectionBegin -> [section2]
...
nKeyBegin -> key2=value2
nNextKey -> ...
...
nNextSection -> [otherSection]
...
=============================
其他文件指针的含义是:nInsertPos - 新的KEY=Value开始写入位置; nCopyPos - 文件的后半部分在原始文件中的位置(整体不需要改写,但可能需要前移或后移),从这里到文件结尾的内容会在改写ini文件之前拷贝到内存,改写KEY后,再写回文件并附加到文件尾部。
上面的代码中,包含 StdAfx.h 通常是因为默认设定,如果取消预编译头的选项,则可以不包含它。
然后我们可以很方便对上面的代码进行测试:
另外我额外提供了一个函数:CeGetPrivateProfileKeyNames,用于读取某个 section 的所有 key 名称。
当然,如果是在 PC 平台,我们就没有必要使用这里我所提供的代码,因为有系统 API 可以调用。
需要注意的是,我提供的代码和 PC 端 API 相比,基本功能,参数意义完全相同,但具有以下一些额外要求:
(1)大小写敏感。(当然也可以通过修改代码,令其大小写不敏感)
(2)每一行,section, key, value, “=” 的前后不允许有空格。
(3)注释行用英文分号“;"起始。允许存在空行。
(4)每一行的字符数不能超过 260 字符(取决于代码中的宏定义)。
(5)函数代码同时适用 unicode 和多字节字符串 的环境。
(6)由于采用标准文件操作函数,因此 CeGetPrivateProfileSectionNames 函数并不保证原子性。(这一点和 PC API 不同)
下面是相关函数代码:
(a) IniFile.h
IniFile.h
/***************************************
* IniFile.h
* 说明:在WinCe平台读写 INI 文件
* by hoodlum1980
* 2009.08.03
***************************************/
#ifndef __INIFILE_H_BY_HOODLUM1980
#define __INIFILE_H_BY_HOODLUM1980
//是否在WINCE平台上
#ifndef WINCE
#define WINCE
#endif
#include "StdAfx.h"
#ifndef WINCE
#include <io.h> //for _sopen
#include <fcntl.h> //for _O_RDWT
#include <share.h> // for _SH_DENYRW
#endif
#ifdef UNICODE // r_winnt
#define t_sopen _wsopen //注意WinCe上不支持!
#define t_fopen _wfopen
#define t_fgets fgetws
#define t_fprintf fwprintf //文件格式化写入
#define t_sprintf swprintf //格式化文本
#define t_strcpy wcscpy
#define t_strncpy wcsncpy //拷贝指定个数的字符
#define t_strcat wcscat //append a string
#define t_strtol wcstol
#define t_strlen wcslen
#define t_strcmp wcscmp
#define t_stricmp _wcsicmp //忽略大小写的字符串比较
#define t_strncmp wcsncmp //比较n个字符
#define t_strchr wcschr //find a character in a string
#define t_strrchr wcsrchr //从结尾向前查找字符
#else //ASCII CODE
#define t_sopen _sopen //注意WinCe上不支持!
#define t_fopen fopen
#define t_fgets fgets //读取一行文本
#define t_fprintf fprintf //文件格式化写入
#define t_sprintf sprintf //格式化文本
#define t_strcpy strcpy
#define t_strncpy strncpy //拷贝指定个数的字符
#define t_strcat strcat //append a string
#define t_strtol strtol //把字符串转换成long(int32)
#define t_strlen strlen
#define t_strcmp strcmp //比较字符串
#define t_stricmp _stricmp //忽略大小写的字符串比较
#define t_strncmp strncmp //比较n个字符
#define t_strchr strchr //查找字符
#define t_strrchr strrchr //从结尾向前查找字符
#endif
//CeWritePrivateProfileString 方法用到的辅助标记
#define MODE_DELETE_SECTION 11
#define MODE_OVERWRITE_SECTION 12
#define MODE_APPEND_SECTION 13
#define MODE_DELETE_KEY 21
#define MODE_OVERWRITE_KEY 22
#define MODE_APPEND_KEY 23
#define LINESIZE 260 //行缓冲区大小
DWORD CeGetPrivateProfileString(
LPCTSTR lpAppName, //section name: [lpAppName]
LPCTSTR lpKeyName, //lpKeyName=lpReturnedString
LPCTSTR lpDefault, //未找到时的默认值
LPTSTR lpReturnedString, //[out] 查找到的结果
DWORD nSize, //[in]lpReturnedString的字符数,注意单位不是字节!
LPCTSTR lpFileName
);
UINT CeGetPrivateProfileInt(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
int nDefault,
LPCTSTR lpFileName
);
DWORD CeGetPrivateProfileSection(
LPCTSTR lpAppName,
LPTSTR lpReturnedString,
DWORD nSize,
LPCTSTR lpFileName
);
DWORD CeGetPrivateProfileSectionNames(
LPTSTR lpszReturnBuffer,
DWORD nSize,
LPCTSTR lpFileName
);
//在PC平台上可以调用_chsize函数调整文件大小,但是在WINCE平台上
//由于不支持,所以必须注意当文件尺寸应该缩小时,文件尾部内容不确定!!!!
BOOL CeWritePrivateProfileString(
LPCTSTR lpAppName,
LPCTSTR lpKeyName, //要修改的KEY,如果为NULL,会删除整个Section
LPCTSTR lpString, //要写入的值,如果为NULL,则会删除这个KEY
LPCTSTR lpFileName
);
//重写某个Section,注意和 PC API 的区别是,这里不保证原子性操作
BOOL CeWritePrivateProfileSection(
LPCTSTR lpAppName, //section name
LPCTSTR lpString, //key1=val1 \0 key2=val2 \0\0
LPCTSTR lpFileName
);
//==============================================
// 以下是我增加的函数(在API中没有)
//==============================================
DWORD CeGetPrivateProfileKeyNames(
LPCTSTR lpAppName,
LPTSTR lpReturnedString,
DWORD nSize, //缓冲区的字符数
LPCTSTR lpFileName
);
#endif
(b) IniFile.cpp/***************************************
* IniFile.h
* 说明:在WinCe平台读写 INI 文件
* by hoodlum1980
* 2009.08.03
***************************************/
#ifndef __INIFILE_H_BY_HOODLUM1980
#define __INIFILE_H_BY_HOODLUM1980
//是否在WINCE平台上
#ifndef WINCE
#define WINCE
#endif
#include "StdAfx.h"
#ifndef WINCE
#include <io.h> //for _sopen
#include <fcntl.h> //for _O_RDWT
#include <share.h> // for _SH_DENYRW
#endif
#ifdef UNICODE // r_winnt
#define t_sopen _wsopen //注意WinCe上不支持!
#define t_fopen _wfopen
#define t_fgets fgetws
#define t_fprintf fwprintf //文件格式化写入
#define t_sprintf swprintf //格式化文本
#define t_strcpy wcscpy
#define t_strncpy wcsncpy //拷贝指定个数的字符
#define t_strcat wcscat //append a string
#define t_strtol wcstol
#define t_strlen wcslen
#define t_strcmp wcscmp
#define t_stricmp _wcsicmp //忽略大小写的字符串比较
#define t_strncmp wcsncmp //比较n个字符
#define t_strchr wcschr //find a character in a string
#define t_strrchr wcsrchr //从结尾向前查找字符
#else //ASCII CODE
#define t_sopen _sopen //注意WinCe上不支持!
#define t_fopen fopen
#define t_fgets fgets //读取一行文本
#define t_fprintf fprintf //文件格式化写入
#define t_sprintf sprintf //格式化文本
#define t_strcpy strcpy
#define t_strncpy strncpy //拷贝指定个数的字符
#define t_strcat strcat //append a string
#define t_strtol strtol //把字符串转换成long(int32)
#define t_strlen strlen
#define t_strcmp strcmp //比较字符串
#define t_stricmp _stricmp //忽略大小写的字符串比较
#define t_strncmp strncmp //比较n个字符
#define t_strchr strchr //查找字符
#define t_strrchr strrchr //从结尾向前查找字符
#endif
//CeWritePrivateProfileString 方法用到的辅助标记
#define MODE_DELETE_SECTION 11
#define MODE_OVERWRITE_SECTION 12
#define MODE_APPEND_SECTION 13
#define MODE_DELETE_KEY 21
#define MODE_OVERWRITE_KEY 22
#define MODE_APPEND_KEY 23
#define LINESIZE 260 //行缓冲区大小
DWORD CeGetPrivateProfileString(
LPCTSTR lpAppName, //section name: [lpAppName]
LPCTSTR lpKeyName, //lpKeyName=lpReturnedString
LPCTSTR lpDefault, //未找到时的默认值
LPTSTR lpReturnedString, //[out] 查找到的结果
DWORD nSize, //[in]lpReturnedString的字符数,注意单位不是字节!
LPCTSTR lpFileName
);
UINT CeGetPrivateProfileInt(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
int nDefault,
LPCTSTR lpFileName
);
DWORD CeGetPrivateProfileSection(
LPCTSTR lpAppName,
LPTSTR lpReturnedString,
DWORD nSize,
LPCTSTR lpFileName
);
DWORD CeGetPrivateProfileSectionNames(
LPTSTR lpszReturnBuffer,
DWORD nSize,
LPCTSTR lpFileName
);
//在PC平台上可以调用_chsize函数调整文件大小,但是在WINCE平台上
//由于不支持,所以必须注意当文件尺寸应该缩小时,文件尾部内容不确定!!!!
BOOL CeWritePrivateProfileString(
LPCTSTR lpAppName,
LPCTSTR lpKeyName, //要修改的KEY,如果为NULL,会删除整个Section
LPCTSTR lpString, //要写入的值,如果为NULL,则会删除这个KEY
LPCTSTR lpFileName
);
//重写某个Section,注意和 PC API 的区别是,这里不保证原子性操作
BOOL CeWritePrivateProfileSection(
LPCTSTR lpAppName, //section name
LPCTSTR lpString, //key1=val1 \0 key2=val2 \0\0
LPCTSTR lpFileName
);
//==============================================
// 以下是我增加的函数(在API中没有)
//==============================================
DWORD CeGetPrivateProfileKeyNames(
LPCTSTR lpAppName,
LPTSTR lpReturnedString,
DWORD nSize, //缓冲区的字符数
LPCTSTR lpFileName
);
#endif
IniFile.cpp
//适用于 char* 和 UNICODE,
//所有字符串必须使用 TEXT("aa") 或者 _T("aa") 的格式(自动适应 char* 或 UNICODE)
//所有相关函数加t_前缀
//IniFile: 读取INI FILE的简单解析!所谓简单,也就是解析代码简单,但对文件格式要求更高
//[1]任何字符串前后不要有空格(使解析代码可以不考虑前后的trim)
// 例如允许"Key1=Val", 而不允许" Key1 = Val "
//[2]允许有注释,第一个字符必须是英文分号';'
//
#include "StdAfx.h"
#include "IniFile.h"
//从appname(section)中读取string类型key
DWORD CeGetPrivateProfileString(
LPCTSTR lpAppName, //section name: [lpAppName]
LPCTSTR lpKeyName, //lpKeyName=lpReturnedString
LPCTSTR lpDefault, //未找到时的默认值
LPTSTR lpReturnedString, //[out] 查找到的结果
DWORD nSize, //[in]lpReturnedString的字符数,注意单位不是字节!
LPCTSTR lpFileName
)
{
DWORD ret = 0;
FILE *stream;
bool bFindVal = false;
bool bFindSection = false;
TCHAR line[ LINESIZE ];
size_t sectionLength, keyLength, lineLength;
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
//设置默认值
t_strcpy(lpReturnedString, lpDefault);
ret = t_strlen(lpReturnedString);
return ret;
}
sectionLength = t_strlen(lpAppName);
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
//读取section前求出 Key 的长度
keyLength = t_strlen(lpKeyName);
bFindSection = true;
continue;
}
//查找Key, Section End?
if(line[0]=='[') break; //遇到了下一个
if(lineLength < keyLength+1 || line[keyLength] != '=') continue; //"KeyName="
if(t_strncmp(line, lpKeyName, keyLength)!=0) continue;
//Now We Get the Key!
t_strcpy(lpReturnedString, line + keyLength + 1);
//Now It's done.
bFindVal = true;
break;
}
fclose(stream);
if(!bFindVal)
{
//设置默认值
t_strcpy(lpReturnedString, lpDefault);
}
ret = t_strlen(lpReturnedString);
return ret;
}
//读取一个int值
UINT CeGetPrivateProfileInt(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
int nDefault,
LPCTSTR lpFileName
)
{
long ret = nDefault; //返回值
FILE *stream;
bool bFindVal = false;
bool bFindSection = false;
TCHAR line[ LINESIZE ];
size_t sectionLength, keyLength, lineLength;
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
//设置默认值
return nDefault;
}
sectionLength = t_strlen(lpAppName);
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
//读取section前求出 Key 的长度
keyLength = t_strlen(lpKeyName);
bFindSection = true;
continue;
}
//查找Key, Section End?
if(line[0]=='[') break; //遇到了下一个
if(lineLength < keyLength+1 || line[keyLength] != '=') continue; //"KeyName="
if(t_strncmp(line, lpKeyName, keyLength)!=0) continue;
//Now We Get the Key!
TCHAR *pStopChar = NULL;
ret = t_strtol(line + keyLength + 1, &pStopChar, 10); //默认为10进制
//Now It's done.
bFindVal = true;
break;
}
fclose(stream);
return ret;
}
//获取某个Section下面的所有“key=value”形式的字符串集合,以0字符分割
//结尾使用两个0字符
//缓冲区写入:"key1=value1 \0 key2=value2 \0 \0 "
//返回值表示写入缓冲区的字符数, 不包括结尾的0字符。
//如果缓冲区不够容纳所有的键值对,则返回值 = (nSize-2)
DWORD CeGetPrivateProfileSection(
LPCTSTR lpAppName,
LPTSTR lpReturnedString,
DWORD nSize, //缓冲区的字符数
LPCTSTR lpFileName
)
{
DWORD ret = 0; //返回值,拷贝的字符数量
DWORD remainSize = nSize - 2; //缓冲区当前所能能够接纳的字符数量
DWORD copySize; //本次循环中需要拷贝的字符数量
FILE *stream;
bool bFindSection = false; //是否已经找到Section
TCHAR line[ LINESIZE ]; //行缓冲区
LPTSTR pItem; //指向当前键值对的写入地址
size_t sectionLength, lineLength;
pItem = lpReturnedString; //指向缓冲区起始地址
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
return ret;
}
sectionLength = t_strlen(lpAppName);
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//缓冲区是否还有剩余空间?
if(remainSize <= 0) break;
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
bFindSection = true;
continue;
}
//查找Key, Section End?
if(line[0]=='[') break; //遇到了下一个
//copy the line to buffer, 注意ncpy不会复制结尾的0字符
copySize = min( remainSize, lineLength );
t_strncpy(pItem, line, copySize);
//追加一个0字符
pItem[copySize] = 0;
//缩小缓冲区剩余字符数量remainSize,和当前写入位置pItem
ret += (copySize + 1); //加1是为了统计结尾的0字符
remainSize -= (copySize + 1);
pItem += (copySize + 1);
}
fclose(stream);
if(bFindSection)
{
//再次对缓冲区追加一个0 字符
*pItem = 0;
}
return ret;
}
//获取一个ini文件中所有section的name,拷贝到缓冲区
//注意和系统API的区别是,系统API的读取是原子性的,即读取时不允许修改ini文件的内容
//而我们的函数未必保证这一点
DWORD CeGetPrivateProfileSectionNames(
LPTSTR lpszReturnBuffer,
DWORD nSize,
LPCTSTR lpFileName
)
{
DWORD ret = 0; //返回值,拷贝的字符数量
DWORD remainSize = nSize - 2; //缓冲区当前所能能够接纳的字符数量
DWORD copySize; //本次循环中需要拷贝的字符数量
TCHAR line[ LINESIZE ]; //行缓冲区
TCHAR *pSectionEndChar; //']'字符指针
LPTSTR pItem; //指向当前键值对的写入地址
FILE *stream; //流指针
size_t lineLength; //行字符长度
pItem = lpszReturnBuffer; //指向缓冲区起始地址
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
return ret;
}
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//缓冲区是否还有剩余空间?
if(remainSize <= 0) break;
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在 UNICODE 环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
if(line[0] != '[') continue; //本行是否是 [section]
//找到了一个Section,开始拷贝
//copy the section name to buffer, 注意ncpy不会复制结尾的0字符
//LINE: "[sectionName]"
// | |
// line pSectionEndChar
//找出‘=’字符的位置
pSectionEndChar = t_strchr(line, ']');
if(pSectionEndChar != NULL)
{
//找到了‘=’字符,(pEqualChar - line)是key的长度
copySize = min( remainSize, pSectionEndChar - line - 1 );
}
else
{
//本行中不存在‘]’字符,对于合法文件来说不会出现此种情况
copySize = min( remainSize, lineLength - 1 );
}
t_strncpy(pItem, line+1, copySize);
//追加一个0字符
pItem[copySize] = 0;
//缩小缓冲区剩余字符数量remainSize,和当前写入位置pItem
ret += (copySize + 1); //加1是为了统计结尾的0字符
remainSize -= (copySize + 1);
pItem += (copySize + 1);
}
fclose(stream);
//再次对缓冲区追加一个0 字符
*pItem = 0;
return ret;
}
//
BOOL CeWritePrivateProfileString(
LPCTSTR lpAppName,
LPCTSTR lpKeyName, //要修改的KEY,如果为NULL,会删除整个Section
LPCTSTR lpString, //要写入的值,如果为NULL,则会删除这个KEY
LPCTSTR lpFileName
)
{
FILE *stream;
void *pVoid = NULL; //文件的后半部分
bool bFindKey = false;
bool bFindSection = false;
TCHAR line[ LINESIZE ];
size_t sectionLength, keyLength, lineLength, nBytesRead = 0;
LONG nInsertPos = -1, nCopyPos = -1, nFileEndPos, nPos; //文件指针位置
LONG nSectionBegin = -1, nKeyBegin = -1, nNextKey = -1, nNextSection = -1;
BYTE mode = 0;
//如果 sectionName 为NULL,返回成功
if(lpAppName == NULL)
return true;
//r+: Opens for both reading and writing. (The file must exist.)
stream = t_fopen(lpFileName, _T("r+"));
if(stream == NULL)
{
return false;
}
//先取一次mode的默认值
if(lpKeyName == NULL)
mode = MODE_DELETE_SECTION;
else if(lpString == NULL)
mode = MODE_DELETE_KEY;
else
mode = MODE_OVERWRITE_KEY;
sectionLength = t_strlen(lpAppName);
//每次读行前,保存文件指针位置
while(nPos = ftell(stream), t_fgets(line, LINESIZE, stream) != NULL)
{
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
//读取section前求出 Key 的长度
if(lpKeyName != NULL)
keyLength = t_strlen(lpKeyName);
nSectionBegin = nPos;
bFindSection = true;
continue;
}
//Section找到了,
//Section End ?
if(line[0]=='[')
{
nNextSection = nPos;
break; //遇到了下一个
}
//是否需要查找KEY?
if(lpKeyName != NULL)
{
if(lineLength < keyLength+1 || line[keyLength] != '=') continue; //"KeyName="
if(t_strncmp(line, lpKeyName, keyLength) != 0) continue;
//Now We Get the Key!
nKeyBegin = nPos;
nNextKey = ftell(stream); //要拷贝的起始位置
//Now It's done.
bFindKey = true;
break;
}
}
//如果已经到达文件尾部,则追加换行
if(feof(stream))
t_fprintf(stream, _T("\r\n"));
if(nNextSection < 0) nNextSection = ftell(stream);
if(nNextKey < 0) nNextKey = ftell(stream);
//遍历后再次更新mode值
if(mode == MODE_DELETE_SECTION)
{
if(!bFindSection)
{
fclose(stream);
return true;
}
else
{
nInsertPos = nSectionBegin;
nCopyPos = nNextSection;
}
}
if(mode == MODE_DELETE_KEY)
{
if(!bFindKey)
{
fclose(stream);
return true;
}
else
{
nInsertPos = nKeyBegin;
nCopyPos = nNextKey;
}
}
if(mode == MODE_OVERWRITE_KEY)
{
if(!bFindSection)
{
mode = MODE_APPEND_SECTION;
}
else
{
if(bFindKey)
{
nInsertPos = nKeyBegin;
nCopyPos = nNextKey;
}
else
{
mode = MODE_APPEND_KEY;
nInsertPos = nNextSection;
nCopyPos = nNextSection;
}
}
}
//追加一个新的Section
if(mode == MODE_APPEND_SECTION)
{
t_fprintf(stream, _T("\r\n[%s]\r\n%s=%s\r\n"), lpAppName, lpKeyName, lpString);
fclose(stream);
return true;
}
//先把文件的后半部分拷贝到内存
fseek(stream, 0, SEEK_END);
nFileEndPos = ftell(stream);
if(nCopyPos >= 0 && nCopyPos < nFileEndPos)
{
//分配内存作为缓冲区
pVoid = malloc(nFileEndPos - nCopyPos + 1);
if(pVoid == NULL)
{
fclose(stream);
return false; //堆内存不足
}
fseek(stream, nCopyPos, SEEK_SET);
nBytesRead = fread(pVoid, 1, nFileEndPos - nCopyPos + 1, stream);
}
//写入新的value值
fseek(stream, nInsertPos, SEEK_SET);
if(lpKeyName != NULL && lpString != NULL)
t_fprintf(stream, _T("%s=%s\r\n"), lpKeyName, lpString);
//现在把文件的后半部分写回文件中
if(pVoid != NULL && nBytesRead > 0)
{
fwrite(pVoid, 1, nBytesRead, stream);
free(pVoid);
}
//此时结尾可能还有一些内容,属于原来的ini文件
//我们把它写成注释
nPos = ftell(stream);
fclose(stream);
//如果文件变小了,那么我们需要更改文件大小
if(nPos < nFileEndPos)
{
#ifdef WINCE //WINCE平台
HANDLE handle = CreateFile(
lpFileName, //LPCTSTR lpFileName
GENERIC_WRITE, //DOWRD dwDesiredAccess,
0, //DWORD dwShareMode, 非共享模式
NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes, ignored
OPEN_EXISTING, //DWORD dwCreationDispostion,
FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes,
NULL//HANDLE hTemplateFile, ignored
);
if(handle != NULL)
{
//移动文件指针
SetFilePointer(handle, nPos, NULL, FILE_BEGIN);
//设置EOF
SetEndOfFile(handle);
//关闭
CloseHandle(handle);
}
#else //PC 平台
int handle = t_sopen(lpFileName, _O_RDWR, _SH_DENYRW);
if(handle > 0)
{
//修改文件大小
_chsize(handle, nPos);
//关闭文件
_close(handle);
}
#endif //
}
return TRUE;
}
//重写某个Section,注意和 PC API 的区别是,这里不保证原子性操作
BOOL CeWritePrivateProfileSection(
LPCTSTR lpAppName, //section name
LPCTSTR lpString, //key1=val1 \0 key2=val2 \0\0
LPCTSTR lpFileName
)
{
FILE *stream;
void *pVoid = NULL; //文件的后半部分
bool bFindSection = false;
TCHAR line[ LINESIZE ]; //行缓冲区
LPCTSTR pItem = lpString;
size_t sectionLength, lineLength, nBytesRead = 0;
LONG nFileEndPos, nPos; //文件指针位置
LONG nSectionBegin = -1, nNextSection = -1;
//如果 sectionName 为NULL,返回失败
if(lpAppName == NULL || lpString == NULL)
return false;
//r+: Opens for both reading and writing. (The file must exist.)
stream = t_fopen(lpFileName, _T("r+"));
if(stream == NULL)
{
return false;
}
sectionLength = t_strlen(lpAppName);
//每次读行前,保存文件指针位置
while(nPos = ftell(stream), t_fgets(line, LINESIZE, stream) != NULL)
{
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
nSectionBegin = nPos;
bFindSection = true;
continue;
}
//Section找到了,
//Section End ?
if(line[0]=='[')
{
nNextSection = nPos;
break; //遇到了下一个
}
}
//如果已经到达文件尾部,则追加换行
if(nNextSection < 0) nNextSection = ftell(stream);
//追加一个新的Section
if(!bFindSection)
{
nSectionBegin = ftell(stream);
}
//覆写Section
//先把文件的后半部分拷贝到内存
fseek(stream, 0, SEEK_END);
nFileEndPos = ftell(stream);
if(nNextSection >= 0 && nNextSection < nFileEndPos)
{
//分配内存作为缓冲区
pVoid = malloc(nFileEndPos - nNextSection + 1);
if(pVoid == NULL)
{
fclose(stream);
return false; //堆内存不足
}
fseek(stream, nNextSection, SEEK_SET);
nBytesRead = fread(pVoid, 1, nFileEndPos - nNextSection + 1, stream);
}
//逐行写入key = val
fseek(stream, nSectionBegin, SEEK_SET);
//再次写入[section],如果不存在就会追加
t_fprintf(stream, _T("[%s]\r\n"), lpAppName);
while(*pItem)
{
t_fprintf(stream, _T("%s\r\n"), pItem);
pItem += t_strlen(pItem) + 1; //移动到下一行
}
//现在把文件的后半部分写回文件中
if(pVoid != NULL)
{
fwrite(pVoid, 1, nBytesRead, stream);
free(pVoid);
}
//此时结尾可能还有一些内容,属于原来的ini文件
//我们把它写成注释
nPos = ftell(stream); //当前文件位置
fclose(stream);
//如果文件变小了,那么我们需要更改文件大小
if(nPos < nFileEndPos)
{
#ifdef WINCE //WINCE平台
HANDLE handle = CreateFile(
lpFileName, //LPCTSTR lpFileName
GENERIC_WRITE, //DOWRD dwDesiredAccess,
0, //DWORD dwShareMode, 非共享模式
NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes, ignored
OPEN_EXISTING, //DWORD dwCreationDispostion,
FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes,
NULL//HANDLE hTemplateFile, ignored
);
if(handle != NULL)
{
//移动文件指针
SetFilePointer(handle, nPos, NULL, FILE_BEGIN);
//设置EOF
SetEndOfFile(handle);
//关闭
CloseHandle(handle);
}
#else //PC 平台
int handle = t_sopen(lpFileName, _O_RDWR, _SH_DENYRW);
if(handle > 0)
{
//修改文件大小
_chsize(handle, nPos);
//关闭文件
_close(handle);
}
#endif //
}
return TRUE;
}
//===========================================================
// 以下是我增加的函数(API中没有)
//===========================================================
//
//获取某个section下的所有的Key名,
//获取某个Section下面的所有“key形式的字符串集合,以0字符分割
//结尾使用两个0字符
//缓冲区写入:"key1 \0 key2 \0 \0 "
//返回值表示写入缓冲区的字符数, 不包括结尾的0字符。
//如果缓冲区不够容纳所有的键值对,则返回值 = (nSize-2)
//注意:此函数是在桌面 API 中也没有的。而是我单独添加的
//
DWORD CeGetPrivateProfileKeyNames(
LPCTSTR lpAppName,
LPTSTR lpReturnedString,
DWORD nSize, //缓冲区的字符数
LPCTSTR lpFileName
)
{
DWORD ret = 0; //返回值,拷贝的字符数量
DWORD remainSize = nSize - 2; //缓冲区当前所能能够接纳的字符数量
DWORD copySize; //本次循环中需要拷贝的字符数量
bool bFindSection = false; //是否已经找到Section
TCHAR line[ LINESIZE ]; //行缓冲区
LPTSTR pItem; //指向当前键值对的写入地址
TCHAR *pEqualChar; //等号字符的在行中的位置
FILE *stream; //流指针
size_t sectionLength, lineLength;
pItem = lpReturnedString; //指向缓冲区起始地址
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
return ret;
}
sectionLength = t_strlen(lpAppName);
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//缓冲区是否还有剩余空间?
if(remainSize <= 0) break;
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
bFindSection = true;
continue;
}
//查找Key, Section End?
if(line[0]=='[') break; //遇到了下一个
//copy the keyname to buffer, 注意ncpy不会复制结尾的0字符
//LINE: "keyName = "
// | |
// line pEqualChar
//找出‘=’字符的位置
pEqualChar = t_strchr(line, '=');
if(pEqualChar != NULL)
{
//找到了‘=’字符,(pEqualChar - line)是key的长度
copySize = min( remainSize, pEqualChar - line );
}
else
{
//本行中不存在‘=’字符,对于合法文件来说不会出现此种情况
copySize = min( remainSize, lineLength );
}
t_strncpy(pItem, line, copySize);
//最佳一个0字符
pItem[copySize] = 0;
//缩小缓冲区剩余字符数量remainSize,和当前写入位置pItem
ret += (copySize + 1); //加1是为了统计结尾的0字符
remainSize -= (copySize + 1);
pItem += (copySize + 1);
}
fclose(stream);
if(bFindSection)
{
//再次对缓冲区追加一个0 字符
*pItem = 0;
}
return ret;
}
在 CeWritePrivateProfileString 函数中,用到的几个文件指针的含义是:假设我们要查找的 Section 是“section2”,Key 是“key2”;//适用于 char* 和 UNICODE,
//所有字符串必须使用 TEXT("aa") 或者 _T("aa") 的格式(自动适应 char* 或 UNICODE)
//所有相关函数加t_前缀
//IniFile: 读取INI FILE的简单解析!所谓简单,也就是解析代码简单,但对文件格式要求更高
//[1]任何字符串前后不要有空格(使解析代码可以不考虑前后的trim)
// 例如允许"Key1=Val", 而不允许" Key1 = Val "
//[2]允许有注释,第一个字符必须是英文分号';'
//
#include "StdAfx.h"
#include "IniFile.h"
//从appname(section)中读取string类型key
DWORD CeGetPrivateProfileString(
LPCTSTR lpAppName, //section name: [lpAppName]
LPCTSTR lpKeyName, //lpKeyName=lpReturnedString
LPCTSTR lpDefault, //未找到时的默认值
LPTSTR lpReturnedString, //[out] 查找到的结果
DWORD nSize, //[in]lpReturnedString的字符数,注意单位不是字节!
LPCTSTR lpFileName
)
{
DWORD ret = 0;
FILE *stream;
bool bFindVal = false;
bool bFindSection = false;
TCHAR line[ LINESIZE ];
size_t sectionLength, keyLength, lineLength;
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
//设置默认值
t_strcpy(lpReturnedString, lpDefault);
ret = t_strlen(lpReturnedString);
return ret;
}
sectionLength = t_strlen(lpAppName);
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
//读取section前求出 Key 的长度
keyLength = t_strlen(lpKeyName);
bFindSection = true;
continue;
}
//查找Key, Section End?
if(line[0]=='[') break; //遇到了下一个
if(lineLength < keyLength+1 || line[keyLength] != '=') continue; //"KeyName="
if(t_strncmp(line, lpKeyName, keyLength)!=0) continue;
//Now We Get the Key!
t_strcpy(lpReturnedString, line + keyLength + 1);
//Now It's done.
bFindVal = true;
break;
}
fclose(stream);
if(!bFindVal)
{
//设置默认值
t_strcpy(lpReturnedString, lpDefault);
}
ret = t_strlen(lpReturnedString);
return ret;
}
//读取一个int值
UINT CeGetPrivateProfileInt(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
int nDefault,
LPCTSTR lpFileName
)
{
long ret = nDefault; //返回值
FILE *stream;
bool bFindVal = false;
bool bFindSection = false;
TCHAR line[ LINESIZE ];
size_t sectionLength, keyLength, lineLength;
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
//设置默认值
return nDefault;
}
sectionLength = t_strlen(lpAppName);
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
//读取section前求出 Key 的长度
keyLength = t_strlen(lpKeyName);
bFindSection = true;
continue;
}
//查找Key, Section End?
if(line[0]=='[') break; //遇到了下一个
if(lineLength < keyLength+1 || line[keyLength] != '=') continue; //"KeyName="
if(t_strncmp(line, lpKeyName, keyLength)!=0) continue;
//Now We Get the Key!
TCHAR *pStopChar = NULL;
ret = t_strtol(line + keyLength + 1, &pStopChar, 10); //默认为10进制
//Now It's done.
bFindVal = true;
break;
}
fclose(stream);
return ret;
}
//获取某个Section下面的所有“key=value”形式的字符串集合,以0字符分割
//结尾使用两个0字符
//缓冲区写入:"key1=value1 \0 key2=value2 \0 \0 "
//返回值表示写入缓冲区的字符数, 不包括结尾的0字符。
//如果缓冲区不够容纳所有的键值对,则返回值 = (nSize-2)
DWORD CeGetPrivateProfileSection(
LPCTSTR lpAppName,
LPTSTR lpReturnedString,
DWORD nSize, //缓冲区的字符数
LPCTSTR lpFileName
)
{
DWORD ret = 0; //返回值,拷贝的字符数量
DWORD remainSize = nSize - 2; //缓冲区当前所能能够接纳的字符数量
DWORD copySize; //本次循环中需要拷贝的字符数量
FILE *stream;
bool bFindSection = false; //是否已经找到Section
TCHAR line[ LINESIZE ]; //行缓冲区
LPTSTR pItem; //指向当前键值对的写入地址
size_t sectionLength, lineLength;
pItem = lpReturnedString; //指向缓冲区起始地址
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
return ret;
}
sectionLength = t_strlen(lpAppName);
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//缓冲区是否还有剩余空间?
if(remainSize <= 0) break;
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
bFindSection = true;
continue;
}
//查找Key, Section End?
if(line[0]=='[') break; //遇到了下一个
//copy the line to buffer, 注意ncpy不会复制结尾的0字符
copySize = min( remainSize, lineLength );
t_strncpy(pItem, line, copySize);
//追加一个0字符
pItem[copySize] = 0;
//缩小缓冲区剩余字符数量remainSize,和当前写入位置pItem
ret += (copySize + 1); //加1是为了统计结尾的0字符
remainSize -= (copySize + 1);
pItem += (copySize + 1);
}
fclose(stream);
if(bFindSection)
{
//再次对缓冲区追加一个0 字符
*pItem = 0;
}
return ret;
}
//获取一个ini文件中所有section的name,拷贝到缓冲区
//注意和系统API的区别是,系统API的读取是原子性的,即读取时不允许修改ini文件的内容
//而我们的函数未必保证这一点
DWORD CeGetPrivateProfileSectionNames(
LPTSTR lpszReturnBuffer,
DWORD nSize,
LPCTSTR lpFileName
)
{
DWORD ret = 0; //返回值,拷贝的字符数量
DWORD remainSize = nSize - 2; //缓冲区当前所能能够接纳的字符数量
DWORD copySize; //本次循环中需要拷贝的字符数量
TCHAR line[ LINESIZE ]; //行缓冲区
TCHAR *pSectionEndChar; //']'字符指针
LPTSTR pItem; //指向当前键值对的写入地址
FILE *stream; //流指针
size_t lineLength; //行字符长度
pItem = lpszReturnBuffer; //指向缓冲区起始地址
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
return ret;
}
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//缓冲区是否还有剩余空间?
if(remainSize <= 0) break;
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在 UNICODE 环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
if(line[0] != '[') continue; //本行是否是 [section]
//找到了一个Section,开始拷贝
//copy the section name to buffer, 注意ncpy不会复制结尾的0字符
//LINE: "[sectionName]"
// | |
// line pSectionEndChar
//找出‘=’字符的位置
pSectionEndChar = t_strchr(line, ']');
if(pSectionEndChar != NULL)
{
//找到了‘=’字符,(pEqualChar - line)是key的长度
copySize = min( remainSize, pSectionEndChar - line - 1 );
}
else
{
//本行中不存在‘]’字符,对于合法文件来说不会出现此种情况
copySize = min( remainSize, lineLength - 1 );
}
t_strncpy(pItem, line+1, copySize);
//追加一个0字符
pItem[copySize] = 0;
//缩小缓冲区剩余字符数量remainSize,和当前写入位置pItem
ret += (copySize + 1); //加1是为了统计结尾的0字符
remainSize -= (copySize + 1);
pItem += (copySize + 1);
}
fclose(stream);
//再次对缓冲区追加一个0 字符
*pItem = 0;
return ret;
}
//
BOOL CeWritePrivateProfileString(
LPCTSTR lpAppName,
LPCTSTR lpKeyName, //要修改的KEY,如果为NULL,会删除整个Section
LPCTSTR lpString, //要写入的值,如果为NULL,则会删除这个KEY
LPCTSTR lpFileName
)
{
FILE *stream;
void *pVoid = NULL; //文件的后半部分
bool bFindKey = false;
bool bFindSection = false;
TCHAR line[ LINESIZE ];
size_t sectionLength, keyLength, lineLength, nBytesRead = 0;
LONG nInsertPos = -1, nCopyPos = -1, nFileEndPos, nPos; //文件指针位置
LONG nSectionBegin = -1, nKeyBegin = -1, nNextKey = -1, nNextSection = -1;
BYTE mode = 0;
//如果 sectionName 为NULL,返回成功
if(lpAppName == NULL)
return true;
//r+: Opens for both reading and writing. (The file must exist.)
stream = t_fopen(lpFileName, _T("r+"));
if(stream == NULL)
{
return false;
}
//先取一次mode的默认值
if(lpKeyName == NULL)
mode = MODE_DELETE_SECTION;
else if(lpString == NULL)
mode = MODE_DELETE_KEY;
else
mode = MODE_OVERWRITE_KEY;
sectionLength = t_strlen(lpAppName);
//每次读行前,保存文件指针位置
while(nPos = ftell(stream), t_fgets(line, LINESIZE, stream) != NULL)
{
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
//读取section前求出 Key 的长度
if(lpKeyName != NULL)
keyLength = t_strlen(lpKeyName);
nSectionBegin = nPos;
bFindSection = true;
continue;
}
//Section找到了,
//Section End ?
if(line[0]=='[')
{
nNextSection = nPos;
break; //遇到了下一个
}
//是否需要查找KEY?
if(lpKeyName != NULL)
{
if(lineLength < keyLength+1 || line[keyLength] != '=') continue; //"KeyName="
if(t_strncmp(line, lpKeyName, keyLength) != 0) continue;
//Now We Get the Key!
nKeyBegin = nPos;
nNextKey = ftell(stream); //要拷贝的起始位置
//Now It's done.
bFindKey = true;
break;
}
}
//如果已经到达文件尾部,则追加换行
if(feof(stream))
t_fprintf(stream, _T("\r\n"));
if(nNextSection < 0) nNextSection = ftell(stream);
if(nNextKey < 0) nNextKey = ftell(stream);
//遍历后再次更新mode值
if(mode == MODE_DELETE_SECTION)
{
if(!bFindSection)
{
fclose(stream);
return true;
}
else
{
nInsertPos = nSectionBegin;
nCopyPos = nNextSection;
}
}
if(mode == MODE_DELETE_KEY)
{
if(!bFindKey)
{
fclose(stream);
return true;
}
else
{
nInsertPos = nKeyBegin;
nCopyPos = nNextKey;
}
}
if(mode == MODE_OVERWRITE_KEY)
{
if(!bFindSection)
{
mode = MODE_APPEND_SECTION;
}
else
{
if(bFindKey)
{
nInsertPos = nKeyBegin;
nCopyPos = nNextKey;
}
else
{
mode = MODE_APPEND_KEY;
nInsertPos = nNextSection;
nCopyPos = nNextSection;
}
}
}
//追加一个新的Section
if(mode == MODE_APPEND_SECTION)
{
t_fprintf(stream, _T("\r\n[%s]\r\n%s=%s\r\n"), lpAppName, lpKeyName, lpString);
fclose(stream);
return true;
}
//先把文件的后半部分拷贝到内存
fseek(stream, 0, SEEK_END);
nFileEndPos = ftell(stream);
if(nCopyPos >= 0 && nCopyPos < nFileEndPos)
{
//分配内存作为缓冲区
pVoid = malloc(nFileEndPos - nCopyPos + 1);
if(pVoid == NULL)
{
fclose(stream);
return false; //堆内存不足
}
fseek(stream, nCopyPos, SEEK_SET);
nBytesRead = fread(pVoid, 1, nFileEndPos - nCopyPos + 1, stream);
}
//写入新的value值
fseek(stream, nInsertPos, SEEK_SET);
if(lpKeyName != NULL && lpString != NULL)
t_fprintf(stream, _T("%s=%s\r\n"), lpKeyName, lpString);
//现在把文件的后半部分写回文件中
if(pVoid != NULL && nBytesRead > 0)
{
fwrite(pVoid, 1, nBytesRead, stream);
free(pVoid);
}
//此时结尾可能还有一些内容,属于原来的ini文件
//我们把它写成注释
nPos = ftell(stream);
fclose(stream);
//如果文件变小了,那么我们需要更改文件大小
if(nPos < nFileEndPos)
{
#ifdef WINCE //WINCE平台
HANDLE handle = CreateFile(
lpFileName, //LPCTSTR lpFileName
GENERIC_WRITE, //DOWRD dwDesiredAccess,
0, //DWORD dwShareMode, 非共享模式
NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes, ignored
OPEN_EXISTING, //DWORD dwCreationDispostion,
FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes,
NULL//HANDLE hTemplateFile, ignored
);
if(handle != NULL)
{
//移动文件指针
SetFilePointer(handle, nPos, NULL, FILE_BEGIN);
//设置EOF
SetEndOfFile(handle);
//关闭
CloseHandle(handle);
}
#else //PC 平台
int handle = t_sopen(lpFileName, _O_RDWR, _SH_DENYRW);
if(handle > 0)
{
//修改文件大小
_chsize(handle, nPos);
//关闭文件
_close(handle);
}
#endif //
}
return TRUE;
}
//重写某个Section,注意和 PC API 的区别是,这里不保证原子性操作
BOOL CeWritePrivateProfileSection(
LPCTSTR lpAppName, //section name
LPCTSTR lpString, //key1=val1 \0 key2=val2 \0\0
LPCTSTR lpFileName
)
{
FILE *stream;
void *pVoid = NULL; //文件的后半部分
bool bFindSection = false;
TCHAR line[ LINESIZE ]; //行缓冲区
LPCTSTR pItem = lpString;
size_t sectionLength, lineLength, nBytesRead = 0;
LONG nFileEndPos, nPos; //文件指针位置
LONG nSectionBegin = -1, nNextSection = -1;
//如果 sectionName 为NULL,返回失败
if(lpAppName == NULL || lpString == NULL)
return false;
//r+: Opens for both reading and writing. (The file must exist.)
stream = t_fopen(lpFileName, _T("r+"));
if(stream == NULL)
{
return false;
}
sectionLength = t_strlen(lpAppName);
//每次读行前,保存文件指针位置
while(nPos = ftell(stream), t_fgets(line, LINESIZE, stream) != NULL)
{
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
nSectionBegin = nPos;
bFindSection = true;
continue;
}
//Section找到了,
//Section End ?
if(line[0]=='[')
{
nNextSection = nPos;
break; //遇到了下一个
}
}
//如果已经到达文件尾部,则追加换行
if(nNextSection < 0) nNextSection = ftell(stream);
//追加一个新的Section
if(!bFindSection)
{
nSectionBegin = ftell(stream);
}
//覆写Section
//先把文件的后半部分拷贝到内存
fseek(stream, 0, SEEK_END);
nFileEndPos = ftell(stream);
if(nNextSection >= 0 && nNextSection < nFileEndPos)
{
//分配内存作为缓冲区
pVoid = malloc(nFileEndPos - nNextSection + 1);
if(pVoid == NULL)
{
fclose(stream);
return false; //堆内存不足
}
fseek(stream, nNextSection, SEEK_SET);
nBytesRead = fread(pVoid, 1, nFileEndPos - nNextSection + 1, stream);
}
//逐行写入key = val
fseek(stream, nSectionBegin, SEEK_SET);
//再次写入[section],如果不存在就会追加
t_fprintf(stream, _T("[%s]\r\n"), lpAppName);
while(*pItem)
{
t_fprintf(stream, _T("%s\r\n"), pItem);
pItem += t_strlen(pItem) + 1; //移动到下一行
}
//现在把文件的后半部分写回文件中
if(pVoid != NULL)
{
fwrite(pVoid, 1, nBytesRead, stream);
free(pVoid);
}
//此时结尾可能还有一些内容,属于原来的ini文件
//我们把它写成注释
nPos = ftell(stream); //当前文件位置
fclose(stream);
//如果文件变小了,那么我们需要更改文件大小
if(nPos < nFileEndPos)
{
#ifdef WINCE //WINCE平台
HANDLE handle = CreateFile(
lpFileName, //LPCTSTR lpFileName
GENERIC_WRITE, //DOWRD dwDesiredAccess,
0, //DWORD dwShareMode, 非共享模式
NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes, ignored
OPEN_EXISTING, //DWORD dwCreationDispostion,
FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes,
NULL//HANDLE hTemplateFile, ignored
);
if(handle != NULL)
{
//移动文件指针
SetFilePointer(handle, nPos, NULL, FILE_BEGIN);
//设置EOF
SetEndOfFile(handle);
//关闭
CloseHandle(handle);
}
#else //PC 平台
int handle = t_sopen(lpFileName, _O_RDWR, _SH_DENYRW);
if(handle > 0)
{
//修改文件大小
_chsize(handle, nPos);
//关闭文件
_close(handle);
}
#endif //
}
return TRUE;
}
//===========================================================
// 以下是我增加的函数(API中没有)
//===========================================================
//
//获取某个section下的所有的Key名,
//获取某个Section下面的所有“key形式的字符串集合,以0字符分割
//结尾使用两个0字符
//缓冲区写入:"key1 \0 key2 \0 \0 "
//返回值表示写入缓冲区的字符数, 不包括结尾的0字符。
//如果缓冲区不够容纳所有的键值对,则返回值 = (nSize-2)
//注意:此函数是在桌面 API 中也没有的。而是我单独添加的
//
DWORD CeGetPrivateProfileKeyNames(
LPCTSTR lpAppName,
LPTSTR lpReturnedString,
DWORD nSize, //缓冲区的字符数
LPCTSTR lpFileName
)
{
DWORD ret = 0; //返回值,拷贝的字符数量
DWORD remainSize = nSize - 2; //缓冲区当前所能能够接纳的字符数量
DWORD copySize; //本次循环中需要拷贝的字符数量
bool bFindSection = false; //是否已经找到Section
TCHAR line[ LINESIZE ]; //行缓冲区
LPTSTR pItem; //指向当前键值对的写入地址
TCHAR *pEqualChar; //等号字符的在行中的位置
FILE *stream; //流指针
size_t sectionLength, lineLength;
pItem = lpReturnedString; //指向缓冲区起始地址
stream = t_fopen(lpFileName, _T("r"));
if(stream == NULL)
{
return ret;
}
sectionLength = t_strlen(lpAppName);
while(t_fgets(line, LINESIZE, stream) != NULL)
{
//缓冲区是否还有剩余空间?
if(remainSize <= 0) break;
//忽略注释行和空行
if(line[0] == 0 || line[0] == ';') continue;
lineLength = t_strlen(line);
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
if(line[ lineLength - 1 ] == 0x0a)
{
line[ lineLength - 1 ] = 0;
lineLength--;
//注意此时可能会成为空字符串
if(lineLength == 0) continue;
}
//尝试寻找到 section
if(!bFindSection)
{
if(line[0] != '[') continue; //本行是否是 [section]
//这里是我们想要的Section吗?
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
if(line[sectionLength + 1] != ']') continue;
if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
//Now Section will appear on next line
bFindSection = true;
continue;
}
//查找Key, Section End?
if(line[0]=='[') break; //遇到了下一个
//copy the keyname to buffer, 注意ncpy不会复制结尾的0字符
//LINE: "keyName = "
// | |
// line pEqualChar
//找出‘=’字符的位置
pEqualChar = t_strchr(line, '=');
if(pEqualChar != NULL)
{
//找到了‘=’字符,(pEqualChar - line)是key的长度
copySize = min( remainSize, pEqualChar - line );
}
else
{
//本行中不存在‘=’字符,对于合法文件来说不会出现此种情况
copySize = min( remainSize, lineLength );
}
t_strncpy(pItem, line, copySize);
//最佳一个0字符
pItem[copySize] = 0;
//缩小缓冲区剩余字符数量remainSize,和当前写入位置pItem
ret += (copySize + 1); //加1是为了统计结尾的0字符
remainSize -= (copySize + 1);
pItem += (copySize + 1);
}
fclose(stream);
if(bFindSection)
{
//再次对缓冲区追加一个0 字符
*pItem = 0;
}
return ret;
}
=============================
...
nSectionBegin -> [section2]
...
nKeyBegin -> key2=value2
nNextKey -> ...
...
nNextSection -> [otherSection]
...
=============================
其他文件指针的含义是:nInsertPos - 新的KEY=Value开始写入位置; nCopyPos - 文件的后半部分在原始文件中的位置(整体不需要改写,但可能需要前移或后移),从这里到文件结尾的内容会在改写ini文件之前拷贝到内存,改写KEY后,再写回文件并附加到文件尾部。
上面的代码中,包含 StdAfx.h 通常是因为默认设定,如果取消预编译头的选项,则可以不包含它。
然后我们可以很方便对上面的代码进行测试:
TestingCode
#include <stdio.h>
#include "IniFile.h"
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR buffer[128];
int age;
CeGetPrivateProfileString(_T("section2"), _T("name"), _T("defaultValue"), buffer, t_strlen(buffer), _T("c:\\test.ini"));
age = CeGetPrivateProfileInt(_T("section2"), _T("age"), -1, _T("c:\\test.ini"));
//get section
//CeGetPrivateProfileSection(_T("section2"), buffer, 128, _T("c:\\test.ini"));
//key names
CeGetPrivateProfileKeyNames(_T("section2"), buffer, 128, _T("c:\\test.ini"));
//section names
GetPrivateProfileSectionNames(buffer, 128, _T("c:\\test.ini"));
CeWritePrivateProfileString(_T("section2"), _T("key2"), _T("testValue"), _T("c:\\test.ini"));
wprintf(buffer);
getchar();
return 0;
}
//假设 C:\\test.ini 的内容是:
//;testing ini file
//[section1]
//key1=aa
//[section2]
//name=myname
//age=20
//[section3]
//key1=bb
#include <stdio.h>
#include "IniFile.h"
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR buffer[128];
int age;
CeGetPrivateProfileString(_T("section2"), _T("name"), _T("defaultValue"), buffer, t_strlen(buffer), _T("c:\\test.ini"));
age = CeGetPrivateProfileInt(_T("section2"), _T("age"), -1, _T("c:\\test.ini"));
//get section
//CeGetPrivateProfileSection(_T("section2"), buffer, 128, _T("c:\\test.ini"));
//key names
CeGetPrivateProfileKeyNames(_T("section2"), buffer, 128, _T("c:\\test.ini"));
//section names
GetPrivateProfileSectionNames(buffer, 128, _T("c:\\test.ini"));
CeWritePrivateProfileString(_T("section2"), _T("key2"), _T("testValue"), _T("c:\\test.ini"));
wprintf(buffer);
getchar();
return 0;
}
//假设 C:\\test.ini 的内容是:
//;testing ini file
//[section1]
//key1=aa
//[section2]
//name=myname
//age=20
//[section3]
//key1=bb