error C2664: “fopen”: 不能将参数 1 从“CString”转换为“const char *”

本文详细介绍了如何在Visual C++ 6.0中配置和编写支持Unicode编码的应用程序。包括预处理宏定义的更改、字符串操作函数的替换、以及如何在Unicode和MBCS编码之间进行转换等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其实还可能出现其它类似的问题,如:

error C2664: “fopen”: 不能将参数 1 从“CString”转换为“const char *”

error C2039: “strcpy”: 不是“ATL::CStringT<BaseType,StringTraits>”的成员

但是程序在VC6下编译得很好,经过研究发现是设置上有所不同。在VC6中,默认使用MBCS编码,即多字节字符;而VC8、VC7默认的是Unicode编码,所以...

关于这两种编码有何不同,我引用了网上的一篇文章,由于作者匿名,只能在此感谢:

http://pc.nengbang.cn/group_thread/view/id-2603

在VC6中,默认使用MBCS编码,即多字节字符,实际就是支持大于0x80的ASCII码。这样,一个中文字可以表示为2个字节,GB2312就是这样表示的。

VC6的默认安装是不带UNICODE库的,要在VC6中写UNICODE程序,必须安装CRT和MFC的Unicode库。

要使你的程序支持Unicode,要在你的项目属性中去掉"_MBCS"宏定义,增加"UNICODE"和"_UNICODE"两个宏定义。(注意,这两个都应该加上,因为CRT和MFC使用UNICODE定义,而STL则使用_UNICODE)

如果你的程序是MFC的,则Unicode版MFC库的入口点是wWinMainCRTStartup。

为了方便开发者,VC6中提供了Tchar.h,里面定义了一些宏用来帮助写两种编码都兼容的代码。

类型

一般文本 数据类型名称 _UNICODE 和 _MBCS 未定义 _MBCS 已定义 _UNICODE 已定义

_TCHAR char char wchar_t
_TINT int int wint_t
_TSCHAR signed char signed char wchar_t
_TUCHAR unsigned char unsigned char wchar_t
_TXCHAR char unsigned char wchar_t
_T 或 _TEXT 无效(由预处理器移除)无效(由预处理器移除)L(将后面的字符或字符串转换成相应的 Unicode 形式)

CRT中的相关函数在Tchar.h中都定义了相应的替代,基本是将str换成了_tcs,比如:CRT中的unsigned int strlen(const char *)现在是unsigned int _tcslen(const TCHAR*),在Uniocde时,将被替换为unsigned int _wcslen(const wchar_t)*,而在MBCS时,会被替换为unsigned int _mcslen(const char*)。

看,写Unicode和MBCS兼容的代码挺容易的吧,我总结了一些替换规则

1 将char换成TCHAR (unsigned char必须去掉unsigned)

2 将str函数换成_tcs函数

3 将字符串常量定义加要_T("")宏

4 printf函数族必须修改为wprintf,不过要注意千万不要使用wprintf函数来解析char型

很多时候程序中既需要Unicode,又需要使用ASCII,这时需要用到操作系统的2个API

WideCharToMultiByte用来将Unicode字符串转化为MBCS的

MultiByteToWideChar用来将MBCS字符串转化为Unicode的

一些注意事项:

在Unicode编码下,sizeof没那么可靠了,memset( 0 sizeof())的习惯用法可能会出大错,改成memset(0sizeof()/szieof(TCHAR))就没事了,呵呵

在Unicode下,一个中文字符就是一个字符,len = strlen() / 2;这样可不行了

用VC6进行UNICODE编程

最近试图将自己的程序编译成Unicode版本,费了不少力气,相关内容整理如下,适用于VC6,但VC7、VC8应该也差不多的(后者新建项目缺省即按Unicode编译)。

1. 添加 UNICODE 和 _UNICODE 预处理定义

位置:Project Settings -> C/C++ -> Preprocessor definitions

添加了这两个定义后,MFC的一些内置类型如 TCHAR、CString 都将转为支持宽字符类型(wchar_t)

2. 使用宽字符相关类型,如:

