填充 (Padding)

用特定字符填充字符串以达到指定长度的操作,通过 padStart 和 padEnd 方法实现。

填充 (Padding) 是为了把字符串对齐到指定长度,而在开头或末尾填入特定字符 (通常是空格或零) 的处理。它是数据整形、显示统一、转换为固定长度格式等编程各种场景中都会用到的基本字符串操作。例如,把商品代码"42"补齐到 6 位变成"000042",把月份数值"3"变成"03",这些处理都属于填充。

JavaScript 中可以使用 ES2017 引入的 padStart() 和 padEnd() 方法。'5'.padStart(3, '0') 返回 '005','hi'.padEnd(10, '.') 返回 'hi........'。Python 中用 str.zfill() 做零填充,用 str.ljust() 和 str.rjust() 可以用任意字符填充。C 语言的 printf 函数也能通过 %05d 这样的格式说明符实现零填充。

这些函数共通的陷阱是,当原字符串已经达到指定长度以上时,它们不做任何处理就原样返回。'abcdef'.padStart(3, '0') 的结果是 'abcdef',Python 的 'abcdef'.rjust(3, '0') 也一样。如果误以为它们会按指定长度截齐,就那样去组装固定长度记录,那么混入了长值的行会单独出现错位。请务必在填充之外再加入把超出部分截断的处理。填充字符传入 2 个以上时,会重复到需要的长度再中途截断,因此 '7'.padStart(5, 'ab') 会变成 'abab7'。Python 的 zfill() 的规格是把符号留在开头,'-5'.zfill(3) 是 '-05'。

零填充在日期和时间的格式中尤为重要。写成"2024-03-05"而不是"2024-3-5",可以让作为字符串的排序正确工作,也保持了显示上的统一感。文件名的序号 (file001.txt、file002.txt) 同样使用零填充,这样在操作系统的文件列表中就会按正确的顺序显示。金融系统中账号和交易号的零填充是必须的,生成固定长度记录也少不了填充。

容易与填充混淆的概念是 CSS 的 padding 属性。CSS 的 padding 指定元素内侧的留白,与字符串操作的填充是完全不同的概念。另外,加密中的 padding (如 PKCS#7 填充) 是为了调整分组密码的数据长度而做的处理,其目的也与字符串的填充不同。需要根据上下文判断正确的含义。

对含有全角字符的字符串做填充时,要注意显示宽度与字符数的不一致。全角字符通常占 2 个字符的显示宽度,因此单纯基于字符数的填充无法把表格或日志输出的列对齐。需要实现考虑 East Asian Width 属性的基于宽度的填充函数,或者使用专用的库。不过 East Asian Width 中还有一个叫 Ambiguous 的区分,它在东亚的上下文中当作全角、在其他场合当作半角处理,而符号类里属于这一区分的很多,因此列的对齐方式会随环境而变。在需要严格对齐列的输出里,应当先在实际的显示环境和等宽字体下确认过,再定下宽度的判定规则。

还有一个容易被忽略的是,JavaScript 的 padStart() 和 padEnd() 所数的单位。它们基准的是 UTF-16 的代码单元,不仅与显示宽度不一致,和看上去的字符数也不一致。用代理对表示的表情符号占 2 个代码单元,因此 '👍'.padStart(3, '*') 的结果是 '*👍',即便本想对齐到 3 个字符的宽度,填充字符也只会加上 1 个。对有可能包含表情符号或组合字符的数据,要把数法统一到书写素簇单位,或者干脆避开用填充来对齐列的设计,这样才安全。

从字数统计的角度来看,通过填充追加的字符当然也会成为统计对象。掌握填充前后字符数变化多少,对固定长度格式的设计与校验都很重要。使用字数统计工具,可以简单确认填充后的字符串是否正确对齐到规范规定的长度。

分享这篇文章