可用 REGEXP 或 LIKE 匹配 ASCII 0–31 控制字符(排除空格),MySQL 用 '[\x00-\x08\x0B\x0C\x0E-\x1F]',PostgreSQL 用 E'[\u0000-\u0008\u000B\u000C\u000E-\u001F]',SQL Server 需动态拼接 LIKE;辅以 LENGTH 与 REPLACE 差值法初筛,并用 ASCII+SUBSTRING 定位具体字符。

用 REGEXP 或 LIKE 匹配常见控制字符(ASCII 0–31,不含空格)
SQL 中没有统一的“检测隐藏字符”函数,但可通过正则或通配符定位 ASCII 值在 0–31 范围内的控制字符(CHAR(0) 到 CHAR(31)),注意排除合法空格(CHAR(32))和制表符/换行符(有时需保留)。不同数据库语法差异大:
- MySQL 8.0+ 支持
REGEXP:用col REGEXP '[[:cntrl:]]'可匹配所有控制字符(含CHAR(9)、CHAR(10)、CHAR(13));若只想查非空白控制符(即排除 \t\n\r),改用col REGEXP '[\x00-\x08\x0B\x0C\x0E-\x1F]' - PostgreSQL 用
~操作符:col ~ E'[\u0000-\u0008\u000B\u000C\u000E-\u001F]';注意必须加E''启用转义 - SQL Server 不支持 POSIX 类,得拼
LIKE:例如col LIKE '%[' + CHAR(0) + '-' + CHAR(8) + CHAR(11) + CHAR(12) + CHAR(14) + '-' + CHAR(31) + ']%'(需动态拼接,因LIKE范围不支持跳过单个值)
用 LENGTH() 和 REPLACE() 差值法粗筛二进制垃圾
某些控制字符(如 CHAR(0))在部分客户端或驱动中会被截断,导致 LENGTH(col) 明显小于预期——尤其当字段内容肉眼看着很短,但实际存储长度很大时。这时可对比原始长度与“剔除常见控制符后”的长度:
- MySQL 示例:
LENGTH(col) > LENGTH(REPLACE(REPLACE(REPLACE(col, CHAR(0), ''), CHAR(1), ''), CHAR(2), '')) - 该方法不精确(只覆盖你手动列出的字符),但执行快、兼容旧版本;适合批量初筛
- 注意:SQL Server 的
LEN()会忽略末尾空格,且对CHAR(0)表现不稳定,优先用DATALENGTH()
查出具体是哪个字符:用 ASCII() / UNICODE() 配合 SUBSTRING() 定位
光知道“有控制字符”不够,调试时得定位到第几个字符、ASCII 值是多少。核心思路是逐字符检查:
- MySQL:
SELECT id, position, ASCII(SUBSTRING(col, position, 1)) AS code FROM t JOIN (SELECT 1 AS position UNION SELECT 2 UNION ... SELECT 100) p ON position (需按最大可能长度补全 <code>UNION) - PostgreSQL 更简洁:
SELECT id, generate_series(1, length(col)) AS pos, ascii(substring(col, generate_series(1, length(col)), 1)) AS code FROM t WHERE ascii(substring(col, generate_series(1, length(col)), 1)) BETWEEN 0 AND 31 AND ascii(...) != 32; - 别直接在应用层遍历——数据库内完成更快,尤其数据量大时
导入/导出场景下控制字符常来自外部,要前置清洗
很多隐藏字符根本不是业务写入的,而是从 Excel、CSV 或爬虫响应体里带进来的(比如 Excel 保存时插入的 CHAR(13) 或 BOM 头)。这时候检测只是亡羊补牢:
- ETL 过程中,在 INSERT 前用
TRIM(TRAILING '\0' FROM col)(MySQL)或REGEXP_REPLACE(col, '[\u0000-\u0008\u000B\u000C\u000E-\u001F]', '', 'g')(PostgreSQL)主动剥离 - CSV 导入时,MySQL 的
LOAD DATA INFILE支持SET col = TRIM(TRAILING '\0' FROM @col);SQL Server 的 OPENROWSET 可配合FORMATFILE过滤 - 真正麻烦的是
CHAR(0)—— 它会让多数字符串函数提前终止,所以清洗逻辑必须放在最前端,不能依赖后续的REPLACE链
控制字符问题往往不是孤立的 SQL 查询问题,而是数据链路某处的编码或协议处理失当;定位到具体字符值比单纯“有没有”重要得多,因为 CHAR(1) 和 CHAR(27) 的成因与风险完全不同。

















