杂记 › 技术笔记 › 文本分列后空字段没丢:a,,b 照留、列号越界填空、填 0 或 abc 退回全部列实测

文本分列后空字段没丢:a,,b 照留、列号越界填空、填 0 或 abc 退回全部列实测

把 text-column 的脚本原文在沙箱里逐字跑,实测分列与合并两档:Tab 档那个 value 是 2 字符字面量、空字段全部保留、列号越界填空而填 0 退回全部列,两套行过滤口径各记各的账。

空字段没丢,是我一开始想错了

先说结论:粘进分列框的 a,,b,两个逗号中间那个空字段没被吃掉;「保留列号」填 2 得到 0 字节的行、填 0 反而三列全吐出来。三个分支同出一条链。

被测页是 文本分列与合并:模板 10134 字节,<script> 原文 4123 字符 / 94 行。页面共三档 tab,本篇只测前两档,第三档见旧文笛卡尔积。读数出自 Node 沙箱加 DOM stub,真机只读回静态属性;CRLF 是否归一化、点 tab 后各框初值、rows 与滚动、大于 1 MB 输入的卡顿,四项未实测。

Tab 档的 value 是 2 个字符

L26 与 L41 那两个 tab 选项都写 value="\t",是反斜杠加小写 t 共 2 字符;真机读回是 U+005C U+0074,与 String.fromCharCode(9) 判不相等。getSep 只认这 2 字符串、换成 U+0009,另外 10 个入参全部原样返回。数据里是字面量 \t 时(a\tb\tc 共 7 字符),选 Tab 档切不开、输出原样 7 字节。

空字段一律保留

doSplit 的 else 分支只 map 加 join、不 filter,相邻两个分隔符之间必产出一个空串,它原样活到输出(输入输出同取逗号档):a,,b 切 3 段、输出 4 字节,行尾 a,b, 留 4 字节带一个空尾段;输出换 - 档时 a,,b 出 8 字节的 a - - b。段数账是「分隔符数 + 1」,首屏每行 2 个逗号对 3 段。空字段这种空只由相邻分隔符生成:只选它(列号 2)得 0 字节的行,统计条却报输出行数 1。

保留列号:1 基、照给定顺序、不去重

链是整格 trim、按逗号 split、每片 parseInt(s.trim(), 10)、滤掉 NaN 与非正数,取值用 parts[idx - 1] 所以是 1 基,选空了才走全列。基准行 张三,25,北京,输出分隔符临时换竖线:

列号填实测输出读法
1,3张三|北京5 字符
1,5张三|越界填空
50 字节的行不丢行,报 1
2,,325|北京空片被滤
1 - 3、1<TAB>2张三截成 1
0、abc、全角2张三|25|北京退全列

越界这种空来自 5 打在 3 段的行上:填空串、不丢行、统计报 1。非法这种空来自 0、abc、全角2:整格作废退全列,跟留空同一条分支。

边角都出自 parseInt:1.9 读成 1、2.99 读成 2、 007 读成 7。量级再大轮到浮点:21 位的 999999999999999999999 读回 1e+21,那个 idx - 1 在这量级不改变值,取到的仍是 undefined、输出 0 字节而统计报 1。

两套行过滤:一个看长度,一个 trim 后看

分列用 filter(l => l.length > 0):a,b、空格行、c,d、真空行、d,e 共 5 段,报输入行数 4、输出 12 字节 4 行,空格行是 0 字节空行——行数算它、内容看不见它,改选列 1,2 才让它现形,成 , 1 字节。CRLF 的空行是 1 个 \r、长度为 1,过得了这条判定(3 对 2)。合并档带 trim,空白行不进账:B 侧塞 1 个空格行加 1 个真空行仍报列表B 3、输出仍 27 字节;A 侧掉 1 行就整排错位,末行成 - z。

只含空白的行,两档各记什么账 分列 length > 0:空格行活,5 段报 4 行 CRLF 空行是 1 个回车 带它算 3 行,LF-only 算 2 合并 trim 后判长度:不进账

图 1:同一条看不见的行,两档各记各的账

doMerge 取两侧较大者,缺侧照挂连接符

