栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

VS C++部分踩坑解决方案(UTF-8、第三方库配置)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

VS C++部分踩坑解决方案(UTF-8、第三方库配置)

使用C++一年多,在windows下淌过不少水,有些简单但常见的坑,网络上一直少有方便的处理方法,在此抛砖引玉,涉及UTF-8编码、第三方库配置。以下配置或代码均在VS2022上使用,其他版本略有异同。

一、文件默认保存为UTF-8

需要保存为UTF-8的原因不做赘述,网络上常见的方法有:

1. “高级保存选项”保存为UTF-8,但仅针对单文件,略繁琐;

2. 通过VSCode或Notepad等编辑器保存为UTF-8,操作多文件比方法1略简单;

3. 使用插件“Force UTF-8(No BOM)”等,保存简单,但影响调试;

4. 使用.editconfig文件,放置在源代码目录、项目目录、项目的上级目录都可以,VS保存新文件时会自动查找其各上级目录中是否有.editconfig文件,按设置保存,但对手动进行过对应设置的不生效。

若需要新项目文件自动保存为UTF-8,只建议使用方法4,以下为本人的.editconfig配置:

root = true

[*]
# 文件字符集编码方式             [latinl, utf-8, utf-8-bom, utf-16be, utf-16le]
charset = utf-8
# 行尾文件格式                  [lf, cr, crlf]
end_of_line = crlf
# 文件是否应以换行符结尾         [true, false]
insert_final_newline = true

[**.{c,h,cc,cxx,cpp}]
# 是否从行尾删除空格             [true, false]
trim_trailing_whitespace = true
# 缩进样式                      [tab, space]
indent_style = space
# 缩进大小                      [tab, 整数]
indent_size = 2
# 单个制表位字符的宽度           [正整数]
table_width = 2
二、UTF-8编码的源文件在编译时出警告或错误,“该文件包含不能在当前代码页(936)中表示的字符”“常量中有换行符”等

这种警告或错误均为VS对UTF-8 withot BOM的代码页支持不完全,在项目属性→C/C++→命令行→其它选项中,填入“/utf-8”,如下图所示:

若本来就有这个选项,仍然出现警告报错,请检查编码,或者去掉/utf-8后面的空格再保存一次。

三、UTF-8编码字符串在文件打开失败、控制台输出乱码

 网络上常见方法有:

1. 修改本地代码页;

2. 使用“chcp”修改控制台代码页;

以上方法均不建议,会带来许多其它问题,C/C++本身有本地化库locale.h、clocale、locale,可以在开头设置本地全局环境。

clocale头文件:

#include 

int main()
{
  std::setlocale(LC_ALL, "zh_CN.UTF-8");
  //****
  return 0;
}

locale头文件

#include 

int main()
{
  std::locale::global(std::locale("zh_CN.UTF-8"));
  //****
  return 0;
}

另外遇见一部分电脑的系统代码页既不是CP65001(UTF-8)也不是CP936(GBK),而是CP37等其他代码页,会在一些奇怪的地方出现问题。 

 注:linux要使用需要安装语言包支持zh_CN.UTF-8。

四、UTF-8与GBK或其它ANSI编码互转

网络上常见方法有:

1. 使用windows的接口进行转换,但不利于跨平台;

2. 使用std::wstring_convert 与 std::codecvt_utf8进行转换,但C++17后已经弃用;

3. 使用icu等第三方库。

这里方法2中提及的方法被弃用,但其核心的std::codecvt_byname仍然长期存在,可以使用下面的方法进行转换:

#include 
#include 

template 
class CodecvtByname : public std::codecvt_byname
{
public:
  CodecvtByname(const char *id, size_t refs = 0)
      : std::codecvt_byname(id, refs)
  {
  }
  CodecvtByname(const std::string &id, size_t refs = 0)
      : std::codecvt_byname(id, refs)
  {
  }
  ~CodecvtByname() = default;
};

//@brief 转换宽字符串到窄字符串
//@param [in] wstr  源宽字符串
//@param [in] id    目标代码页id
//@return 窄字符串
auto FromWstr(const std::wstring &wstr, const std::string &id) -> std::string
{
  CodecvtByname cvt(id);
  std::mbstate_t mb = std::mbstate_t();
  std::string str(wstr.size() * 4, ''); // 仅考虑到最长四字节编码
  const wchar_t *fromNext = nullptr;
  char *toNext = nullptr;
  cvt.out(mb, &wstr[0], &wstr[wstr.size()], fromNext, &str[0], &str[str.size()], toNext);
  return std::string(str.c_str());
}

