去年十月,双引我在一个后台管理系统里提交了一条用户昵称:张"三"。号过页面没报错,双引数据库也写进去了。号过三天后运营同事导出报表,双引Excel 直接把这行数据劈成了三列。号过她截图发到群里问“谁把名字拆了”,双引没人吭声。号过我盯着那个半角双引号看了很久——它既不是双引中文引号,也不是号过转义后的 \",就是双引一个裸的、ASCII 码 34 的号过字符,安安静静地躺在 varchar 字段里,双引等着在某个导出环节引爆。号过
双引号这东西,双引在编程世界里从来就不是一个普通字符。HTML 属性值用双引号包裹,SQL 字符串用单引号,但 MySQL 默认也认双引号,JSON 的 key 和 string value 必须用双引号。它像一把万能钥匙,出现在哪里,哪里的解析器就得重新判断“这到底是内容还是边界”。过滤它,本质上不是删掉一个符号,而是回答一个问题:这个字符在当前上下文里,到底该不该有语法意义?
我见过最粗暴的做法是 replace(/"/g, '')。直接把双引号抹掉。张"三" 变成 张三,看着挺干净。但用户如果本来就叫“张”三“”,你这一刀下去,名字就残了。更麻烦的是,有些业务场景里双引号是合法内容——比如商品标题 15.6" 笔记本内胆包,你把英寸符号干掉,变成 15.6 笔记本内胆包,意思全变了。过滤不等于删除,这个区别很多人没想清楚。
转义是另一条路。把 " 换成 " 或者 \",让解析器知道“这是内容,不是边界”。HTML 里这么干没问题,浏览器渲染出来还是双引号。但如果你把 " 再塞回数据库,下次读出来的时候,有些 ORM 会做二次转义,变成 ",页面上就显示成 " 这五个字符。转义层数一多,谁都不知道最终该显示什么。我调试过一个老系统,用户昵称里带引号,前端显示成 ",后端日志里是 \",数据库里是 ",三个地方三个样。查了一下午,最后发现是模板引擎自动转义和手动转义叠加了。
真正靠谱的思路是上下文感知。你在拼 SQL 的时候,双引号不该由你来过滤,应该用参数化查询,让驱动去处理。你在生成 HTML 的时候,双引号该转义成 ",这是 HTML 实体的职责。你在写 JSON 的时候,双引号该转义成 \",这是 JSON 规范的要求。你在做 CSV 导出的时候,字段里出现双引号,标准做法是用两个双引号 "" 来表示一个 ",并且整个字段用双引号包起来。每种场景的规则都不一样,指望一个全局过滤器打天下,迟早出事。
我现在的习惯是:输入层只做长度和编码校验,不碰双引号。存储层原样保留。输出层按目标格式分别处理。听起来多写了几行代码,但省掉了后面无数个“为什么导出乱码”的工单。那个昵称事件之后,我把导出模块的 CSV 生成逻辑重写了一遍,专门加了一条:字段内双引号替换为两个双引号。测试的时候我输入 张"三",导出的 CSV 用 Excel 打开,显示正常。运营同事没再找过我。
双引号过滤这件事,说到底是别把它当敌人。它就是一个字符,有语法意义的时候你尊重语法,没语法意义的时候你保留原样。最怕的是半懂不懂,一刀切下去,数据坏了,你还不知道为什么。