char -> TCHAR、char * -> LPTSTR、const char * -> LPCTSTR

3. 对字符串常量使用 _T() 宏

4. 替换C库中的中字符串操作函数,如 strlen -> _tcslen、strcmp -> _tcscmp 等

类似的还有C库中字符串与数字的转换函数,如 atoi -> _ttoi、itoa -> _itot 等

5. 将 Project Settings -> link -> Output -> Entry Point 设为 wWinMainCRTSTartup

否则会有如下错误:
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16

6. C++标准库中的string,有对应的宽字符版本wstring,两者均为basic_string的特化版本

可在StdAfx.h中:

#ifdef _UNICODE
#define tstring wstring
#else
#define tstring string
#endif

然后在代码中使用 tstring 即可,类似的还有 fstream/wfstream、ofstream/wofstream 等

7. 宽字符版本的英文字符仍可直接与整型值进行比较,如:

CString s = _T("ABC");
ASSERT(s[0] == 'A');

8. 对于仍需使用ANSI字符串的地方,如第三方类库的接口,仍可继续使用;如需进行Unicode字符串和ANSI字符串的互转换,可使用 MultiByteToWideChar 和 WideCharToMultiByte

二元“=”: 没有找到接受“CString”类型的右操作数的运算符(或没有可接受的转换) 空初始值设定项对于带有未指定绑定的数组无效 “_AFXDLL”: 宏重定义 'CWinApp::Enable3dControls': CWinApp::Enable3dControls is no longer needed. You should remove this call. “CListCtrl::InsertColumn”: 没有重载函数可以转换所有参数类型 “CListCtrl::InsertColumn”: 没有重载函数可以转换所有参数类型 “CListCtrl::InsertColumn”: 没有重载函数可以转换所有参数类型 “CListCtrl::InsertColumn”: 没有重载函数可以转换所有参数类型 “CListCtrl::InsertColumn”: 没有重载函数可以转换所有参数类型 “CListCtrl::InsertColumn”: 没有重载函数可以转换所有参数类型 “BOOL CListCtrl::SetItemText(int,int,LPCTSTR)”: 无法将参数 3 从“const char [3]”转换为“LPCTSTR” “BOOL CListCtrl::SetItemText(int,int,LPCTSTR)”: 无法将参数 3 从“const char [3]”转换为“LPCTSTR” 二元“=”: 没有找到接受“CString”类型的右操作数的运算符(或没有可接受的转换) 二元“=”: 没有找到接受“CString”类型的右操作数的运算符(或没有可接受的转换) 二元“=”: 没有找到接受“CString”类型的右操作数的运算符(或没有可接受的转换) 二元“=”: 没有找到接受“CString”类型的右操作数的运算符(或没有可接受的转换) 二元“=”: 没有找到接受“CString”类型的右操作数的运算符(或没有可接受的转换) “int CWnd::MessageBoxW(LPCTSTR,LPCTSTR,UINT)”: 无法将参数 1 从“const char [15]”转换为“LPCTSTR” “int CWnd::MessageBoxW(LPCTSTR,LPCTSTR,UINT)”: 无法将参数 1 从“const char [15]”转换为“LPCTSTR” “int CWnd::MessageBoxW(LPCTSTR,LPCTSTR,UINT)”: 无法将参数 1 从“const char [15]”转换为“LPCTSTR”
03-24
#ifndef _INI_DOCUMENT_H_ #define _INI_DOCUMENT_H_ #define ASCIILINESZ 1024 //每行文本能容纳文字的最大长度 #define ASCIISECTIONSZ 256 //每个Section的最大长度 #define ASCIIKEYSZ 256 //每个Key的最大长度 #define ASCIIVALUESZ 256 //每个Value的最大长度 #define ASCIIDESCSZ 512 //每个注释的最大长度 #include "RBTree.h" #include "Section.h" #include "Key.h" #include "Value.h" namespace INI { using KeyMap = RBTREE::RBTree<Key, Value>; using SectionMap = RBTREE::RBTree<Section, KeyMap>; class IniDocument { typedef enum class _line_status_ : unsigned char { LINE_ERROR,//未知行 LINE_DESCRIPTION,//注释行 LINE_SECTION,//节点行 LINE_KEY//键值行 } line_status; public: IniDocument(const char* _szFile); ~IniDocument(); KeyMap& operator[](const char* _szSection); void Clear(); private: void load(); line_status parse(char* _szLine, char* _szSection, char* _szKey, char* _szValue, char* _szDesc); private: char* m_szFile;//文件路径 SectionMap m_sectionMap; //存储section的映射 }; } #endif // !_INI_DOCUMENT_H_ #include "../inc/IniDocument.h" INI::IniDocument::IniDocument(const char* _szFile) : m_szFile(nullptr) { if (!_szFile || _szFile[0] == '\0') { //错误处理: 文件路径不能为空 throw("文件路径不能为空."); } size_t nLen = strlen(_szFile); size_t nSize = ((nLen + 1) + 3) & ~3; if (nLen >= _MAX_PATH) { //错误处理: 文件路径过长 throw("文件路径过长."); } m_szFile = new char[nSize]; memcpy(m_szFile, _szFile, nLen + 1); load(); //加载文件内容到内存 } INI::IniDocument::~IniDocument() { Clear(); } INI::KeyMap& INI::IniDocument::operator[](const char* _szSection) { //返回对应Section的映射 return m_sectionMap[Section(_szSection)]; } void INI::IniDocument::Clear() { if (m_szFile) { delete[] m_szFile; m_szFile = nullptr; } for (SectionMap::iterator it = m_sectionMap.begin(); it != m_sectionMap.end(); ++it) { //清空每个Section的映射 it->second.Clear(); } m_sectionMap.Clear(); //清空Section映射 } void INI::IniDocument::load() { /*特别注意 读出的ini内容一般都是utf-8,而保存在内存中的内容也是utf-8.直接保存到文件没问题 但如果查找就会出问题。要把查找的中文字串 转成utf8再查找才能找到 但每次查找section或者key都转一次utf8就浪费了 解决方案: 从文件解析出来的utf8内容转回普通char字串。再保存到内存中,这样代码直接就能查找到。 而保存回文件的时候,把内存的字串转回utf8再保存即可 如果不想频繁转换,直接使用ANSI编码文件即可 */ #pragma warning(disable:4996) FILE* pFile = fopen(m_szFile, "r"); #pragma warning(default:4996) if (!pFile) { printf("<IniDocument::load> 打开Ini文件失败,路径: %s\n", m_szFile); return; } char szLineBuffer[ASCIILINESZ] = { 0 };//把文件中的内容逐行读入此内存中 char szSection[ASCIISECTIONSZ] = { 0 };;//把节点解析完成后放入此内存中 char szKey[ASCIIKEYSZ] = { 0 };;//把Key解析完成后放入此内存中 char szValue[ASCIIVALUESZ] = { 0 };;//把Value解析完成后放入此内存中 char szDesc[ASCIIDESCSZ] = { 0 };;//把注释读入此内存中 size_t nLines = 0; //记录当前的行数 size_t nLen = 0; //记录当前行的长度 KeyMap* pKeyMap = nullptr; //用于存储当前解析的Key映射 Desc desc; //用于存储当前解析的全行注释 while (fgets(szLineBuffer, ASCIILINESZ, pFile))//每次读取一行内容 { ++nLines; //行数自增 if (szLineBuffer[0] == '\n')continue;//如果取出来的内容第一个字符就是换行符,无条件取下一行内容 nLen = strlen(szLineBuffer);//更新行内容的字串长度 if (szLineBuffer[nLen - 1] != '\n' && !feof(pFile)) { //如果此行的结束符 不是换行符\n 并且 文件并未结束 -> 如果都满足条件,说明此行肯定是太长了。应当调整行的最大长度。默认:ASCIILINESZ=1024 printf("<IniDocument::load> 行 %zu 内容过长,请检查文件内容.\n", nLines); continue; //跳过此行 } //开始进行字串解析 switch (parse(szLineBuffer, szSection, szKey, szValue, szDesc))//进入解析过程 { case line_status::LINE_ERROR://未知行 break; case line_status::LINE_DESCRIPTION://全行注释 { desc = szLineBuffer; //将注释内容存储到Desc对象中 break; } case line_status::LINE_SECTION://节点 { Section section(szSection); //创建Section对象 if (desc.m_szDesc)section.m_vecDesc[0] = std::move(desc); //将注释内容存储到Section对象中 if (szDesc[0] != '\0')section.m_vecDesc[1] = szDesc; //如果有第二个注释内容,存储到Section对象中 pKeyMap = &m_sectionMap[section]; //获取当前Section映射 break; } case line_status::LINE_KEY://键值 { if (!pKeyMap) { printf("<IniDocument::load> 键值行 %zu 解析失败<没有Section>,请检查文件内容.\n", nLines); continue; //如果没有Section映射,跳过此行 } Key key(szKey); //创建Key对象 Value value(szValue); //创建Value对象 if (desc.m_szDesc) //如果有全行注释内容 { //将注释内容存储到Key对象中 key.m_vecDesc[0] = std::move(desc); } if (szDesc[0] != '\0') //如果有行块注释内容 { key.m_vecDesc[1] = szDesc; //将行块注释内容存储到Key对象中 } (*pKeyMap)[key] = std::move(value); //将Key和Value存储到当前Section映射中 break; } default: break; } szLineBuffer[0] = 0; //清空行内容 szSection[0] = 0; //清空Section内容 szKey[0] = 0; //清空Key内容 szValue[0] = 0; //清空Value内容 szDesc[0] = 0; //清空注释内容 } fclose(pFile); //关闭文件 } INI::IniDocument::line_status INI::IniDocument::parse(char* _szLine, char* _szSection, char* _szKey, char* _szValue, char* _szDesc) { #pragma warning(disable:4996) line_status sta = line_status::LINE_ERROR;//分析结果 size_t nCount = 0; //记录sccanf匹配的数量 if (_szLine[0] == '#' || _szLine[0] == ';')//开头就是单行注释的解析符 {//说明全行都是注释 sta = line_status::LINE_DESCRIPTION; } //开始解析节点 Section //(必须遵守一个规则,从有到无,比如结尾带分号的应该先做匹配。 //然后再到结尾不带分号的。因为如果先匹配不带分号的,带分号的也会匹配得上。就会莫名多个分号了) //例如: key = value; 如果先匹配无分号的,那么值就是 value; 达不到预期效果,结尾多了个分号 else if ( //以下匹配 节点(后面带注释) (nCount = sscanf(_szLine, "[%[^]]]%[^\n]", _szSection, _szDesc)) == 2 ||//例子: [section01]#this is description 01或者[section01];this is description 01 (nCount = sscanf(_szLine, "[%[^]]]%[^\n]", _szSection, _szDesc)) == 1//例子: [section] ) { sta = line_status::LINE_SECTION; } //开始解析键值 key = value else if ( //(必须遵守一个规则,从有到无,比如结尾带分号的应该先做匹配。然后再到结尾不带分号的。因为如果先匹配不带分号的,带分号的也会匹配得上。就会莫名多个分号了) (nCount = sscanf(_szLine, "%[^=]=%[^;]%[^\n]", _szKey, _szValue, _szDesc)) == 3 || //例子: key1=value1;this is description 01 (nCount = sscanf(_szLine, "%[^=]=%[^#]%[^\n]", _szKey, _szValue, _szDesc)) == 3 ||//例子: key2=value2#this is description 01 (nCount = sscanf(_szLine, "%[^=]=%[^\n]", _szKey, _szValue)) == 2//例子: key1=value1 ) { sta = line_status::LINE_KEY; } #pragma warning(default:4996) return sta; } 把我提供给你的所有代码再结合这次给你的代码,你可以尝试重构组合,是否已经形成一个完整的ini解析器
最新发布
07-26
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值