使用方法
- 「制表位宽度」默认 4,允许 1 到 16 的整数;改数字或按回车立即重算。
- 「输入内容」粘贴或输入含制表符的文本,默认示例 5 行、6 个制表符。输入框按 Ctrl+Enter(Mac 上 Cmd+Enter)触发转换。
- 结果区首行给出宽度、行数、制表符总数与补空格总数;「转换后全文」是可直接复制使用的成品文本。
- 逐行对照表给出每行的原文(制表符显示为 →)、制表位数、补空格数与转换结果,用来核对每一行补了多少。
- 明细表默认显示 12 行,点「加载更多」展开全部;「导出 CSV」存成带字节序标记的文件,Excel 打开不乱码。
- 「复制结果」复制转换后的全文;「复制对照表」把当前明细表按 TSV 复制,粘进表格软件列位不乱。
- 宽度只影响补多少个空格,不改变字符顺序;同一份输入,宽度 4 与宽度 8 的结果行数、制表符数完全相同,只有空格数不同。
- 宽度写成 0 或 17、输入留空,都会给出针对该项的具体提示,不会拿上一次的结果冒充本次输出。
这个制表符转空格转换器能解决什么问题
第一类是「粘贴进来对不齐」。从终端日志、老旧代码或别人的配置里复制出来的文本,制表符在不同软件里渲染宽度不同,粘到别处就歪。本页把制表符按制表位展开成真实空格,宽度自己定,粘到哪儿都是同一副样子。
第二类是「一律替换踩坑」。很多人以为 Tab 转空格就是把每个制表符换成 4 个空格,结果第二列起全部错位:ab 后面本该补 2 个空格对齐到第 4 列,一律替换给了 4 个,直接冲到第 6 列。本页按「下一个制表位的整数倍」补,ab 后面就是 2 个空格。
第三类是 Makefile 与构建脚本的坑。Makefile 的 recipe 行必须以制表符开头,换成空格会报 missing separator;把含制表符的构建片段贴进网页、文档或 issue 时,同样会因宽度差异被读错。本页先把制表符显式展开,再粘贴到不支持制表位的载体上。
第四类是缩进审计。团队规范要求纯空格缩进,但要先知道每行有几个制表符、展开后补了几个空格。对照表的「制表位数」与「补空格数」两列就是审计依据:默认第 5 行有两个制表符、补 6 个空格,一眼可查。
这个工具在本站也叫「制表符转空格转换器」,只做制表符到空格一个方向;反向(空格折回制表符)不是总能可逆,本页有意不做,原因写在正文。
计算原理与变量说明
核心规则只有一行。按制表位(tab stop)展开:设宽度为 w,遇到制表符时补 w − (列号 mod w) 个空格,让当前列落到下一个制表位的整数倍上。列号从 0 开始计:字符 a 占第 0 列,打到 a 后列号变 1;在列号 1 处遇制表符且 w = 4,补 4 − (1 mod 4) = 3 个空格。如果列号恰好是 w 的整数倍,就补满 w 个——abcd 后面那个制表符正是如此,补 4 个空格。
精核过的例子(w = 4)。a→b 得 a···b(补 3);ab→c 得 ab··c(补 2);abc→d 得 abc·d(补 1);abcd→e 得 abcd····e(补 4);a→b→c 得 a···b···c(两个制表符各补 3);12345678→X 得 12345678····X(8 列已是制表位,补 4)。若把宽度改成 8,行首的 →x 补满 8 个空格。这里的 · 只代表一个空格,输出里都是真空格。
为什么不能一律替换。把每个制表符都换成固定 N 个空格,第一列也许碰巧对齐,第二列起必然错位——因为「补几个空格」取决于制表符落在第几列,而列号由前面的字符决定。这正是 Makefile 里 Tab 不能被空格替代的同一回事:Make 认的是「行首是制表符」这个语法事实,不是视觉宽度,换成等量空格直接报 missing separator。
每行从列 0 重新起算。制表位不跨行累计:第 1 行结束时的列号不影响第 2 行,每行都从第 0 列开始数。换行支持 LF、CRLF、CR 三种,按行拆分后各自展开;行首的制表符同样按规则补空格,缩进场景(代码块、层级列表)的层级不会被抹平。
宽度为什么默认 4。4 是主流编辑器的常见档位(VS Code 等默认 tab size 即 4);传统 Unix 工具链常用 8——expand 的默认制表位就是 8,Linux 内核代码风格也用 8。两种都对,只是口径不同,所以本页把宽度做成显式输入项,1 到 16 可调,不替用户猜。
列号按显示宽度计。中日韩字符、全角标点记 2 列,其余记 1 列,与等宽终端的口径一致;代理对(emoji 等)按两个码元各 1 列计。这个口径影响的是补几个空格,不影响任何已有字符。
反向不做。空格折回制表符只有当成组空格恰好对齐到制表位时才无损;把「任意」空格折回去会改变内容语义(比如对齐用的空格与缩进用的空格混在一起时无法区分)。本页只做制表符到空格这一个方向,并在结果区注明。
完整示例(数值仅作演示)
默认示例:宽度 4,输入 5 行、6 个制表符。转换后全文逐字如下——第 1 行 a b(a 后 3 个空格);第 2 行 abc d(1 个空格);第 3 行 abcd e(4 个空格);第 4 行 12345678 X(4 个空格);第 5 行 a b c(两个制表符各补 3 个,共 6 个)。合计补空格 3+1+4+4+6 = 18 个,与结果区首行一致。
逐行对照表把这 5 行摊开:第 1 行原文 a→b、制表位 1 个、补 3 个空格;第 5 行原文 a→b→c、制表位 2 个、补 6 个空格。任一行补的数 = 该行每个制表符各自的 w − (列号 mod w) 之和。
换宽度看差异。宽度改成 8,第 1 行变成 a b(补 7 个空格,落到第 8 列),第 4 行因为 8 列正好是制表位仍然补 8 个;宽度改成 2,第 1 行补 1 个空格(1 mod 2 = 1,补 2−1 = 1),第 4 行 12345678 是 8 个字符、8 mod 2 = 0,补满 2 个。
精核例子与「一律替换」错位对照表
下表左两列是精核过的输入与补空格数(宽度 4,最后一行为宽度 8),右列是输出,· 代表一个空格。→ 代表输入里的一个制表符。
| 输入(→ = 制表符) | 宽度 | 补空格数 | 输出(· = 空格) |
|---|---|---|---|
| a→b | 4 | 3 | a···b |
| ab→c | 4 | 2 | ab··c |
| abc→d | 4 | 1 | abc·d |
| abcd→e | 4 | 4 | abcd····e |
| a→b→c | 4 | 3 + 3 | a···b···c |
| 12345678→X | 4 | 4 | 12345678····X |
| →x | 8 | 8 | ········x |
下表把「一律换成 4 个空格」与「按制表位展开」摆在一起:第一行碰巧看着接近,第二行起一律替换就整体跑偏,错位的列数随每行前缀长度变化。
| 输入(→ = 制表符) | 一律换成 4 个空格(错) | 按制表位展开(对) |
|---|---|---|
| a→b | a····b | a···b |
| ab→c | ab····c | ab··c |
| abc→d | abc····d | abc·d |
| a→b→c | a····b····c | a···b···c |
结果边界与舍入说明
- 宽度必须是 1 到 16 的整数:写成 0 或 17 时提示超出范围;写成 4.5 时提示必须是整数;留空时提示不能为空。
- 输入留空时提示「输入内容不能为空,请粘贴或输入含制表符的文本」;不含制表符的输入也能转换,结果与输入逐字相同,补空格数为 0。
- 换行按 LF、CRLF、CR 三种拆分,原样保留在输出里,不统一替换成某一种;行数统计按拆分后的行计。
- 列号按显示宽度计(中日韩字符 2 列、其余 1 列),与等宽终端口径一致;这个口径只影响补空格数。
- 行首空白与行首制表符一律保留并展开,本页不做 strip、不去重、不改写任何非制表符字符。
- 补空格数按行累加,与结果区首行「补空格 N 个」一致;没有任何四舍五入,全部是整数计数。
- 本页只做制表符到空格一个方向;反向(空格折回制表符)不做,因为它只在成组空格恰好对齐制表位时才无损。
- 页面不联网、不写 localStorage,输入内容只参与本次转换。
常见问题
为什么不能把每个制表符都换成 4 个空格?
因为该补几个空格取决于制表符落在第几列,而列号由同一行里它前面的字符决定。规则是补「宽度 − (列号 mod 宽度)」个空格:a 后面补 3 个、ab 后面补 2 个、abc 后面补 1 个、abcd 后面补 4 个。一律换成 4 个,第一行也许碰巧对齐,第二列起必然错位。
这和 Makefile 报 missing separator 是一回事吗?
是同一类问题的两个面。Make 要求 recipe 行首是制表符这个语法事实,换成等量空格就报 missing separator;本页处理的是视觉层:把制表符展开成与制表位对齐的空格,让文本在只认空格的载体(网页、文档、issue)上不错位。注意展开后的文本反过来不能再喂给 Make。
宽度选 4 还是 8?
看目标场景。4 是主流编辑器的常见档位(VS Code 等默认值);8 是传统 Unix 工具链的口径(expand 默认制表位为 8,Linux 内核代码风格也用 8)。本页默认 4,并把宽度做成 1 到 16 的显式输入项,不替用户猜。
每行都从第 0 列重新数制表位吗?跨行会累计吗?
不累计,每行独立从列 0 开始。第 1 行结束时停在第几列,不影响第 2 行的第一个制表符补几个空格。换行按 LF、CRLF、CR 三种格式拆分,各自展开。
行首的制表符(缩进)会被处理吗?
会,而且必须处理。行首制表符同样按「补到下一个制表位」展开,代码块与层级列表的缩进层级不会丢。默认示例没有行首制表符,粘一段带缩进的代码进去就能在对照表里看到每行的补空格数。
为什么没有「空格转制表符」的反向功能?
因为它不总是可逆。把成组空格折回制表符,只有在这组空格恰好从某个制表位开始、到下一个制表位结束时才无损;对齐用的空格和缩进用的空格混在一起时根本无法区分。本页只做制表符到空格一个方向,并在工具区标明。
中文按几列算?会不会和终端显示不一致?
本页按显示宽度计:中日韩字符与全角标点记 2 列,其余记 1 列,与等宽终端口径一致;emoji 等代理对按两个码元各 1 列计。这个口径只影响补几个空格,输出里所有原有字符一个不少。
输入的数据会上传吗?工具免费吗?
免费,无需注册。所有转换都在当前浏览器本地完成,页面不联网、不写 localStorage,输入内容只用于本次转换。