Unicode编码中的零宽空格0x200B
目录
1.前言
2.零宽空格0x200B
3.项目异常场景
4.异常成因
5.解决方案
前言
本篇文章主要是讲解关于Unicode编码中的零宽空格0x200B导致的编译器识别函数或对象异常的情况,并且提供了在Visual Studio(后续简称Vs)中对应的解决方法和预防方案。
零宽空格0x200B
零宽空格(ZWSP)是一个特殊的Unicode字符,编码为U+200B,它在视觉上不可见且不占用任何空间。尽管不可见,它在文本中实际存在,常用于文本格式化、断行控制、隐秘标记等场景。
零宽空格实际运用场景如下:
1.文本格式化与断行控制:在长单词或URL中插入零宽空格,允许在不影响阅读的情况下进行自动换行,尤其在HTML或Web内容中非常常见。
2.隐秘标记与信息隐藏:用于嵌入隐形信息,例如数字水印或隐藏标记,甚至对抗自动文本分析工具。
3.程序开发与排版设计:在代码中处理长字符串或调整文本显示时使用,尤其在多语言排版中帮助优化布局。
项目异常场景
对于零宽空格0x200B在项目中的影响,博主本人表示一言难尽。主要原因是该编码你即看不见又不占任何空间,但是编译器却能识别到编码,然后影响到项目中函数的跳转和对象的检索,具体场景如下:
参考图1,你可以发现在ModuleIntegration类型中定义了一个punlic权限的槽函数,并且该函数的命名和Vs的输出窗口中的提示一致,但是无论是清理编译缓存还是复制粘贴都提示找不到该槽函数
图1.找不到类型中定义的函数
而我们在点击输出窗口中的列时,则会跳转到moc_ModuleIntegration.cpp文件中,该文件中已经将我们声明的槽函数进行链接,并且经过检查该函数的类型和信号类型完全一致。
图2.moc_ModuleIntegration.cpp文件
此时我怀疑是没有提供函数定义或者提供的函数定义参数有问题?于是跳转到了cpp文件检查了该槽函数的定义,并且保证函数定义和声明一致。但是在编译时还是提示该函数并不是ModuleIntegration类型的成员,这很奇怪,无论是参数,返回值还是函数命名都保持一致,为什么编译器还是找不到该槽函数?而我将该槽函数声明为普通函数时则不报错
图3.ModuleIntegration.cpp文件
除此之外,我在dataVizPreprocessing类型中定义了一个信号,该信号的参数为三个QVector<double>。但是当我使用connect()函数进行信号和槽的链接时提示该信号是未定义的标识符,这个报错首先令我想到的是信号的参数没有被Qt元对象识别?但是由于这三个参数都为QVector<double>类型,所以我果断排除了该原因。此外我还怀疑了是否是Qt5和Qt6的版本问题导致的信号槽链接出错,但是经过排查也否定了这个原因。
图4.未定义的信号
排查了Qt导致的问题后,我把目光放到了emit语句上,由于该信号传递的是自定义类型中的成员,于是我通过跳转检查并分析了该自定义类型的成员和信号参数是否一致,结果如图5所示的确是一致的。
图5.自定义类型成员
异常成因
通过异常场景,我们可以发现零宽字符导致的异常千奇百怪,而大多数情况我们只能根据编译器的提示来分析该异常的成因。在通过编码和copy大法排查后,最终才定位到了Unicode编码的零宽空格0x200B中,而为了复现该问题博主也是尝试了各种快捷键想要输入该编码,但是最后都没有成功。那么为什么写的好好的代码中会存在该空格呢?最后经过排查才发现是由于Ai的回复中插入了零宽空格0x200B进行排版,而我们在copy后该字符也被一并的复制到了编译器中,但是由于肉眼看不到导致我们忽略了该字符。
解决方案
显示零宽空格0x200B并且删除该字符则很简单,在Vs中我们可以通过下载Invisible Character Visualizer插件对项目中的零宽空格0x200B进行显示。
图6.Invisible Character Visualizer插件
在下载并安装成功该插件后,我们返回项目中则可以看到零宽空格0x200B
图7.零宽空格0x200B
除此之外,我们也可以通过Windows自带的记事本来显示零宽空格0x200B,如图8所示记事本当前的编码为UTF-8
图8.记事本当前编码
我们可以通过在记事本中右键,在点击显示Unicode控制字符后,则会见代码中隐藏的零宽空格0x200B显示出来
图9.在记事本中显示零宽空格0x200B