在 WinCe 平台读写 ini 文件
在上篇文章开发 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
- /***************************************
- * 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
- //适用于 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”;
=============================
...
nSectionBegin -> [section2]
...
nKeyBegin -> key2=value2
nNextKey -> ...
...
nNextSection -> [otherSection]
...
=============================
其他文件指针的含义是:nInsertPos - 新的KEY=Value开始写入位置; nCopyPos - 文件的后半部分在原始文件中的位置(整体不需要改写,但可能需要前移或后移),从这里到文件结尾的内容会在改写ini文件之前拷贝到内存,改写KEY后,再写回文件并附加到文件尾部。
上面的代码中,包含 StdAfx.h 通常是因为默认设定,如果取消预编译头的选项,则可以不包含它。
然后我们可以很方便对上面的代码进行测试:
#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
搜索
复制
在 WinCe 平台读写 ini 文件的更多相关文章
- c# 利用动态库DllImport("kernel32")读写ini文件(提供Dmo下载)
c# 利用动态库DllImport("kernel32")读写ini文件 自从读了设计模式,真的会改变一个程序员的习惯.我觉得嘛,经验也可以从一个人的习惯看得出来,看他的代码编写习 ...
- VB读写INI文件的四个函数以及相关API详细说明
WritePrivateProfileString函数说明 来源:http://blog.csdn.net/wjb9921/article/details/2005000 在我们写的程序当中,总有一 ...
- C# 读写INI 文件
INI 格式: [Section1] KeyWord1 = Value1 KeyWord2 = Value2 ... [Section2] KeyWord3 = Value3 KeyWord4 = V ...
- WIN32读写INI文件方法
在程序中经常要用到设置或者其他少量数据的存盘,以便程序在下一次执行的时候可以使用,比如说保存本次程序执行时窗口的位置.大小.一些用户设置的 数据等等,在 Dos 下编程的时候,我们一般自己产生一个 ...
- 读写ini文件
C# 使用文件流来读写ini文件 背景 之前采用ini文件作为程序的配置文件,觉得这种结构简单明了,配置起来也挺方便.然后操作方式是通过WindowsAPI,然后再网上找到一个基于WindowsAPI ...
- Windows中读写ini文件
.ini 文件是Initialization File的缩写,即初始化文件,是windows的系统配置文件所采用的存储格式,来配置应用软件以实现不同用户的要求.配置文件有很多种如ini配置文件,XML ...
- C#中读写INI文件
C#中读写INI文件 c#的类没有直接提供对ini文件的操作支持,可以自己包装win api的WritePrivateProfileString和GetPrivateProfileString函数实现 ...
- Qt读写ini文件
一 背景 1 ini文件介绍 .ini 文件是Initialization File的缩写,即初始化文件. 除了windows现在很多其他操作系统下面的应用软件也有.ini文件,用来配置应用软件以实现 ...
- 【python-ini】python读写ini文件
[python-ini]python读写ini文件 本文实例讲述了Python读写ini文件的方法.分享给大家供大家参考.具体如下: 比如有一个文件update.ini,里面有这些内容: 1 2 ...
随机推荐
- js如何在指定页面跳转到另一指定页面
要实现从一个页面A跳到另一个页面B,js实现就在A的js代码加跳转代码 JS跳转大概有以下几种方式: 第一种:(跳转到b.html)<script language="javascri ...
- JS总结之一:字符串的调用方法
字符串的调用方法:var s="hello, world";document.write(s.charAt(0)); //第一个字符document.write(s.charAt( ...
- java代码中 路径符号的写法
String path="D:\\新建文件夹\\2.png"; File file=new File(path); System.out.println(file.exists() ...
- lua学习
在lua中,一切都是变量,除了关键字. 1.注释: 单行注释: 连续两个减号“--”表示注释的开始,一直延续到行末.相当于C语言中的“//” 多行注释:由“--[[”表示注释开始, “]]”表示注释结 ...
- unable to connect to :5555
有可能批处理文件用的adb和eclipse的adb不兼容.把你的批处理文件用的adb换成eclipse的adb就可以了: 运行结果:
- MPEG1的码流层次与各层次的作用
1. 序列层(Sequence layer) 序列层主要用于为随机播放提供全局参数支持,这些参数包括图像宽高.像素高宽比.帧率.码率.VBV大小.帧内量化矩阵.帧间量化矩阵. 2. 图像组层(Grou ...
- MVC验证生成的代码
- linux学习的哲学层面的思考-架构
参考:http://blog.chinaunix.net/uid-26119273-id-3356414.html 学习Linux,准备做产品的话,不要把Linux当成了终极目标(当然,这是对应用而言 ...
- ActionResult的其它返回值
我们上边所看到的Action都是return View();我们可以看作这个返回值用于解析一个aspx文件.而它的返回类型是ActionResult如 public ActionResult Inde ...
- POJ 3061 Subsequence 二分查找
题目大意:给出长度为n的一个序列,给出一个数字S,求长度最短的序列和大于等于S的连续子序列,输出该长度,如果没有答案输出0. 题目思路:看数据范围,这道题就是卡时间的.我们可以用sum[i]记录前i项 ...