A07 字符串与数字处理

2026-10-31 17:230 阅读约 12 分钟JavaScript
AI 摘要30 秒读完

解决字符串与数字边界不清问题:输入清洗、数值校验、金额与日期本地化格式化的完整写法。

  • 解析用 Number 严格校验,parseInt 会静默忽略尾部非法字符
  • 判定用 Number.isFinite,非法输入在入口拦截返回 null
  • 展示一律走 Intl.NumberFormat 与 DateTimeFormat,不手工拼千分位
  • 金额以整数分存储,避免浮点二进制误差
  • 跨端展示需固定 locale 与 timeZone,Intl 依赖运行时 ICU 数据
AI 生成 · 仅供参考

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 验证与讨论

  1. 执行脚本,确认 toAmount(' ¥1,299.50 ') 为 1299.5,toAmount('12px') 为 null。
  2. 把 Number(cleaned) 换成 parseInt(cleaned, 10),确认非法输入不再被拦截。
  3. 分别用 Intl 与 toFixed(2) 格式化 1299.5,比较千分位与货币符号差异。
  4. 执行 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 节

参考文献

[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

0/2000

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