最近在把我用 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()
一系列流程处理过的,所以可以直接数。因为这个不是重点,还请大家放过。
1
Thymolblue 18 天前
2025 年了 Visual Studio 中文语言的默认编码还是 GB2312 。同事改完代码一推到仓库全是乱码
|
2
dearmymy 18 天前
当年给公司写 mfc 程序,新手的我被 win 的各种字符串整的心理阴影。
|
![]() |
3
yolee599 18 天前 via Android
用 #if 宏来实现不同平台的条件编译就可以了啊
|
4
henix 18 天前
我的处理方式是边界处全部转换成 UTF-8 ,这样内部的处理逻辑就可以保持一致了
参考 https://utf8everywhere.org/#windows |
![]() |
5
geelaw 18 天前 via iPhone ![]() UTF-8 是自同步的,所以任何合法的 UTF-8 序列是另一个合法的 UTF-8 序列的子串时,必然是 Unicode 码位意义下的子串。
无论如何 Windows 和 Linux 都没法共用一套代码,因为 Linux 上反斜线可用于文件名,因此 /a\b 在 Windows 上层数是 2 ,在 Linux 上层数是 1 。 另外计算斜线和反斜线并不能正确得出层数,主流操作系统里 . 是本目录,.. 是上层目录(但对于根目录来说是本目录),这两个名称存在于所有目录里,需要特别处理。 |
![]() |
6
geelaw 18 天前 ![]() 另外楼主似乎以为 Linux 上文件名是 UTF-8 编码的,这是错的。Linux 文件名是不含 '/' 也不含 '\0' 的任何 uint8_t 串,操作系统并不关心 U 不 UTF 的。这一点和 Win32 无甚差别:Win32 规定文件名是任何不含一些选定不可用字符的 uint16_t 串,路径分割符是 '\\' 和 '/'。
楼主的代码在 Linux 上可用(排除上面 . 和 .. 的考虑的话),仅仅是因为 C 标准的传递参数的方式和 Linux 原生路径表示是一样的。 |
![]() |
7
wtks1 18 天前
就算写 shell 脚本现在也用 utf-8 ,gbk 保存的默认打开中文全是乱码
|
8
hwdq0012 18 天前
utf8 显示到命令提示符上错误了,还不能直接用 utf82gbk 转换,因为系统会把一些乱码替换为 ”方块问号“
跨平台必知必会的编码问题 |
9
hwdq0012 18 天前
@geelaw #5 windows 的 unicode 前面可能有 bom ,不过系统默认设置是使用 local 编码,ide 也是
高版本的 windows 才有预览版本的 utf8 功能,但很多 bug , 而且市场上已经形成 windows 上使用 local 编码程序生态了,你切了 utf8 那些软件都显示乱码 |
![]() |
10
DOLLOR 18 天前 ![]() windows 自带的控制台也是一个坑,哪怕你 chcp 65001 之后 printf 的 utf-8 编码里的中文能正常显示了,但 scanf 接收你输入的文字,还依然是 gbk 编码。
|
![]() |
11
tool2dx 18 天前 ![]() GBK 编码挺好的,中文汉字必定是 2 字节,第一个字节必定大于 128 (0~255),我都是单独把中文和英文先筛选出来,再做处理的。
|
12
w568w 18 天前 ![]() > 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] https://learn.microsoft.com/en-us/cpp/cpp/char-wchar-t-char16-t-char32-t |
13
bbao 18 天前 ![]() 我这两天好像突然穿越回到了 2010 年前后,有讨论 GBK 的,有讨论 跨域的,又看到了上古神兽 JQUERY 、TOMCAT 。
|
![]() |
15
ysc3839 18 天前 via Android
不应该用 mbtowc ,这么做会产生更多问题。
如果只需要支持 Windows 10 ,那可以在 manifest 中声明使用 UTF-8 编码,然后一律使用 UTF-8 。否则需要在调用系统 API 时手动把 UTF-8 转换成 UTF-16 ,然后调用 UTF-16 版本的 API 。 |
![]() |
16
rekulas 18 天前
12 楼说的对 虽然我不怎么写 c 但你这个判断一看就不正确
|
![]() |
17
ShadowPower 18 天前
|
![]() |
18
fairytale 18 天前 via Android
Windows ?难道不应该用 CreateFileW ?
|
19
atuocn 18 天前
#5
``` UTF-8 是自同步的,所以任何合法的 UTF-8 序列是另一个合法的 UTF-8 序列的子串时,必然是 Unicode 码位意义下的子串。 ``` 这个说法是以前不了解的。感谢 |
![]() |
20
fairytale 18 天前 via Android
Windows 如果用 ucrt140 的话,就可以全套 utf8 了(别混用 win api )
|
![]() |
21
sagaxu 18 天前
早年还有比 GBK 更坑爹的 GB2312 ,某个大领导的“镕”字不在 GB2312 范围中
|
![]() |
22
minami 18 天前
|
![]() |
23
adoal 18 天前
Windows 老老实实用 wide 版本的 API ,不要用 C style stringsc 处理和 OS API 相关的字符串,不要听什么 utf8everywhere 的鼓吹。
|
![]() |
24
geelaw 18 天前
|
25
kirory 18 天前
std::filesystem::path {argv[1]}
|
![]() |
26
AoEiuV020JP 18 天前
c/c++折腾跨平台就是很麻烦, 最近有一些代码需要跨平台编译出动态库, 纠结许久还是放弃 c/c++改用 go ,代价就是动态库大了一些,但代码真的很省心,很现代,
|
27
w568w 18 天前
@geelaw #24 是的,这里 [1] 也有人讨论这个问题。我用 rune 是因为我最常写的 Dart 里也吸收了这个名词。
[1] https://learn.microsoft.com/en-us/answers/questions/2085971/why-is-system-text-rune-named-like-this |
![]() |
28
1BF6oSYCD9ngBHo1 18 天前
没有人提到 C23 的 char8_t 吗,最近学 C23 看到个大量采用这个的库 https://github.com/micl2e2/mcpc ,震惊! C 里面也可以全程 UTF8 !
|
29
yk000123 18 天前
偏个题。不能用斜杠、反斜杠数量来判断文件目录层级。首先 Linux 里有`.`,`..`,其次同一个文件的相对路径和绝对路径的斜杠数量也可能不同。还有,Windows 里我不清楚,但只是 Linux 里,`/path/to/file`和`//path////to///file`指向的是同一个文件。
|
![]() |
31
mikewang OP @geelaw #5
@yk000123 #29 抱歉,其实是因为完整的代码逻辑很长,这里是我随手举的例子,没有完全说明清楚。传入的路径是标准化后的绝对路径(如 realpath() 处理后的字符串),所以不考虑 ./ ../ // 等情况了。移植到 Windows 上是做了 #ifdef _WIN32 处理的, Linux 上不做反斜杠判断。 @geelaw #6 Linux 上确实可以不是 UTF-8 ,正如中文 Windows 上也不一定是 GBK (可以手动改成实验状态的 UTF-8 ),但可以认为已经成为了事实上的标准。绝大多数用户使用默认配置就是这种情况了。 @w568w #12 在 UTF-8 上应该是可靠的(只要不是去数字符数的话)。这里的困境是:我也知道有问题,但是似乎没有办法简单解决。正如需求就是简单的数斜杠,那么真的需要引入一个 Unicode 库吗,其实我自己也是怀疑的(?) 另外 mbtowc(),wc 是 widechar 吧,不是 NULL 空终止。 @minami #22 其实是说我的代码有 BUG 啦,这个代码确实学艺不精,其实我也想知道 *应该* 怎么写,或许你也可以举个例子 hhh 这是很多人都会犯的错误。但在 UTF-8 ,它是允许你这么遍历的。一个是方便我这种懒人,二是让那些欧美地区人写的这类代码也能正常跑在中文上。 比如说 strstr() 找子串,GBK 是用不得的。utf-8 在不引入第三方库下就能这么找,是不是挺省事?;) |
![]() |
32
lisxour 18 天前
@yk000123 其实楼主的代码加上相对路径的识别就好了,说白了缺少三种特殊处理,“.”、“..”、空白,经常和路径打交道的,这三种特殊情况,第一行 if 就开始处理了
|
33
aloxaf 18 天前 ![]() 发现目录层级这玩意儿还是有些门道的
`..` 其实不能被删掉,也就是说 a/c 和 a/b/../c 并不等价,因为 b 可能是一个符号链接,此时它的父目录就不是 a 。 Rust 和 Python 的实现都是正确的,只会删掉多余的 `/` 和 `.`,并且在文档中强调了这一点 Go 和 NodeJS 都会把 `..` 也删掉,但 NodeJS 提到了它的行为并不严格遵守 POSIX 规范 |
![]() |
34
geelaw 18 天前
@mikewang #31 一个中国生活的、使用 Windows 二次元爱好者,很可能分区是 NTFS 格式,同一个文件的文件名里既有中文,又有日语。此时无论用户的代码页是 936 (简体中文) 还是 932 (日语) 都无法通过非 Unicode API 访问此文件。
|
![]() |
35
minami 18 天前 ![]() @mikewang 字符编码方式永远都是 trade off 的艺术,你不能光看一项优点,就忽略了它其他方面的缺点,GBK 作为定长编码,相比变长编码还是有独到的优势的。而且默认字符编码这个问题,尊重平台特性,尊重历史兼容性,才是正确的,就像 Apple 拼尽全力,也无法彻底去掉大小写不敏感一样
![]() |
![]() |
36
mikewang OP @geelaw #34 其实 936 是包含了平片假名的,只要没有生僻字勉强还行(
所以我也很好奇其他 posix 程序是怎么移植过来的,毕竟大多数 API 都是 char *,到最后一步再转成 LPCWSTR 么,好像也有问题。 好在 Windows 10 1903 往后可以通过 manifest 指定 code page 为 UTF-8 (65001)了,以后 ANSI API 应该还有发展空间: https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page |
37
bbao 18 天前
那个啥,我冒失了,原谅我,楼主是个 14 岁的初中生~~~~~~~~~~~~~ 是我不够 nice 。
|
![]() |
40
geelaw 18 天前 ![]() @mikewang #36 我印象里见过 -U8 结尾的 Win32 API ,用这个比设置代码页为 65001 之后用 -A 要好,当然,-U8 和 -A 在面对目前的文件系统时,都不如 -W 好。
-A 属于为了兼容性维持的 API ,内部操作都是转换为 UTF-16 之后调用 -W 的;我的理解是允许 manifest 设置 65001 是为了让 POSIX 程序最初的移植容易一点,而非作为主要存在的形式。 因为文件系统的路径并不需要是合法的 UTF-16 ,所以直接用 -W 和文件系统交互依然是惟一正确的选择。 |
![]() |
41
buf1024 18 天前 ![]() 如果用其他 char 本身就是 utf 字节的语言实现,op 会不会没有了这种感慨呢?
要知道 c 的出现比 utf 早太多了,gbk 也比 utf 早很多。 |
![]() |
42
Shatyuka 18 天前
API 不一样那没办法,Windows API 要么就是本地 codepage (虽然现在能改成 UTF-8 ),要么就是 UTF-16 。
简单移植方法是编译时添加 /utf-8 选项,入口点用 wmain ,手动把 UTF-16 转成 UTF-8 ,后面就可以和 Linux 用一套代码了。想要输出正常可能还要 SetConsoleOutputCP(CP_UTF8) 本质上还是你代码本地化处理有问题,应该按字符遍历字符串,而不是字节。 |
![]() |
43
mikewang OP @bbao #13 其实 GB 系列编码不算老吧,GBK 有年代了,但是 GB18030-2022 是新出的,而且属于强制国标,国产化适配必须的。像不少系统原来只支持 UTF-8 ,现在要支持使用 GB18030 ,你说到底算进步还是退步哈哈
|
![]() |
44
ysc3839 18 天前 via Android
|
![]() |
45
mikewang OP @ysc3839 #44
原样传递是一部分,POSIX 程序还是会处理的字符串的。比如 musl 的 PATH 变量,就是通过 strchrnul() 直接分割冒号的。这个函数只按字节处理。看了一下 glibc 也是一样的。 非 UTF-8 下就有出问题的风险。所以 UTF-8 的设计是很好的,GBK 和 GB18030 就差那么一点(其实我想说明的也就是这个意思) https://git.musl-libc.org/cgit/musl/tree/src/process/execvp.c |
![]() |
48
geelaw 18 天前
|
![]() |
50
datou 18 天前
@mikewang https://www.gov.cn/ 都是 utf-8 了,还有信创产品强制 GBK 的吗?
|
![]() |
51
fairytale 18 天前 via Android
如果楼主写的是纯 c/c++程序,不依赖系统 api ,比如用的 fopen 或者 std::filesystem ,而不是 open/CreateFileW ,那么在 Windows 设置链接 ucrt.lib 就是 utf8 (默认是 libcmtd.lib )。无需任何代码修改,跨平台。
|
![]() |
52
tool2dx 18 天前
@ysc3839 “大多数 POSIX 程序其实不管字符串编码,基本是原样传递。”
小米安卓系统,一般来说保存的文件名应该用的是 UTF8 ,不小心写成了 GBK ,结果就炸了,文件死活删不掉。V 站貌似还有帖子,一直解决不了。 |
53
kneo 18 天前 via Android
数斜杠就好?只能说你还有的坑。
|
![]() |
54
fairytale 18 天前 via Android
简单点,用这个 mingw-w64-ucrt-x86_64 啥都不改。mingw-w64 默认还是 codepage ,带 ucrt 的是 utf8 。编码转换在 Windows 系统 ucrtbase.dll 里自动转换。
|
![]() |
55
fairytale 18 天前 via Android
@tool2dx “大多数 POSIX 程序其实不管字符串编码,基本是原样传递。”是对的。编码只是 shell 输入输出用,内核不在乎编码。LC_ALL=xxx sh 启动新 shell 就能删了。
|
![]() |
57
ysc3839 18 天前 via Android
|
![]() |
59
fairytale 18 天前 via Android
@ysc3839 ucrtbase.dll 内置了 utf8 支持,暴露的 api 的输入输出全是 utf8 ,翻译到系统就是 utf16 ,过程中没有任何 gbk 参与,emoj 兼容
|
![]() |
60
ysc3839 18 天前 via Android
@fairytale 并不是。自从 VS2015 开始,MSVC CRT 中一些通用 C 语言函数被移了出来,作为系统组件,随系统升级,称为 Universal CRT 。只要是 VS2015 及之后版本编译的程序默认都会使用 UCRT ,但是仍然不会使用 UTF-8 编码。
可以自己写个程序试试用 fopen 打开文件,我刚刚实测是无法打开的。 |
![]() |
61
ysc3839 18 天前 via Android
@fairytale 我跟踪了一下 fopen ,内部是有转换机制的,会根据__acrt_get_utf8_acp_compatibility_codepage()返回的代码页进行转换,而这个函数是根据 C locale 来返回的。
所以 UTF-8 转换功能确实存在,但是并不是默认启用的,必须修改 locale 才会启用。 |
62
Alias4ck 18 天前
跨平台永远都是最麻烦的话题
|
63
newtype0092 18 天前
@bbao #13 还有几个讨论 Vim 和 Emacs 的哈哈哈
|
![]() |
64
realpg 18 天前
问题从来不在编码
而在于你写的代码 你使用了一个非跨平台的各自平台编译器 你想让程序跨平台能运行 那么默认就是你自己处理各个平台兼容性问题 而你处理不好就开始怒喷了 |
65
si 18 天前 ![]() GB2312 制定的时候两个字节都是大于 0x80 的,微软搞 GBK 的时候为了塞下更多汉字把 0x40-0x80 也用了,GBK 随着 Windows 应用的太广了,变成事实上的标准,后面再制定 GB18030 的时候也只能选择兼容 GBK ,所以处理起来就比较麻烦了。
如果没有特殊需求,最好别用 GBK ,遇到不支持的字符处理不了。 |
![]() |
66
mikewang OP @realpg #64
> 问题从来不在编码 我不赞成,编码可以分优劣。 > 而在于你写的代码 在 GBK 的条件下,代码确实是有问题的。 > 你使用了一个非跨平台的各自平台编译器 你想让程序跨平台能运行 那么默认就是你自己处理各个平台兼容性问题 > 而你处理不好就开始怒喷了 是的,我正在写一个跨平台的 C 库,正在处理这些问题。与其说“处理不好”,倒不如说“很难处理好”。 例如,很多人都说过,不要把 Windows 用户目录设置为中文,因为很多软件会报错。具体地说,我在上面举的 musl execvp() 函数,最终就是用 char * 遍历的。 当一个问题普遍存在时,我们就要思考问题的根源,比如比较 GBK 和 UTF-8 在设计上的优劣。 跨平台的东西也是人写出来的,方便了大家,但是写起来不舒服,请允许我吐槽一下。 @si #65 > GB2312 制定的时候两个字节都是大于 0x80 的,微软搞 GBK 的时候为了塞下更多汉字把 0x40-0x80 也用了。 赞,这么看来 GB2312 倒是完全兼容 ASCII 的。我不认可 GBK 的原因主要就是第二字节侵占了 ASCII 码范围,产生了麻烦。最终 GB18030 还是拓宽到了四字节,当初不如直接加字节来的痛快。 |
![]() |
68
mikewang OP |
69
si 18 天前
@mikewang
GBK 兼容 GB2312 ,所以已经取代 GB2312 成为事实上的标准,GB 编码的程序和数据都是以 GBK 为标准的。 GB18030 只是为了扩展 GB 编码收录 Unicode 中的字符,再搞一种不兼容的编码没有太大意义。 我觉得可能当时的想法是准备切换到 Unicode ,没有考虑扩展 GB2312 ,结果微软出个 GBK 把路卡死了,再制定标准也只能捏着鼻子兼容 GBK 了。 |
70
shimanooo 18 天前
你能保证 UTF-8 里没有 0x5c 吗?
应该按字来处理,而不是字节。一个字可能占多个字节,要按编码规则整体递进。 |
71
FeS 18 天前 via Android
这个问题 sdl 有个解决方案,即用宏定义来替换 main 函数,自己再写一套 main 函数,在 Windows 平台上先进入重定向的 main 函数转换 argv 的编码
不过这种方法挺丑陋的,而且也会失去 main 自动加 return 0 的特性,好处是不用改动代码(参考 sdlmain ) 不过话说回来,其实也没必要从 main 函数去收 argv ,直接调用系统 api 拿就好了,linux 好像是可以从某个路径读(忘了),这样可以脱离 main 函数从任意位置获取命令行参数了,编码也可以完全自己控制。 |
![]() |
72
mikewang OP @shimanooo #70 UTF-8 保证 `0x5c` 就是 `\`。正是我想说明的地方。
可以看图: ![]() 0 开头只有 0yyyzzzz 的形式,是单字节。多字节都是 1 开头的。虽然多字节浪费了一些空间,但是处理起来高效呀。 |
![]() |
73
NPC666 18 天前 via Android
“你知道 string 有几种写法吗?”
|
![]() |
75
ysc3839 17 天前 via Android
@FeS POSIX 下没有通用的取进程参数的方法,Linux 下可以读/proc/self/cmdline ,但其他系统就不一定有/proc 了。
另外 Linux 下可以通过修改 argv 指向的 buffer 来实现修改进程名,但不能通过/proc 修改。 |
76
mark2025 17 天前
@Thymolblue
.editorconfig 文件解决 # EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs # Editor configuration, see http://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 |
77
mark2025 17 天前
@si 当年就是准备从 gb2312 进化到 unicode 的,然而微软大力推销它的 gbk ( gb2312 是国标,gbk 是行标)导致路线错误,zf 很生气于是是在 office2001 ( 2003 ?) 快要发布之前强制规定所有软件必须兼容 gb18030 狠狠地恶心了一把微软(返工增加对 gb18030 的兼容)。
|
79
tairan2006 17 天前
Windows 系统的兼容性带来的坏处
|
80
HTravel 17 天前
数文件层级居然能是简单的数斜杠数?那你这 linux 版本来质量也就不咋地。
至少命令行中,cd ..、cd ../../这类还是少吗? |
![]() |
81
mikewang OP |
82
HTravel 17 天前
@mikewang C 和 C++中,字符串从来就没一个统一标准啊。既然你都标准化路径了,按理说,在 C 家族这类把字符串看作字节数组的语言里,更应该能想到,要把各种字符串转换成同一种编码再处理啊。而且现在的文件命名,很可能碰到✨➔✅❌这类 emoji 字符,你怎么敢不转换成 UTF8 这种可以包含所有字符集的编码就处理的
|
83
realJamespond 17 天前
vs 基本概念不都是要 wchar 的么
|
84
Bazingal 17 天前
@Thymolblue @mark2025 vs2022 17.3 已经可以设置指定编码保存文件了,在工具-选项-Environment-文档-使用特定编码保存文件
|
85
jackmod 17 天前
程序内部应当只使用 utf8 。和操作系统交互的边界需要特殊处理。
win 下的边界是把 uint8_t 的 utf8 串和 uint16_t(wchar_t)的 utf16 互转。 不仅文件名需要转换,打开文件的操作也需要包装一下 OpenFileW 。 linux 下直接用 uint8_t 的 utf8 。另外 linux 下的 wchar_t 是 uint32_t 。 |
86
YetToCome 17 天前
编码转换就是屎中屎,之前看到某大厂出品的跨国代码时,codepage 相关的核心代码大概就有几千行还不包括重复的玩意。
|
![]() |
87
iugo 17 天前
有一些比较老的硬件, 只能接受 GB 18030. 并且这一字符集是现行国家标准: https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=A1931A578FE14957104988029B0833D3
|
![]() |
88
mikewang OP @HTravel #82 那肯定是不敢,不然我也不会发帖了。所以回到标题:大家快点统一 UTF-8 ,少一层转换,大家都省事。
对于 Windows ,应该把 ANSI API 的 UTF-8 支持好。事实上 POSIX 的大多 API 就是微软所谓的 ANSI API 。 Unicode API 的想法是好,但是最后 UTF-16 还是变成了变长编码(代理对),实在是又吃了亏。(路线走错了) |
89
sapphire 17 天前
C 发展到现在,程序员还不区分字节流和字符串这两种东西吗?如果是做应用,那不应该出现字符串里数目录分隔符这种事情的,老老实实用库就好。
|
![]() |
90
chocotan 17 天前
说个曾经遇到的。
apache httpclient 库发起 GET 请求,query 如果使用 GBK 编码,部分生僻字使用同为 apache 家的 tomcat 可以正常接收,使用 spring-cloud-gateway 接收就会变成乱码。 实际原因也是一样——GBK 的一些生僻字复用了 ASCII ,不同的解码逻辑会导致不同的结果。 后来把 tomcat 的解码逻辑复制到 gateway 中了。至于为什么要用 GBK ,是历史遗留问题。 |
![]() |
91
mikewang OP @sapphire #89
不是应用,我在做一个基础的跨平台库,尽力兼顾简洁、性能、准确性。调库直接转换应该是省事,但是我不想搞得太臃肿(比如在 OpenWRT 路由器下面也能运行?) 其实支持中文只是我的一个想法,当初程序只支持 ASCII 。后来我发现引入 UTF-8 代价很小,大多数代码都没问题。在后来我引入了 Windows 支持,就遇到了 GBK 。它对原先 ASCII 的代码兼容不好。然后我就吐槽了。 |
92
sapphire 17 天前 ![]() @mikewang 如果你是库开发者,那数分隔符无可厚非,但是要分开字符串和字节流,UTF-8 和 GBK 都是字节流,如果你不打算像其他现代语言那样都处理成“真”字符串,希望直接处理 UTF-8 字节流,可以把其他编码的字节流转换到 UTF-8 上,而不是在逻辑层面还考虑多种编码的问题。
|
![]() |
95
fairytale 17 天前 via Android
@mikewang 新环境能统一,老环境算了。utf8 出现的太晚了。比如马屁股决定了火箭直径这事,换标准意味着基础设施全换。Windows 不可能舍弃兼容性的。不像 mac ,说不兼容就不兼容。
|
96
xinyu391 17 天前
c++17 的 filesystem 呢
是不是 屏蔽平台差异? |
97
hsj1992 17 天前
我也碰见过,情况是 windows 跟 wsl 后端的 docker 之间,我直接在 windows 系统上运行命令去备份 docker 里的数据库。就因为两边的编码不一致错误导致丢失字符,每个中文词结尾的文字,三位编码丢失了最后一位变成??,以及中文里的圆括号等丢失,最后自己磨了快一个月,靠着三个字符的前两个还在,整体词语也在,把这个上万行的数据库备份文件里的丢失靠猜的补齐了。
|
98
v2tudnew 17 天前
UTF-8 BOM → UTF-16 BOM → UTF-8 → ANSI
UTF-8 有些字符会出现奇奇怪怪的问题。 |