//@brief 转换窄字符串到宽字符串
//@param [in] str 源窄字符串
//@param [in] id  源代码页id
//@return 宽字符串
auto ToWstr(const std::string &str, const std::string &id) -> std::wstring
{
  CodecvtByname cvt(id);
  std::mbstate_t mb = std::mbstate_t();
  std::wstring wstr(str.size(), '');
  const char *fromNext = nullptr;
  wchar_t *toNext = nullptr;
  cvt.in(mb, &str[0], &str[str.size()], fromNext, &wstr[0], &wstr[wstr.size()], toNext);
  return std::wstring(wstr.c_str());
}

//@brief 转换字符串编码
//@param [in] str       源字符串
//@param [in] sourceId  源代码页id
//@param [in] targetId  目标代码页id
//@return 目标编码的字符串
auto CodeConvert(const std::string &str, const std::string &sourceId, const std::string &targetId)
    -> std::string
{
  std::wstring wstr = ToWstr(str, sourceId);
  return FromWstr(wstr, targetId);
}

//@brief GBK编码字符串转为UTF-8编码字符串
//@param [in] str GBK编码的字符串
//@return UTF-8编码的字符串
auto GbkToUtf8(const std::string &str) -> std::string
{
#ifdef _MSVC_LANG
  return CodeConvert(str, ".936", ".65001");
#else
  return CodeConvert(str, "zh_CN.GBK", "zh_CN.UTF-8");
#endif // _MSVC_LANG
}

//@brief UTF-8编码字符串转为GBK编码字符串
//@param [in] str UTF-8编码的字符串
//@return GBK编码的字符串
auto Utf8ToGbk(const std::string &str) -> std::string
{
#ifdef _MSVC_LANG
  return CodeConvert(str, ".65001", ".936");
#else
  return CodeConvert(str, "zh_CN.UTF-8", "zh_CN.GBK");
#endif // _MSVC_LANG
}

//@brief 本地编码字符串转为UTF-8编码字符串
//@param [in] str 本地编码的字符串
//@return UTF-8编码的字符串
auto AnsiToUtf8(const std::string &str) -> std::string
{
#ifdef _MSVC_LANG
  return CodeConvert(str, "", ".65001");
#else
  return CodeConvert(str, "", "zh_CN.UTF-8");
#endif // _MSVC_LANG
}

//@brief UTF-8编码字符串转为本地编码字符串
//@param [in] str UTF-8编码的字符串
//@return 本地编码的字符串
auto Utf8ToAnsi(const std::string &str) -> std::string
{
#ifdef _MSVC_LANG
  return CodeConvert(str, ".65001", "");
#else
  return CodeConvert(str, "zh_CN.UTF-8", "");
#endif // _MSVC_LANG
}

 注:linux要使用需要安装语言包支持zh_CN.UTF-8和zh_CN.GBK。

五、第三方库导入

C++的第三方库是个无敌大坑,而且不像linux有pkg-config可以使用,windows虽然出现了vcpkg,但各种原因有些库只能本地编译加载,每次VS新项目都手动配置第三方库非常繁琐。可以使用项目属性表,多项目可以共用。 

属性管理器(可以从视图中找到)→【某项目】→添加现有属性表 

https://download.csdn.net/download/sheenwit/86246450

这里以本人的opencv属性表为例,分别对应附加包含目录、附加库目录、附加依赖项、调试环境变量,之后再在调试环境中增加一条“$(OPENCV_PATH);”,如果需要添加其他属性,在具体项目中的.vcxproj文件中就可查到对应的属性名。修改文件时需要注意对应的debug/release/win32/x64。

六、min、max宏

项目如果包含windows头文件,会引入微软的min、max宏,带来一大堆问题,直接添加预处理器定义NOMINMAX即可。

七、项目模板

像上面的全局locale、/utf-8、NOMINMAX宏基本每次都会用到,可以直接创建项目模板,具体操作这里不赘述。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1018021.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号