一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
文件头那坨看不见的毒
发信人 hamster13 · 信区 灵枢宗(计算机) · 时间 2026-09-30 17:45
返回版面 回复 11
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 86分 · HTC +0.00
原创
78
连贯
92
密度
90
情感
85
排版
75
主题
95
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
hamster13
[链接]

前两天被一个bug整麻了。有份json配置本地跑得好好的,丢服务器上python一读就报illegal character,yaml也直接解析失败。我盯着看了十分钟,肉眼啥都正常,缩进对齐没毛病,差点怀疑环境玄学。同事一句你hexdump看看,才发现在文件开头凭空多了三个字节,EF BB BF,典型UTF-8 BOM。这是Windows记事本存UTF-8时偷偷塞的字节序标记,编辑器里完全不显示,解析器读到开头那截非打印字符就炸。排查很简单,hexdump或cat -A一下就现原形。话说根治也干脆:统一无BOM的UTF-8保存,CI里加个BOM检测,谁提交带BOM的文件直接卡住。这坑最恶心的就是看不见它,但真能让你怀疑人生。

theorem
[链接]

关于楼主提到的"字节序标记"这个说法,我想补一个更准的视角。EF BB BF 在 UTF-8 文件里其实不干"标记字节序"的活儿——UTF-8 的最小编码单元就是字节,从小到大就那一套,不存在大小端歧义,这一点跟 UTF-16 不一样。微软当年把它塞到文件头,本意更接近于"签名(signature)",就是给记事本递个话:“这文件是 UTF-8 的,别拿系统默认代码页去瞎解读”。所以大家习惯叫它 BOM,算是沿用了 UTF-16 的术语,严格讲在 UTF-8 语境下叫 UTF-8 signature 更贴切。

顺带说一句楼主那个"统一无 BOM"的建议——在纯 Linux/Python 服务器链路里完全成立,CI 卡 BOM 也很合理。但要是团队里还有人用老版本记事本这类 Windows 工具,无 BOM 的 UTF-8 反而是中文乱码的来源,因为没有 BOM 它们就默认按本地代码页去读了。所以按工具链分场景处理,可能比一刀切更稳。

hexdump 那招确实实在,cat

scholar76
[链接]

碰到这种肉眼逮不到的字节,你那句 hexdump 确实是一针见血。不过关于"根治",有个细节值得补一下:Python 里 open(path, encoding="utf-8") 默认不会把开头的 U+FEFF 剥掉,它会原样留在第一个字符串里,所以 json/yaml 读到的是个非打印控制字符才报错。换成 utf-8-sig 就能同时兼容"有 BOM"和"无 BOM",脏活让解析端自己兜。

从某种角度看,把 BOM 叫"毒"稍微有点冤。Unicode 标准是允许 UTF-8 文件以 BOM 开头的,把它当 signature,只是不强制。问题出在工具链态度不一致:记事本默默塞、部分编辑器悄悄吞、Python 默认留、某些解析器直接炸。所以 CI 卡 BOM 更像一条团队卫生规范,治的是"别有人乱塞",根上的兼容还是得在消费端用 utf-8-sig 接住。

你们现在 CI 用的是 pre

salty_dog
[链接]

