A07 字符串与数字处理
解决字符串与数字边界不清问题:输入清洗、数值校验、金额与日期本地化格式化的完整写法。
- 解析用 Number 严格校验,parseInt 会静默忽略尾部非法字符
- 判定用 Number.isFinite,非法输入在入口拦截返回 null
- 展示一律走 Intl.NumberFormat 与 DateTimeFormat,不手工拼千分位
- 金额以整数分存储,避免浮点二进制误差
- 跨端展示需固定 locale 与 timeZone,Intl 依赖运行时 ICU 数据
A07 字符串与数字处理
摘要:用户输入一律是字符串,运算与展示需要数字与格式化文本,两侧的转换必须显式完成。
Number与parseInt的成功条件不同,toFixed返回字符串,Intl.NumberFormat与Intl.DateTimeFormat负责本地化输出。本文给出输入清洗、数值校验、金额与日期格式化的完整写法。
关键词:字符串,数字,格式化,本地化,Intl
所属:frontend/js / A-入门 | 上一篇:A06-数组与对象 | 下一篇:A08-DOM操作与事件
1 引言
表单提交的价格 "1,299" 直接参与减法会得到 NaN,日期 "2026-10-31" 直接比较大小在部分格式下结果错误,金额 0.1 + 0.2 显示为 0.30000000000000004。三者都属于「字符串与数字边界不清」。
本文解决三个问题:输入怎么清洗、数值怎么校验、展示怎么格式化。范围见表 1-1。
| 项 | 本文覆盖 | 本文不覆盖 |
|---|---|---|
| 字符串 | 截取、拆分、替换、模板、trim |
正则(B08) |
| 数字 | 解析、校验、取整、精度 | 大数运算(BigInt) |
| 格式化 | Intl.NumberFormat、日期、百分数 |
时区处理(C04) |
2 预备知识
| 项 | 要求 | 核对方式 |
|---|---|---|
| 环境 | Node.js 20+(内置完整 Intl) |
node -e "console.log(Intl)" |
| 前置 | A06 | 能遍历与聚合数据 |
| 文件 | js-practice/strings-numbers.js |
编码 UTF-8 |
3 原理与实现
3.1 解析与校验是两步
解析负责把字符串转为数字,校验负责判定是否可用,两步不能合并,如图 3-1 所示。
flowchart TD
A["用户输入 raw"] --> B["清洗:trim、去千分位"]
B --> C["解析:Number(raw)"]
C --> D{"Number.isFinite"}
D -->|否| E["返回校验错误"]
D -->|是| F{"业务范围检查"}
F -->|否| G["返回范围错误"]
F -->|是| H["返回数字"]
图 3-1 从原始输入到可用数字的四个关卡
图上可见:
parseInt('12px')会静默返回12,跳过解析错误;用Number严格解析能让非法输入暴露在入口。
3.2 完整实现
// js-practice/strings-numbers.js;运行环境:Node.js 20+
// 1 输入清洗与严格解析
function toAmount(raw) {
const cleaned = String(raw).replace(/[,\s¥¥]/g, '');
const n = Number(cleaned);
if (!Number.isFinite(n) || n < 0) return null;
return Math.round(n * 100) / 100;
}
console.log(toAmount(' ¥1,299.50 ')); // 1299.5
console.log(toAmount('12px')); // null
console.log(Number.parseInt('12px', 10)); // 12,宽松解析
// 2 字符串常用操作
const path = '/docs/frontend/js/a07.md';
const [, , , , file] = path.split('/');
console.log(file); // a07.md
console.log(path.endsWith('.md')); // true
console.log(' trim '.trim()); // 'trim'
console.log('a-b-c'.replaceAll('-', '/')); // a/b/c
// 3 模板与填充
const id = 7;
console.log(`order-${String(id).padStart(4, '0')}`); // order-0007
// 4 格式化:金额、百分比
const fmtCNY = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' });
console.log(fmtCNY.format(1299.5)); // ¥1,299.50
const fmtPercent = new Intl.NumberFormat('zh-CN', { style: 'percent', maximumFractionDigits: 1 });
console.log(fmtPercent.format(0.256)); // 25.6%
// 5 日期格式化与安全解析
const date = new Date('2026-10-31T08:30:00+08:00');
const fmtDate = new Intl.DateTimeFormat('zh-CN', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Asia/Shanghai',
});
console.log(fmtDate.format(date)); // 2026年10月31日 08:30
console.log(date.getTime()); // 1793406600000
// 6 金额用整数分存储,避免浮点误差
const cents = (yuan) => Math.round(yuan * 100);
console.log(cents(0.1) + cents(0.2)); // 30,单位为分
console.log((cents(0.1) + cents(0.2)) / 100); // 0.3
解析方法的选择见表 3-1。
| 方法 | '12px' |
' 12 ' |
'' |
适用场景 |
|---|---|---|---|---|
Number(v) |
NaN |
12 |
0 |
严格解析用户输入 |
parseInt(v, 10) |
12 |
12 |
NaN |
已知带单位的前缀数字 |
parseFloat(v) |
12 |
12 |
NaN |
小数宽松解析 |
v * 1 |
NaN |
12 |
0 |
不推荐,语义不清 |
格式化选项见表 3-2。
| 目标 | 构造参数 | 示例输出 |
|---|---|---|
| 金额 | { style:'currency', currency:'CNY' } |
¥1,299.50 |
| 百分比 | { style:'percent', maximumFractionDigits:1 } |
25.6% |
| 千分位 | { maximumFractionDigits:0 } |
1,300 |
| 日期 | { dateStyle:'medium', timeZone:'Asia/Shanghai' } |
2026年10月31日 |
| 相对时间 | new Intl.RelativeTimeFormat('zh-CN') |
3 天前 |
4 验证与讨论
- 执行脚本,确认
toAmount(' ¥1,299.50 ')为1299.5,toAmount('12px')为null。 - 把
Number(cleaned)换成parseInt(cleaned, 10),确认非法输入不再被拦截。 - 分别用
Intl与toFixed(2)格式化1299.5,比较千分位与货币符号差异。 - 执行
0.1 + 0.2 === 0.3与分单位计算,对照结果。
| 检查项 | 预期结果 | 说明 |
|---|---|---|
| 清洗后解析 | 1299.5 |
去符号与千分位 |
| 非法输入拦截 | 返回 null |
Number.isFinite 判定 |
| 本地化输出 | 带货币符号与千分位 | Intl 按语言规则 |
| 精度控制 | 分单位相加为 30 |
整数运算回避浮点误差 |
讨论与局限。 Intl 输出依赖运行时 ICU 数据,浏览器与 Node.js 版本不一致时结果可能不同;跨端展示需固定 timeZone 与 locale。另,Intl.DateTimeFormat 不解析字符串,传给 new Date 的格式必须是规范支持的写法。
5 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
金额运算得 NaN |
输入含货币符号或千分位 | 先清洗再 Number |
显示 0.30000000000000004 |
浮点二进制误差 | 用整数分存储,展示时再除 |
| 日期显示差一天 | 未指定时区,按 UTC 解析 | 传完整 ISO 字符串并指定 timeZone |
| 千分位分隔符在部分环境缺失 | Intl 构造参数不完整 |
显式传 locale 与 style |
trim 对全角空格无效 |
全角空格不在默认空白集合内 | 用正则 replace(/\\u3000/g, '') |
6 结论
| 序号 | 结论 | 依据 |
|---|---|---|
| 1 | 解析用 Number,判定用 Number.isFinite |
图 3-1 |
| 2 | parseInt 会静默忽略尾部字符 |
表 3-1 |
| 3 | 展示一律走 Intl,不手工拼千分位 |
表 3-2 |
| 4 | 金额以整数分存储 | 3.2 节 |
- 上一篇:A06-数组与对象
- 下一篇:A08-DOM操作与事件
- 返回:A00-总结 | 科目 README
参考文献
[1] MDN Web Docs · Intl.NumberFormat,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat(访问于 2026-10-31)
[2] ECMA-402 · Intl.NumberFormat,https://tc39.es/ecma402/#numberformat-objects(访问于 2026-10-31)

评论 0
还没有评论,快来抢沙发吧~