坑爹的 GBK:大家都应该去用 UTF-8
最近在把我用 C 写的一批 Linux 工具移植到 Windows 上,在字符编码上遇到了大坑。
举个简单的例子:数文件层级。
在 Linux 上,我们数斜杠数量就好。
在 Windows 上,再加上反斜杠,应该就好了。——我是这样想的。
#include <stdio.h>
int main(int argc, char *argv[]) {
int level;
const char *p;
if (argc < 2) {
return 1;
}
for (level = 0, p = argv[1]; *p; p++) {
if (p == '/' || p == '\') {
level++;
}
}
printf("%d\n", level);
return 0;
}
用 MinGW 的 GCC 编译一下,然后跑几个用例:
gcc -o getlevel.exe getlevel.c
C:\>getlevel C:\浙江省\宁波市\北仑区\小港街道.txt
4
C:\>getlevel C:\浙江省\宁波市\北仑区\大碶街道.txt
5
天塌了,这么简单的代码竟然出了 bug 。
原来 碶 的 编码是 {0xb4, 0x5c},其中 0x5c 和反斜杠的 ASCII 编码一模一样。
GBK 的第一字节兼容 ASCII ,但第二字节的范围是 0x40 ~ 0xfe,与 ASCII 的 0x00 ~ 0x7f 重叠。BUG 就这么诞生了。
UTF-8 没有这个问题的原因是:只要字节范围在 0x00 ~ 0x7f,那么就一定是 ASCII ,因为后续字节都避开了这个范围。虽然中文编码比 GB 系列长了,但是这个设计确实省了很多事。包括 strstr() strcmp() 之类的都不会出现奇奇怪怪的 bug 。
或许我应该使用 wmain() 然后获取 wchar_t,但是 wmain() 是 Windows 特有的东西,这样做就没法和 Linux 公用同一套代码了。目前加上了 mbtowc() 作为修复。原本简洁的代码变得十分复杂:(
说到这又不得不吐槽下 Windows 的各种奇怪 API 了,不知道它是如何存活到现在的...
代码是简化后的,这里不用关注路径处理的问题。
原始代码中,输入的路径是先判断存在,再经过 POSIX 的 realpath() / Windows 上的 GetFullPathName() 一系列流程处理过的,所以可以直接数。因为这个不是重点,还请大家放过。
2025 年了 Visual Studio 中文语言的默认编码还是 GB2312 。同事改完代码一推到仓库全是乱码
当年给公司写 mfc 程序,新手的我被 win 的各种字符串整的心理阴影。
用 #if 宏来实现不同平台的条件编译就可以了啊
我的处理方式是边界处全部转换成 UTF-8 ,这样内部的处理逻辑就可以保持一致了
参考 utf8everywhere.org/#windows
UTF-8 是自同步的,所以任何合法的 UTF-8 序列是另一个合法的 UTF-8 序列的子串时,必然是 Unicode 码位意义下的子串。
无论如何 Windows 和 Linux 都没法共用一套代码,因为 Linux 上反斜线可用于文件名,因此 /a\b 在 Windows 上层数是 2 ,在 Linux 上层数是 1 。
另外计算斜线和反斜线并不能正确得出层数,主流操作系统里 . 是本目录,.. 是上层目录(但对于根目录来说是本目录),这两个名称存在于所有目录里,需要特别处理。
另外楼主似乎以为 Linux 上文件名是 UTF-8 编码的,这是错的。Linux 文件名是不含 '/' 也不含 '\0' 的任何 uint8_t 串,操作系统并不关心 U 不 UTF 的。这一点和 Win32 无甚差别:Win32 规定文件名是任何不含一些选定不可用字符的 uint16_t 串,路径分割符是 '\' 和 '/'。
楼主的代码在 Linux 上可用(排除上面 . 和 .. 的考虑的话),仅仅是因为 C 标准的传递参数的方式和 Linux 原生路径表示是一样的。
就算写 shell 脚本现在也用 utf-8 ,gbk 保存的默认打开中文全是乱码
utf8 显示到命令提示符上错误了,还不能直接用 utf82gbk 转换,因为系统会把一些乱码替换为 ”方块问号“
跨平台必知必会的编码问题
#5 windows 的 unicode 前面可能有 bom ,不过系统默认设置是使用 local 编码,ide 也是
高版本的 windows 才有预览版本的 utf8 功能,但很多 bug , 而且市场上已经形成 windows 上使用 local 编码程序生态了,你切了 utf8 那些软件都显示乱码
windows 自带的控制台也是一个坑,哪怕你 chcp 65001 之后 printf 的 utf-8 编码里的中文能正常显示了,但 scanf 接收你输入的文字,还依然是 gbk 编码。
GBK 编码挺好的,中文汉字必定是 2 字节,第一个字节必定大于 128 (0~255),我都是单独把中文和英文先筛选出来,再做处理的。
for (level = 0, p = argv[1]; *p; p++)
这个处理方法是不对的,一个 char 代表「 UTF-8 编码序列中的一个字节」,不存在任何和文本相关的含义。尽管 UTF-8 有一些和 ASCII 兼容的假设,但存在很多 corner case (就像主帖提到的),所以不可靠。
如果是高级语言,要枚举字符应当先枚举 Unicode 码点( runes )。
用 mbtowc 转换其实也有问题。wc 指的是「空终止宽字符串」,它不等于 runes 。例如,Windows 上它代指的是经过 UTF-16LE [1] 编码的字符串,对高位字符也需要用多字节的 surrogate pairs 来占位。Linux 上可能是 UTF-32 ,但也不一定。总之,一般建议避免使用 wchar_t 。
言而总之,如果你想枚举 UTF-8 字符串中的字符,最合规的做法是要么依赖 ICU 、utf-8 这样的字符处理库,要么用 C11 里的 mbrtoc32 ( mb -> UTF-32 )。
[1] learn.microsoft.com/en-us/cpp/cpp/char-wchar-t-char16-t-char32-t
我这两天好像突然穿越回到了 2010 年前后,有讨论 GBK 的,有讨论 跨域的,又看到了上古神兽 JQUERY 、TOMCAT 。
#13 哈哈哈哈哈,同感
不应该用 mbtowc ,这么做会产生更多问题。
如果只需要支持 Windows 10 ,那可以在 manifest 中声明使用 UTF-8 编码,然后一律使用 UTF-8 。否则需要在调用系统 API 时手动把 UTF-8 转换成 UTF-16 ,然后调用 UTF-16 版本的 API 。
12 楼说的对 虽然我不怎么写 c 但你这个判断一看就不正确
learn.microsoft.com/zh-cn/windows/apps/design/globalizing/use-utf8-code-page
Windows ?难道不应该用 CreateFileW ?
#5
UTF-8 是自同步的,所以任何合法的 UTF-8 序列是另一个合法的 UTF-8 序列的子串时,必然是 Unicode 码位意义下的子串。
这个说法是以前不了解的。感谢
Windows 如果用 ucrt140 的话,就可以全套 utf8 了(别混用 win api )
早年还有比 GBK 更坑爹的 GB2312 ,某个大领导的“镕”字不在 GB2312 范围中
这代码给人看乐了,学艺不精也能算 bug ,就跟 tcp 粘包侠一样可乐
Windows 老老实实用 wide 版本的 API ,不要用 C style stringsc 处理和 OS API 相关的字符串,不要听什么 utf8everywhere 的鼓吹。
#9 任何设计之初就没有打算适配 Windows ME 或者更低版本的 Windows 的 Windows 软件不用 UTF-16 调用 Windows API 都是自始错误的设计,因为 Windows NT 系列的最初版本 (3.1) 就是使用 UCS-2 (后来改为 UTF-16 )作为原生字符串表示的。
#12 有必要提示其他读者:Unicode 码点的官方名字是 code point ,使用 rune 这种字母类型名字称呼 code point 似乎是 Go 引起的一种不必要的时尚潮流,而且这种时髦感也被 .NET 团队吸收了。
std::filesystem::path {argv[1]}
c/c++折腾跨平台就是很麻烦, 最近有一些代码需要跨平台编译出动态库, 纠结许久还是放弃 c/c++改用 go ,代价就是动态库大了一些,但代码真的很省心,很现代,
#24 是的,这里 [1] 也有人讨论这个问题。我用 rune 是因为我最常写的 Dart 里也吸收了这个名词。
[1] learn.microsoft.com/en-us/answers/questions/2085971/why-is-system-text-rune-named-like-this
没有人提到 C23 的 char8_t 吗,最近学 C23 看到个大量采用这个的库 github.com/micl2e2/mcpc ,震惊! C 里面也可以全程 UTF8 !
偏个题。不能用斜杠、反斜杠数量来判断文件目录层级。首先 Linux 里有.
,..
,其次同一个文件的相对路径和绝对路径的斜杠数量也可能不同。还有,Windows 里我不清楚,但只是 Linux 里,/path/to/file
和//path////to///file
指向的是同一个文件。
这不是 utf8 吧,只是单独提出了一个类型用来表示 unicode code unit ,语义上更明确了,但没有任何编码信息
#5
#29
抱歉,其实是因为完整的代码逻辑很长,这里是我随手举的例子,没有完全说明清楚。传入的路径是标准化后的绝对路径(如 realpath() 处理后的字符串),所以不考虑 ./ ../ // 等情况了。移植到 Windows 上是做了 #ifdef _WIN32 处理的, Linux 上不做反斜杠判断。
#6
Linux 上确实可以不是 UTF-8 ,正如中文 Windows 上也不一定是 GBK (可以手动改成实验状态的 UTF-8 ),但可以认为已经成为了事实上的标准。绝大多数用户使用默认配置就是这种情况了。
#12
在 UTF-8 上应该是可靠的(只要不是去数字符数的话)。这里的困境是:我也知道有问题,但是似乎没有办法简单解决。正如需求就是简单的数斜杠,那么真的需要引入一个 Unicode 库吗,其实我自己也是怀疑的(?)
另外 mbtowc(),wc 是 widechar 吧,不是 NULL 空终止。
#22
其实是说我的代码有 BUG 啦,这个代码确实学艺不精,其实我也想知道 应该 怎么写,或许你也可以举个例子 hhh 这是很多人都会犯的错误。但在 UTF-8 ,它是允许你这么遍历的。一个是方便我这种懒人,二是让那些欧美地区人写的这类代码也能正常跑在中文上。
比如说 strstr() 找子串,GBK 是用不得的。utf-8 在不引入第三方库下就能这么找,是不是挺省事?;)
其实楼主的代码加上相对路径的识别就好了,说白了缺少三种特殊处理,“.”、“..”、空白,经常和路径打交道的,这三种特殊情况,第一行 if 就开始处理了
发现目录层级这玩意儿还是有些门道的
..
其实不能被删掉,也就是说 a/c 和 a/b/../c 并不等价,因为 b 可能是一个符号链接,此时它的父目录就不是 a 。
Rust 和 Python 的实现都是正确的,只会删掉多余的 /
和 .
,并且在文档中强调了这一点
Go 和 NodeJS 都会把 ..
也删掉,但 NodeJS 提到了它的行为并不严格遵守 POSIX 规范
#31 一个中国生活的、使用 Windows 二次元爱好者,很可能分区是 NTFS 格式,同一个文件的文件名里既有中文,又有日语。此时无论用户的代码页是 936 (简体中文) 还是 932 (日语) 都无法通过非 Unicode API 访问此文件。
字符编码方式永远都是 trade off 的艺术,你不能光看一项优点,就忽略了它其他方面的缺点,GBK 作为定长编码,相比变长编码还是有独到的优势的。而且默认字符编码这个问题,尊重平台特性,尊重历史兼容性,才是正确的,就像 Apple 拼尽全力,也无法彻底去掉大小写不敏感一样
#34 其实 936 是包含了平片假名的,只要没有生僻字勉强还行(
所以我也很好奇其他 posix 程序是怎么移植过来的,毕竟大多数 API 都是 char *,到最后一步再转成 LPCWSTR 么,好像也有问题。
好在 Windows 10 1903 往后可以通过 manifest 指定 code page 为 UTF-8 (65001)了,以后 ANSI API 应该还有发展空间:
learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page
那个啥,我冒失了,原谅我,楼主是个 14 岁的初中生~~~~~~~~~~~~~ 是我不够 nice 。
这就有点尬黑了,tomcat 只是集成了,啊不,人家是进化了(
别啊,我现在工作了,虽然时间不长。或许是看到了我的历史帖子,那是我注册 v 站的十周年纪念,不是说今年(
#36 我印象里见过 -U8 结尾的 Win32 API ,用这个比设置代码页为 65001 之后用 -A 要好,当然,-U8 和 -A 在面对目前的文件系统时,都不如 -W 好。
-A 属于为了兼容性维持的 API ,内部操作都是转换为 UTF-16 之后调用 -W 的;我的理解是允许 manifest 设置 65001 是为了让 POSIX 程序最初的移植容易一点,而非作为主要存在的形式。
因为文件系统的路径并不需要是合法的 UTF-16 ,所以直接用 -W 和文件系统交互依然是惟一正确的选择。
如果用其他 char 本身就是 utf 字节的语言实现,op 会不会没有了这种感慨呢?
要知道 c 的出现比 utf 早太多了,gbk 也比 utf 早很多。
API 不一样那没办法,Windows API 要么就是本地 codepage (虽然现在能改成 UTF-8 ),要么就是 UTF-16 。
简单移植方法是编译时添加 /utf-8 选项,入口点用 wmain ,手动把 UTF-16 转成 UTF-8 ,后面就可以和 Linux 用一套代码了。想要输出正常可能还要 SetConsoleOutputCP(CP_UTF8)
本质上还是你代码本地化处理有问题,应该按字符遍历字符串,而不是字节。
#13 其实 GB 系列编码不算老吧,GBK 有年代了,但是 GB18030-2022 是新出的,而且属于强制国标,国产化适配必须的。像不少系统原来只支持 UTF-8 ,现在要支持使用 GB18030 ,你说到底算进步还是退步哈哈
#36 大多数 POSIX 程序其实不管字符串编码,基本是原样传递。
#40 据我所知 Windows 并没有提供 UTF-8 单独的 API 。而且从 Vista 开始新增的大部分 API 都是只支持 UTF-16 的了。
manifest 设置 UTF-8 就是为了方便移植,因为在此之前并没有一个官方的方案单独设置某个进程的代码页,要改只能改系统全局的,但改全局的又会让另一部分老 ANSI 应用炸掉。
#44
原样传递是一部分,POSIX 程序还是会处理的字符串的。比如 musl 的 PATH 变量,就是通过 strchrnul() 直接分割冒号的。这个函数只按字节处理。看了一下 glibc 也是一样的。
非 UTF-8 下就有出问题的风险。所以 UTF-8 的设计是很好的,GBK 和 GB18030 就差那么一点(其实我想说明的也就是这个意思)
git.musl-libc.org/cgit/musl/tree/src/process/execvp.c
但是很远古的 Linux 系统也有使用 GBK ,似乎没有炸掉的情况,可能是用户主动避免了
UTF-8 也不能避免文件名包含冒号,只能说编码有 0x3A 的目录名不能用于 PATH
#44
learn.microsoft.com/en-us/windows/win32/api/windns/ 后缀是 _UTF8
learn.microsoft.com/en-us/windows/win32/api/webservices/ 后缀是 Utf8
这是少部分特殊 API ,我之前没用过,不知道。其他绝大部分常用 API 都不支持 UTF-8 。
www.gov.cn/ 都是 utf-8 了,还有信创产品强制 GBK 的吗?
如果楼主写的是纯 c/c++程序,不依赖系统 api ,比如用的 fopen 或者 std::filesystem ,而不是 open/CreateFileW ,那么在 Windows 设置链接 ucrt.lib 就是 utf8 (默认是 libcmtd.lib )。无需任何代码修改,跨平台。
“大多数 POSIX 程序其实不管字符串编码,基本是原样传递。”
小米安卓系统,一般来说保存的文件名应该用的是 UTF8 ,不小心写成了 GBK ,结果就炸了,文件死活删不掉。V 站貌似还有帖子,一直解决不了。
数斜杠就好?只能说你还有的坑。
简单点,用这个 mingw-w64-ucrt-x86_64 啥都不改。mingw-w64 默认还是 codepage ,带 ucrt 的是 utf8 。编码转换在 Windows 系统 ucrtbase.dll 里自动转换。
“大多数 POSIX 程序其实不管字符串编码,基本是原样传递。”是对的。编码只是 shell 输入输出用,内核不在乎编码。LC_ALL=xxx sh 启动新 shell 就能删了。
#12 话说用小动物做 logo 是什么时候开始的?
#51 这么做并不行,链接 ucrt.lib 只是动态链接 ucrtbase.dll ,内部实现都是一样的,都是走 fopen->_open->CreateFileA ,仍然有编码问题
#52 Android 有 fuse 进行了一层过滤,可能会存在一些问题。
楼主你用 mingw-w64-ucrt-x86_64 彻底解决编码烦恼 utf8 一路畅行
ucrtbase.dll 内置了 utf8 支持,暴露的 api 的输入输出全是 utf8 ,翻译到系统就是 utf16 ,过程中没有任何 gbk 参与,emoj 兼容
并不是。自从 VS2015 开始,MSVC CRT 中一些通用 C 语言函数被移了出来,作为系统组件,随系统升级,称为 Universal CRT 。只要是 VS2015 及之后版本编译的程序默认都会使用 UCRT ,但是仍然不会使用 UTF-8 编码。
可以自己写个程序试试用 fopen 打开文件,我刚刚实测是无法打开的。
我跟踪了一下 fopen ,内部是有转换机制的,会根据__acrt_get_utf8_acp_compatibility_codepage()返回的代码页进行转换,而这个函数是根据 C locale 来返回的。
所以 UTF-8 转换功能确实存在,但是并不是默认启用的,必须修改 locale 才会启用。
跨平台永远都是最麻烦的话题
#13 还有几个讨论 Vim 和 Emacs 的哈哈哈
问题从来不在编码
而在于你写的代码
你使用了一个非跨平台的各自平台编译器 你想让程序跨平台能运行 那么默认就是你自己处理各个平台兼容性问题
而你处理不好就开始怒喷了
GB2312 制定的时候两个字节都是大于 0x80 的,微软搞 GBK 的时候为了塞下更多汉字把 0x40-0x80 也用了,GBK 随着 Windows 应用的太广了,变成事实上的标准,后面再制定 GB18030 的时候也只能选择兼容 GBK ,所以处理起来就比较麻烦了。
如果没有特殊需求,最好别用 GBK ,遇到不支持的字符处理不了。
#64
问题从来不在编码
我不赞成,编码可以分优劣。而在于你写的代码
在 GBK 的条件下,代码确实是有问题的。你使用了一个非跨平台的各自平台编译器 你想让程序跨平台能运行 那么默认就是你自己处理各个平台兼容性问题
而你处理不好就开始怒喷了
是的,我正在写一个跨平台的 C 库,正在处理这些问题。与其说“处理不好”,倒不如说“很难处理好”。
例如,很多人都说过,不要把 Windows 用户目录设置为中文,因为很多软件会报错。具体地说,我在上面举的 musl execvp() 函数,最终就是用 char * 遍历的。
当一个问题普遍存在时,我们就要思考问题的根源,比如比较 GBK 和 UTF-8 在设计上的优劣。
跨平台的东西也是人写出来的,方便了大家,但是写起来不舒服,请允许我吐槽一下。
#65
GB2312 制定的时候两个字节都是大于 0x80 的,微软搞 GBK 的时候为了塞下更多汉字把 0x40-0x80 也用了。
赞,这么看来 GB2312 倒是完全兼容 ASCII 的。我不认可 GBK 的原因主要就是第二字节侵占了 ASCII 码范围,产生了麻烦。最终 GB18030 还是拓宽到了四字节,当初不如直接加字节来的痛快。
#50 好像说明不了啥,因为 HTML 5 惟一合规的编码是 UTF-8 。
#50
是需要支持的,有认证,但可以有配置使用其他字符集。对于信创看来,UTF-8 (或者准确说 Unicode )显然不够自主,万一 IRG 卡你脖子不收录新汉字怎么办
GBK 兼容 GB2312 ,所以已经取代 GB2312 成为事实上的标准,GB 编码的程序和数据都是以 GBK 为标准的。
GB18030 只是为了扩展 GB 编码收录 Unicode 中的字符,再搞一种不兼容的编码没有太大意义。
我觉得可能当时的想法是准备切换到 Unicode ,没有考虑扩展 GB2312 ,结果微软出个 GBK 把路卡死了,再制定标准也只能捏着鼻子兼容 GBK 了。
你能保证 UTF-8 里没有 0x5c 吗?
应该按字来处理,而不是字节。一个字可能占多个字节,要按编码规则整体递进。
这个问题 sdl 有个解决方案,即用宏定义来替换 main 函数,自己再写一套 main 函数,在 Windows 平台上先进入重定向的 main 函数转换 argv 的编码
不过这种方法挺丑陋的,而且也会失去 main 自动加 return 0 的特性,好处是不用改动代码(参考 sdlmain )
不过话说回来,其实也没必要从 main 函数去收 argv ,直接调用系统 api 拿就好了,linux 好像是可以从某个路径读(忘了),这样可以脱离 main 函数从任意位置获取命令行参数了,编码也可以完全自己控制。
#70 UTF-8 保证 0x5c
就是 \
。正是我想说明的地方。
可以看图:
0 开头只有 0yyyzzzz 的形式,是单字节。多字节都是 1 开头的。虽然多字节浪费了一些空间,但是处理起来高效呀。
“你知道 string 有几种写法吗?”
有 0x5c 就是 ascii 范围内的了。其余字节都是大于等于 0x80 。
POSIX 下没有通用的取进程参数的方法,Linux 下可以读/proc/self/cmdline ,但其他系统就不一定有/proc 了。
另外 Linux 下可以通过修改 argv 指向的 buffer 来实现修改进程名,但不能通过/proc 修改。
.editorconfig 文件解决
# EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs
# Editor configuration, see editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
当年就是准备从 gb2312 进化到 unicode 的,然而微软大力推销它的 gbk ( gb2312 是国标,gbk 是行标)导致路线错误,zf 很生气于是是在 office2001 ( 2003 ?) 快要发布之前强制规定所有软件必须兼容 gb18030 狠狠地恶心了一把微软(返工增加对 gb18030 的兼容)。
我有 GB18030
Windows 系统的兼容性带来的坏处
数文件层级居然能是简单的数斜杠数?那你这 linux 版本来质量也就不咋地。
至少命令行中,cd ..、cd ../../这类还是少吗?
#80
是简化后的代码,因为这个不是重点。整块太长了,再加上异常处理流程等等,我估计大伙都不愿意看。
入参是被 realpath()标准化后的路径,不存在这种了。
C 和 C++中,字符串从来就没一个统一标准啊。既然你都标准化路径了,按理说,在 C 家族这类把字符串看作字节数组的语言里,更应该能想到,要把各种字符串转换成同一种编码再处理啊。而且现在的文件命名,很可能碰到✨➔✅❌这类 emoji 字符,你怎么敢不转换成 UTF8 这种可以包含所有字符集的编码就处理的
vs 基本概念不都是要 wchar 的么
vs2022 17.3 已经可以设置指定编码保存文件了,在工具-选项-Environment-文档-使用特定编码保存文件
程序内部应当只使用 utf8 。和操作系统交互的边界需要特殊处理。
win 下的边界是把 uint8_t 的 utf8 串和 uint16_t(wchar_t)的 utf16 互转。
不仅文件名需要转换,打开文件的操作也需要包装一下 OpenFileW 。
linux 下直接用 uint8_t 的 utf8 。另外 linux 下的 wchar_t 是 uint32_t 。
编码转换就是屎中屎,之前看到某大厂出品的跨国代码时,codepage 相关的核心代码大概就有几千行还不包括重复的玩意。
有一些比较老的硬件, 只能接受 GB 18030. 并且这一字符集是现行国家标准: openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=A1931A578FE14957104988029B0833D3
#82 那肯定是不敢,不然我也不会发帖了。所以回到标题:大家快点统一 UTF-8 ,少一层转换,大家都省事。
对于 Windows ,应该把 ANSI API 的 UTF-8 支持好。事实上 POSIX 的大多 API 就是微软所谓的 ANSI API 。
Unicode API 的想法是好,但是最后 UTF-16 还是变成了变长编码(代理对),实在是又吃了亏。(路线走错了)
C 发展到现在,程序员还不区分字节流和字符串这两种东西吗?如果是做应用,那不应该出现字符串里数目录分隔符这种事情的,老老实实用库就好。
说个曾经遇到的。
apache httpclient 库发起 GET 请求,query 如果使用 GBK 编码,部分生僻字使用同为 apache 家的 tomcat 可以正常接收,使用 spring-cloud-gateway 接收就会变成乱码。
实际原因也是一样——GBK 的一些生僻字复用了 ASCII ,不同的解码逻辑会导致不同的结果。
后来把 tomcat 的解码逻辑复制到 gateway 中了。至于为什么要用 GBK ,是历史遗留问题。
#89
不是应用,我在做一个基础的跨平台库,尽力兼顾简洁、性能、准确性。调库直接转换应该是省事,但是我不想搞得太臃肿(比如在 OpenWRT 路由器下面也能运行?)
其实支持中文只是我的一个想法,当初程序只支持 ASCII 。后来我发现引入 UTF-8 代价很小,大多数代码都没问题。在后来我引入了 Windows 支持,就遇到了 GBK 。它对原先 ASCII 的代码兼容不好。然后我就吐槽了。
如果你是库开发者,那数分隔符无可厚非,但是要分开字符串和字节流,UTF-8 和 GBK 都是字节流,如果你不打算像其他现代语言那样都处理成“真”字符串,希望直接处理 UTF-8 字节流,可以把其他编码的字节流转换到 UTF-8 上,而不是在逻辑层面还考虑多种编码的问题。
set 一下就行
只用纯 c/c++标准库,用 mingw-w64-ucrt 编译环境就能一字不改在 Windows 全程 utf8 。
新环境能统一,老环境算了。utf8 出现的太晚了。比如马屁股决定了火箭直径这事,换标准意味着基础设施全换。Windows 不可能舍弃兼容性的。不像 mac ,说不兼容就不兼容。
c++17 的 filesystem 呢
是不是 屏蔽平台差异?
我也碰见过,情况是 windows 跟 wsl 后端的 docker 之间,我直接在 windows 系统上运行命令去备份 docker 里的数据库。就因为两边的编码不一致错误导致丢失字符,每个中文词结尾的文字,三位编码丢失了最后一位变成??,以及中文里的圆括号等丢失,最后自己磨了快一个月,靠着三个字符的前两个还在,整体词语也在,把这个上万行的数据库备份文件里的丢失靠猜的补齐了。
UTF-8 BOM → UTF-16 BOM → UTF-8 → ANSI
UTF-8 有些字符会出现奇奇怪怪的问题。
今天虎王行动第一天,网安通报了一批涉嫌挖矿的 ip ,都是通过探针发现有访问 bitbucket.org/的行为 然后我解释这应该是我路由器在检查更新,但他们还是让我把路由关…
2.5 用虚拟信用卡野卡订阅了一年的会员,今天发现会员被取消了,关键是也没有退款,发了邮件询问也没有回复(猜测是因为美国人在睡觉?) 此外,我是收到了一封退款邮件,内容是退掉 …
在《这多年来我一直在钻研的技术》这篇文章中,我讲述了一下,我这么多年来一直在关注的技术领域,其中我多次提到了工业级的软件,我还以为有很多人会问我怎么定义工业级?以及一个高可用性…