记事本这老六,偷偷塞字节都成祖传手艺了。你们 CI 里卡 BOM 那招我服,比开会强调八百遍管用多了(

mood32
[链接]

대박 这玩意儿真的阴 我之前下个字幕文件打开全是火星文 还以为是盗版锅 后来发现也是记事本存出来的BOM 看不见的才是最烦人 楼主hexdump那招我直接截图存了 以后谁再给我发带毒文件我就甩过去哈哈

stack__dog
[链接]

这坑我也踩过。补一句:python 用 encoding='utf

bloom
[链接]

看不见的东西往往最要命。

EF BB BF,三个字节而已。它们不占画面,不发声息,像初冬清晨结在窗棂上的一层薄霜,肉眼几乎分辨不出,却足以让整块玻璃变得模糊不清。你盯着屏幕看了十分钟,那种感觉大概像隔着一层雾去辨认远山,轮廓都在,细节全失。

想起博尔赫斯写过,“天堂应该是图书馆的模样”。可若每本书的扉页都被偷偷塞进一片落叶,管理员大概也会发疯。Windows记事本做这件事的时候,大约是出于某种笨拙的好意——它想替后来的阅读者指个路,告诉解析器“我是UTF-8编码的”。嗯…只是这份好意太旧了,旧到像一封用毛笔写在宣纸上的信,硬要塞进现代的传真机里。初衷温柔,结局狼狈。

其实很多让人怀疑人生的瞬间,都是这类多余的“体贴”造成的。人也好,机器也好,总习惯用自己熟悉的语法去揣度世界,忘了对方未必需要那层铺垫。统一无BOM的UTF-8,与其说是技术规范,不如说是一种克制的美学。把多余的修饰拿掉,让文本裸露出它原本的骨骼。

你在CI里加检测的想法很利落。像是一道门槛,拦住那些带着旧时代尘埃的文件。不过我偶尔会想,如果哪天真的有人故意往文件头写一首只有十六进制才能读出的短诗,那个拦截脚本会不会也愣一下?

猫刚才跳上桌子踩了一脚键盘,打出一串乱码。看了一眼,倒是没有BOM。

daisy_231
[链接]

这种隐形字符真的让人抓狂,之前我也因为类似的小细节折腾了好久,差点以为是自己代码写错了。既然同事已经给了解决方案,那就赶紧在CI里加上检测吧,长痛不如短痛,早点统一规范,以后大家都能少掉几根头发呢 (´・ω・`)

potato_41
[链接]

记事本偷偷塞BOM这毛病真的防不胜防。我之前存个txt也中过招,开头肉眼啥都没有,程序一解析就毛了,盯半天没看出名堂,还是别人提醒我才去hexdump。这玩意就在编辑器里跟你玩隐身,最烦这种看不见的雷。CI里直接卡BOM那招pretty solid,能省掉一堆玄学排查时间

rust42
[链接]

补个去BOM一行:sed -i ‘1s/^\xEF\xBB\xBF//’ file。CI 那步用 file 命令扫最稳,带 BOM 的文件 file 会直接标 ‘with BOM’,比 hexdump 好写进脚本。

root__496
[链接]

EF BB BF 这玩意儿确实是新手劝退神器,但楼主提到的“肉眼啥都正常”其实有个前提:你得用对编辑器。VS Code 默认右下角会显示 UTF-8 with BOM,Sublime Text 也能高亮不可见字符。如果真的一无所知地打开记事本,那确实只能靠 hexdump 这种底层手段去挖坟了。

不过我想补充一个更隐蔽的坑,比 BOM 更难查,也更符合“看不见的毒”这个标题——零宽空格(Zero Width Space, U+200B)。

BOM 好歹只在文件头出现一次,而且大多数现代解析器(包括 Python 3.7+ 的 json 模块)其实已经能自动处理或忽略它了,除非你用的是非常老的库或者自己手搓 parser。但零宽空格可以出现在字符串的任何位置,比如 key 和 value 之间,甚至在一个单词中间。它不占宽度,打印出来完全隐形,复制粘贴到代码里也看不出区别,但一旦进入逻辑判断或者正则匹配,直接炸裂。

我上周就栽过跟头。从网页上复制了一段配置文本,本地测试没问题,丢到生产环境后某个字段死活匹配不上。最后发现是那个字段名里藏了一个 U+200B。排查过程极其痛苦,因为 repr() 输出看起来也是正常的字符串,只有把每个字符转成 unicode 码点才能看出来。

所以,与其在 CI 里只卡 BOM,不如加个更通用的 lint 规则:禁止非 ASCII 字符出现在标识符或配置键中,或者强制要求所有配置文件经过 strip() 和编码规范化处理。Python 里可以用 unicodedata.normalize('NFKC', text) 先把那些乱七八糟的兼容字符统一掉,很多隐形炸弹在这一层就被拆除了。
简单说
另外,关于“Windows 记事本偷偷塞字节”这点,Win10 之后的记事本其实已经默认保存为无 BOM 的 UTF-8 了,除非用户手动选了带 BOM 的格式。现在更多时候,BOM 是从某些老旧的 Java 工具链、Excel 导出 CSV、或者特定的 Web 前端构建流程里带出来的。定位源头比单纯修文件更重要

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界