C++字符串常量跨平台编译问题

2023-11-14

C++字符串常量跨平台编译问题(与字符串编码相关),有需要的朋友可以参考下。

  1. 问题 在C++代码中,给一个string类型的变量赋值一个中文字符串常量,例如: string s = “中文字符串” 变量s中保存的字节内容是什么?如果源文件的编码格式转换了,比如从GB2312转换为UTF-8,变量s中的内容会发生变化吗?其结果是否与编译器有关?
    假设有一个C++源程序:
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
int main(int argc, char * argv[])
{
string s="中文字符串";
cout << "Length of string: "<< s.size()<< endl;
cout << "Bytes in string: "<< endl;
for (string::size_type i=0; i< s.size();i++)
{
cout <<setfill('0')<< setw(2)<<hex<< (int)(0x0ff &s[i]) << " ";
}
cout << endl;
return 0;
}

使用三种不同的编译器编译: Windows平台(简体中文): Vs2008 SP1 Linux x86(Ubuntu 10.04 LTS): G++ 4.4.3 ARM Linux: arm-none-linux-gnueabi-g++ 4.3.2(From Sourcery G++ Lite 2008q3-72)
2. 实验1 在Vs2008中创建一个空的VC++项目,并创建一个cpp文件,将上面的代码放进去,编译运行,得到的结果是: Length of string: 10 Bytes in string: d6 d0 ce c4 d7 d6 b7 fb b4 ae
在Linux下使用g++编译: g++ -o testtest.cpp,运行结果相同
使用ARM Linux的交叉编译工具: arm-none-linux-gnueabi-g++ -o testtest.cpp编译,并在目标板上运行,结果也相同。
在简体中文的Windows中运行的Vs2008,创建的C++源文件的缺省编码方式为CP936,即GB2312,该编码下,每个中文字符对应2个字节。通过检查(可以使用Python把那几个字节来换转换一下编码),可以确定,输出的10个字节就是这5个中文字符的CP936的编码值。
考虑到源代码的编码格式也是CP936,似乎可以得到如下结论,在这几个编译器中,字符串常量的字节内容,直接就是字符串在源代码文件中对应的字节内容。
照这个结论,如果把源文件转换为UTF-8编码存储,对应输出的字节应该是这几个中文字符的UTF-8编码,而长度应该是15(这几个中文字符的UTF-8编码都是3个字节)。
3. 实验2
在Vs2008中,打开源代码,选择文件菜单中的“高级保存选项",修改编码为Unicode (UTF-8带签名)-代码页65001,把同样的源代码分别在三个编译器中编译运行。 Vs2008:结果仍然与原来相同,即长度为10,字节内容为CP936的10个字节,而不是UTF-8的15个字节; G++(x86): 长度变为15,字节内容为15个字节,经验证为那几个中文字符对应的UTF-8编码; G++ ARM: 编译错误, error: stray ‘/357’ in program error: stray ‘/273’ in program error: stray ‘/277’ in program 从实验的情况来看,x86下的GCC确实是按照实验1中的结论工作,字符串常量的值随文件编码改变而发生了相应的改变;而Vs2008输出的结果却表明,Vs2008似乎自动将编码转换成了原来的样子即CP936的形式,这个结论还是挺意外的。 而G++ ARM的编译错误,应该是由于这个版本的编译器不能识别源文件插入的BOM导致的,后来使用更新的交叉编译器(G++ 4.4.1 from Sourcery G++ lite2010q1-202)证实了这一点,并且输出结果与x86中的G++编译的完全相同。
到这一步,可以初步得出结论,G++不会试图转换常量的字符串编码,会直接使用与源文件字符编码对应的字符串常量。Vs2008会试图将Unicode的编码格式转换成对应地区(Locale)的缺省编码(简体中文系统下,为GB2312即代码页CP936),并按照这个编码的内容来确定常量字符串的值(后者有一些推论在里面)。
4. 跨平台编译的解决方案
如果一份代码需要在Visual Studio下编译,又需要使用GCC编译,如何选择文件的编码方式比较合理呢。 如果代码中不会出现中文或其它非Ascii字符,这当然不是问题,因为AsciiUTF-8及CP936这些编码格式在ASCII字符的范围内都是一致的。
如果有中文这类字符串常量,就会有点麻烦。如果全部采用UTF-8(带BOM),这样,在Windows下可以正常工作,而且类似于printf或cout这样的控制台输出,都可以正常输出中文字符串,因为如前面的实验,VisualStudio在编译时已经把UTF-8编码的源文件中的字符串常量编程了本地的编码,因此在操作系统的控制台窗口中输出完全没有问题。相反,如果直接向控制台输出一个UTF-8编码的中文字符串,显示是不正确的。 而在Linux下(以Ubuntu为例),由于GCC直接使用了UTF-8源代码文件中字符串的编码,而Ubuntu下,输出的UTF-8编码字符串可以被正确显示,而如果直接输出CP936编码的字符串就不行了。 因此,如果可以把所有的源代码全部转换为带UTF-8(带BOM)的编码存储,VS和GCC编译的结果,都可以得到正确的显示输出。
不过,如果要把字符串通过网络传输给另一个程序;或直接把字符串写到文件中去,这种方式下,传输的内容是不同的。在Windows下,传输的是CP936的编码内容,根据前面的例子“中文字符串"这五个汉字共传输10个字节;而用GCC编译的结果却传输15字节的UTF-8编码字符串。接收数据的程序,则需要来源的不同根据情况进行解码。如果接收程序是使用Java编写的,由于Java里面的字符串都使用Unicode,因此需要根据不同的来源,使用CP936或UTF-8来解码收到的内容为Unicode字符串。通常,客户端程序是不应该关心提供数据的服务程序是VS编译的还是GCC编译的,因此这样分别进行解码比较困难。
因此,更常见的做法是传输时使用统一的编码格式,而程序在从传输信道获得字符串或向传输信道写入字符串时,根据自身的情况进行解码或编码。而在UTF-8和CP936之间,选择更恰当的一种作为信道中传输的字符串,不是一个困难的选择。UTF-8可以传输所有的字符,而CP936只能传输简体中文的字符,从通用的角度考虑肯定应该是UTF-8。
也就是说,如果代码都使用UTF-8编码,而信道中传输字符串时也使用UTF-8编码的情况下,在VS中编译时,需要在输出字符串到信道或从信道获得字符串时,要进行CP936(或其它多字节编码)与UTF-8之间的转换。转换可以通过Windows提供的SDK完成,具体方法是,先将多字节字符串转换为Windows的WideChar的字符串,再将WideChar转换为UTF-8字符串(没有直接转换的API),反之类似。 相关的Windows的SDK为: MultiByteToWideChar WideCharToMultiByte 后者可以将WideChar转换为CP936或UTF-8.不过函数调用时,需要注意转换后的UTF-8所需要的存储长度要大于输入的WideChar字符串的长度,具体长多少,是不确定的,与字符串内容有关。有两个办法,一个是直接分配足够大的长度用来存储编码后的结果,例如WideChar字符串长度的四倍;另一个方法是一次一次试,先分配与WideChar相同的长度,调用WideChartToMultiByte,如果返回值是0且GetLastError返回 ERROR_INSUFFICIENT_BUFF,就在把缓冲区扩大点再试。
为了方便转换,写了一个Python脚本,可以用来转换一个目录内所有的.h,.cpp,.hpp,.cxx,.c文件;同时,可以识别已经转换过的文件,和有BOM标记的(UTF-16,UTF-32文件);如果没有标记,则认为是CP936编码的文件,将执行转换,脚本文件可在这里下载。 使用方法为: python toutf8.py -d youSourceDir
StackOverflow上的这篇文章同样解释了如何在C++代码里实现UTF8常量字符串的问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++字符串常量跨平台编译问题 的相关文章

