去年底我替一位同事接手了一次商品编码迁移。手头有一份后台导出的清单,120 行,一行一个编码,形如 SKU-10086。他要把它拼进一条 SQL 的 IN 条件,要求每行变成"引号包住、后面带逗号"。我心想这点活手动改就是了,结果改到第四十几行漏了一个逗号,第六十几行的行尾多出一个空格,整段贴过去直接报错,回头找问题花的时间比预估多了好几倍。从那以后,凡是"每一行都要做同一件事"的活,我不再逐行敲。
手动逐行改,错误全藏在行尾和最后一行
逐行加引号难的不是打字,而是每行都得一模一样。人眼看着两个字符串完全相同,实际一个结尾带空格一个不带,SQL 里就是两条不同的值。我数过那次的三类错:漏逗号、多空格,还有最隐蔽的最后一行。前 119 行都该是 "SKU-10086",,最后那行不该有尾随逗号,多数数据库遇到 IN ('a','b',) 会当语法错误。规则本身就是"其他行一样、最后一行不一样",这恰恰最不适合人来做。
我也试过查找替换。把每行替换成 "$1", 这种带回引的写法本来是对的,但那份文件是 Windows 下导出的,行尾是回车加换行两个字符,我的模式只匹配了换行,替换完每行末尾拖着一个看不见的回车,粘进查询编辑器照样报错。
前后缀:把每行变成代码里的字面量
这份清单要的目标形态,本质就是给每行左边加一个字符、右边加两个字符。用 文本行操作 的前后缀模式,前缀填一个 ASCII 双引号 ",后缀填 ",(也就是先引号再逗号),120 行一次性出来就是 "SKU-10086", 的形态,可以直接贴进括号里。要点在于后缀里那两个字符的顺序:写成 "," 出来就是"逗号在前",粘上去必然语法错。
换语言时只是换引号种类。JavaScript 数组里我更习惯单引号,前缀填 '、后缀填 ',;值本身可能含单引号时,先决定好用哪种引号包,别等拼完再手工找冲突。
"SKU-10086",
"SKU-10087",
"SKU-20011",
序号不是打上就行,起始值和步长要提前定
另一件常事是编号。给目录、题目加 1. 2. 看着没什么可说,但两个决定影响后面很多事。一是起始值:我有一批清单接续上月,上月编到 100,这一批必须从 101 开始,而不是补 100 行空内容。二是步长:按 5 或 10 递增是为了以后往中间插条目时不必重排;按 1 递增最自然,可一旦要在第 3 条和第 4 条之间加东西,就出现 3.5 这种编号。
补零这件事我是吃过亏才重视的。纯字符串排序下 10. 会排在 2. 前面,因为比的是第一个字符。清单条目接近或超过 10 项,我就用 01. 02. 这种补齐形式,让编号长度一致,之后交给排序或者放进文件名都不会错位。
删字符和截取:先数准位置,再看行长是否一致
加前缀的反向操作我也常用。那批编码里的 SKU- 是固定 4 个字符,需要纯数字时我选"删除每行左侧 4 个字符",只想留前几位就用截取。用这类模式前我会先扫一遍最短行:删除和截取按位置走,本来比 n 个字符短的行不可能给你完整目标,只会剩下残段甚至空行。所以我习惯再对照一次行数,行数和输入不等就说明有行被"删空"了。
下面这张表是我当时把几种目标形态倒推出来的填法,字符都是照抄可用的,读者可以直接对着试。
| 目标形态 | 前缀填 | 后缀填 | 序号设置 | 输入示例 | 输出示例 |
|---|---|---|---|---|---|
| SQL 的 IN 片段 | " | ", | 关闭 | SKU-10086 | "SKU-10086", |
| JS 数组元素(单引号) | ' | ', | 关闭 | SKU-10086 | 'SKU-10086', |
| 带编号的清单 | 留空 | 留空 | 起始 1,步长 1,补零两位 | 苹果 | 01. 苹果 |
| 接续上月的编号 | 留空 | 留空 | 起始 101,步长 5 | 苹果 | 101. 苹果 |
| 去掉固定前缀 | — | — | 删除左侧 4 个字符 | SKU-10086 | 10086 |
表格公式我也用,但不是所有场合都划算
如果数据本来就在表格里、还要顺带算别的列,公式更顺手。在 Excel 里给 A1 加引号和逗号,我会写成 =CHAR(34)&A1&CHAR(34)&",",其中 34 是双引号的十进制码点,这样比连写四个双引号去转义更好读,也不容易数错。往下填充 120 行、复制、再"只粘贴文本"覆盖回来,动作也不慢。
但我现在只在两种情况下选公式:列本身要参与计算,或者文件已经打开在表格里。反过来,纯文本清单我不愿走这条路——粘回文本要多一次"粘贴为值",而表格还会自作聪明地改内容:12 位纯数字串显示成科学计数法,超过 15 位精度直接丢掉,形如 2024120310001 这种带日期意味的编码还可能被识别成数值。需要先把清单变成规范列表时,我一般先用文本排序去重处理掉重复项再做前后缀,顺序反过来的话重复行会变成重复的带引号行,看起来像是数据本身有问题。
遇到"改某一行的中间"这类需求,我会换工具
前后缀、序号、按位置删除和截取,覆盖的是"每行动作相同"的场景。一旦每行的插入点不同——比如把 name=张三名 变成 name='张三名'——就不是行级批量动作了,得用带模式的替换,这时我会去用批量查找替换,把值捕获出来再加引号。分清一件事属于"整行的位置规律"还是"行内的内容规律",比记住哪个工具能干什么更有用。
几个我当时想过的问题
Q:一行里内容长短不一,加前缀会不会加错位置?
前后缀和序号跟行长无关,它们是接在行首、行尾的,多长都一样处理,所以加引号加逗号这类动作我从不担心行不齐。真正跟长度有关的是"删除左侧 n 个字符"和"截取前 n 个",这两种要提前确认最短行也够长。
Q:序号补零除了好看还有别的用处吗?
有用处,主要在排序和文件名上。字符长度一致,字符串排序才会和数值排序一致;放进文件名时 01.jpg 到 12.jpg 能按序排,而 1.jpg、10.jpg、2.jpg 就会乱。
Q:几百行的清单一次处理完,结果怎么核对?
我看两个数:输出行数与输入行数是否相等,末尾那一两行是否符合预期。行数不等说明有行被删空或有空行混进来;末尾不对通常是逗号位置或者编号到哪。这两步比我上次那种"改完直接粘"可靠得多。
Q:原文件里的空行会被一起加引号吗?
会被当成一行处理,出来就是一对引号加逗号,属于凭空多出的项。所以我先清理空行再做行级操作,清单有重复的话也在那一步顺手去掉。
Q:最后那行的尾随逗号怎么处理最稳?
我倾向于"不管它":统一按带逗号生成,粘进括号后再删掉最后一个。真正危险的是靠人记住"最后一行不一样",一百行里漏一次就得回头查半天。
回到开头那份 120 行的清单。现在我的判断依据很简单:动作对每行都相同、且规律能用位置描述(行首、行尾、序号、第几个字符),交给行级批量处理;动作取决于行内内容,用查找替换;数据本身还要参与计算、又已经躺在表格里,才用公式。那次的教训不是"手动太慢"——慢只是表象,真正浪费时间的是"最后一行例外"这类规则,机器不会漏,人一定会。