A 给 5 行、B 给 3 行、连接符用默认 - :结果 5 行 27 字节,前 3 行 a - x 各 5 字节,末两行是 d - 这种 4 字节——缺的那侧填空串,不减行,尾巴挂着连接符;B 长则头挂 - d。连接符填空串才有干净的 d,5 行 12 字节;两侧都空才 0 行、统计报 0/0/0。50 行配 37 行出 442 字节,比空连接符档的 292 多 150,正是 50 行乘 3 字节连接符,缺侧 13 行照拼。

幂等只在空行处破

同取逗号档时:干净数据三趟逐字节相等、都是 33 字节;带内边空格的 张三, 25 ,北京 第一趟把 18 字节改成 16 字节,之后稳定在 16。破口是空格行——样本 P(53 段 / 580 字节,含 1 空格行)连跑四趟是 580、578、577、577,空格行在第一趟被写成空行、第二趟才滤掉,行数从此永久少 1(52 行变 51 行)。

空格档对空字段可逆(a,,b 变两个连空格、8 字节不动),对字段内空格不可逆(New York,25 切回来 2 段变 3 段、26 字节不变)。 - 档是单向门:张三,25 出 7 字符 / 11 字节,而真机读回输入分隔符 5 档的 value 里没有它,同页切不回来。

批量样本 M 与参差样本

M 是 50 行混合加 1 空格行加 1 真空行加 1 行 row33,a,,b,共 53 段 / 775 字节;五档都是 52 行输出,空格行仍留在输出里。逗号档 703 字节 / 663 字符; - 档 909 字节,最宽 26;只保留 3 档 136 字节里有 22 行是 0 字节空行。全角逗号不入档,r3,v3,w3,x3 只切 1 刀。

宽窄账不属于 M:参差样本(三行列数不齐)四个单字符输出档同宽 29 字符 / 49 字节,换 - 档是 41 字符 / 61 字节,差的 12 字节 = 分隔符总数 6 乘 2——列数不齐,不是内容长短不齐。

它不认引号,也没有失败路径

带引号的字段 a,"b,c",d 是 9 字符,逗号档切出 4 段,被引号包住的那段劈成两半。所以「从CSV数据中提取指定列」那句在文案对照里判 [冲突]。这一页没有报错路径:maxlength、alert(、try {、onerror 各命中 0 次,非法输入只有静默兜底——整份 trim 后为空就清空输出框与统计条。

三本字节账别串用

首屏三行样例共 26 字符,走默认双逗号档输出 50 字节且与输入逐字节相等;49 与 61 字节属于参差样本那两档;703 与 909 字节属于批量样本 M 的 52 行。容量与显示无关:rows=6 只是显示行数,91669 字节的 5000 行样本单趟 3.52 至 3.93 ms、单行 5000 段不足 1 ms,都是沙箱纯计算耗时、不含 DOM 写入与 paint。行级加前后缀或编号是另一档工具的活,旧文加前缀与序号写过。

Q:a,,b 中间那个空字段还在,是没切干净?

切干净了:2 个逗号对 3 段,中间那段本来就是空串,不滤空段,join 回去仍是 4 字节;要它消失只能让列号跳过第 2 列。

Q:保留列号填了 0,为什么三列全出来了?

0 被「非正数就滤掉」那条滤掉,abc 同理,整格一个不剩,就跟留空一样走全列分支。

Q:粘进表格软件每行多出一列空的,怪哪一步?

两种输入不同:行尾多打的分隔符,a,b, 带一个空尾段;合并缺侧的 d - ,末尾还挂着连接符。

📎 相关文章

120 个词槽、15 条整句、1 句写死的开头:lorem-ipsum 的词表清点与重复片段实测 2026-11-05 SVG 预览的尺寸听谁的:显示框恒等于容器,删掉 viewBox 后 r=1 实测 2 像素 2026-11-05 13 位连写只截出 11 位:text-extract 七个内置类型的正则边界实测 2026-11-05 1 GB 文件由 1024 块拼成:任意大小文件生成的分块节奏、取消边界与三种填充字节 2026-11-04 文件名提取的三处边界:结尾斜杠只兜一格、查询串整块留下、空行静默消失 2026-11-04