随机推荐

  • 查看Oracle数据库的用户名和密码

    运行 cmd 按如下输入命令 sqlplus as sysdba sysdba为超级用户 alter user 用户名 account unlock 解除锁定 必须带 号 注意用英文字符 alter user 用户名 identified
  • 网页版百度网盘倍速方法

    打开控制台在console中输入以下代码 videojs getPlayers video player html5player tech setPlaybackRate 2 然后回车即可 后面数字即为速度可以自由定义 附 打开控制台办法
  • Selenium Python2022(二)

    您可能需要在多种情况下针对不同的浏览器 例如Firefox Chrome Internet Explorer Edge 测试代码 跨不同浏览器测试网站的做法称为自动浏览器测试 要使用Selenium自动化测试执行自动浏览器测试 您应该在单元
  • 元宇宙时代超高清视音频技术白皮书关于流媒体协议和媒体传输解读

    流媒体协议 元宇宙业务场景对流媒体传输的实时性和互动性提出了更高的要求 这就需要在传统的 RTMP SRT HLS 等基础上增加实时互动的支持 实时互动 指在远程条件下沟通 协作 可随时随地接入 实时地传递虚实融合的多维信息 身临其境的交互
  • 数据可视化:在 React 项目中使用 Vega 图表 (二)

    上一篇讲了如何在 React 项目中用 Vega Lite 绘制基本的 area chart 图表 本篇将介绍如何绘制多层图表 如何添加图例 多层图表 通过上一篇文章 我们知道了可以通过 mark encoding 等来描述我们想要的图表
  • STM32F103ZET6【HAL函开发】STM32CUBEMX------7.DAC实验

    STM32F103ZET6有2 个 DAC 转换器 每个转换器对应 1 个输出通道 对应的IO口如下 DAC OUT1 PA4 DAC OUT2 PA5 目的 DAC通道2可以输出3 3以下的任意电压 同时通过ADC1通道8采集到 打印到串
  • Vue中div contenteditable 的光标定位

    在Vue做项目时 做了一个div contenteditable true 的组件作为文本输入框 在非手动输入值后 光标会丢失 经测试以下这段代码可用 直接将光标定位到最后 function keepLastIndex obj consol
  • 刷脸支付大量的商户正在大步踏进数字化

    传统的商业零售不缺乏客源 但是弊端在于 顾客和商家建立有效接触的时间太短 可能只有结账时的短短十秒左右 很多人都有过去便利店买东西 店员安利办会员卡的经历 但是绝大多数人只想尽快买单走人 商家想做进一步精准推进和营销实在难上加难 蜻蜓是首款
  • Python3,我把新年祝福写在“雨“中,你看,雨一直下,气氛还算融洽,在同个屋檐下....

    新年愿望写在 雨 中 1 引言 2 代码实战 2 1 模块介绍 2 1 1 Pygame 介绍 2 1 2 Pygame的display介绍 2 1 3 Pygame的event介绍 2 1 4 Pygame的font介绍 2 2 代码示例
  • C++之弱引用智能指针weak_ptr的验证

    目录 shared ptr带来的问题 标准库weak ptr弱引用验证 标准库中weak ptr交叉引用的验证 shared ptr带来的问题 在交叉引用时 会造成堆上资源无法释放的问题 使用weak ptr弱引用就可以打破这个交叉引用 因
  • 如何判断一组数据是否符合正态分布呢?

    在很多模型及假设检验中都需要满足一个假设条件 数据需服从正态分布 这篇文章主要讲讲如何判断数据是否符合正态分布 主要分为两种方法 描述统计方法和统计检验方法 判断一组数据是否为正态分布的方法 描述统计方法 Q Q图 P P图 直方图 茎叶图
  • C程序argc、argv的使用

    前提 C文件编译 汇编后生成的 exe文件 就可以通过命令行来执行该exe文件 命令行执行 exe文件 用微软推出的powershell 格式是 start exe 或者 start exe 用powershell无法输入argv 总是执行
  • Spring boot 序列化规则

    Include Include ALWAYS 默认 Include NON DEFAULT 属性为默认值不序列化 Include NON EMPTY 属性为 空 或者为 NULL 都不序列化 Include NON NULL 属性为NULL
  • Window命令激活(不需要安装软件,激活180天)

    1 以管理员方式运行命令 注解 该命令为设置KMS服务器地址 slmgr skms kms micaesoft com 2 激活 注解 该命令为向KMS服务器发起请求 slmgr ato 其他 激活完毕后 若要查看许可证详细信息 可使用命令
  • 方差与分类分析

    分类数据与方差分析 1 分类数据 2 chi 2 2 统计量 这个统计量主要用于测定两个分类变量之间的相关程度 若用 f 0
  • python增加一列数据_使用Python向DataFrame中指定位置添加一列或多列的方法

    对于这个问题 相信很多人都会很困惑 本篇文章将会给大家介绍一种非常简单的方式向DataFrame中任意指定的位置添加一列 在此之前或许有不少读者已经了解了最普通的添加一列的方式 如下 import pandas as pd feature
  • 多边形区域填充算法_求取任意两个多边形的IOU

    在目标检测中 真值与预测值的交并比IoU Intersection over Union 是用来评价检测模型的一个重要指标 通俗意义上来讲 也就是检测结果与真实的结果重叠的区间所占两者面积之和的权重 一般说来 当IoU gt 0 5 我们认
  • .net 面试题(高级开发人员篇)

    1 DateTime Parse myString 这行代码有什么问题 有问题 当myString不能满足时间格式要求的时候 会引发异常 建议使用DateTime TryParse 2 PDB是什么东西 在调试中它应该放在哪里 PDB是用于
  • python opencv打开摄像头失败报错,尝试使用网络摄像头python opencv捕获视频时gstreamer严重错误...

    i m trying to take a video with webcam using opencv and python with a simple code import numpy as np import cv2 cap cv2
  • C++字符串常量跨平台编译问题

    C 字符串常量跨平台编译问题 与字符串编码相关 有需要的朋友可以参考下 问题 在C 代码中 给一个string类型的变量赋值一个中文字符串常量 例如 string s 中文字符串 变量s中保存的字节内容是什么 如果源文件的编码格式转换了 比