JavaScript 经度正则怎么写:度分秒与小数格式的选择边界

编程狮(w3cschool.cn) 2026-10-30 10:13:21 浏览数 (39)
反馈

一句话先回答:经度正则不要试图同时管住书写格式和数值区间。经度正则要分两步走——先用一条正则把「书写格式」看住,小数度数写法和度分秒写法各一条;再用 Number 转数字、用比较运算去查负一百八十到正一百八十这个区间。格式交给正则,区间交给代码,两边的职责互不侵犯,这样既好维护,也不会放过「格式合法但坐标越界」的值。本文给出能直接复制的两条经度正则、一个组合校验函数,以及全角符号、多余空格、秒的小数位这些最容易失手的边界,代码基于 ECMAScript 2018 以上的正则写法。

经度正则怎么写?小数度数与度分秒分开管

一、把输入先分成两类:小数度数与度分秒

先看结论

  • 小数度数(如 116.4074)用一条简洁正则看格式,范围另用数字比较。
  • 度分秒写作(如 116°24'26.664")单独一条,毫秒就是秒的小数部分,不要新开字段。
  • 毫秒并非必需字段,写成 (\.\d+)? 可选分支,既兼容 24'26" 也兼容 24'26.664"。
  • 先 trim() 再匹配,全角符号要先转半角,否则肉眼看着一样、正则却永远不通过。
  • 正则只负责「看起来对」,最终一定要有一次数值范围检查。

经度有两种书写习惯:测绘、日志里常见十进制小数,地图标注、人工录入常见度分秒。这两类的字符集完全不同,硬合成一条正则会让分支爆炸,可读性迅速崩坏。正确的做法是开两个函数,由调用方选择。

想系统过一遍正则的基础写法,可以先看正则表达式教程,本文只讲坐标这一个场景。

小数度数的最小可用写法:

// 只看格式:可选负号 + 1 到 3 位整数 + 可选小数部分
const DECIMAL_LNG = /^-?\d{1,3}(?:\.\d+)?$/;

console.log(DECIMAL_LNG.test('116.4074'));  // true
console.log(DECIMAL_LNG.test('-180'));      // true
console.log(DECIMAL_LNG.test('+116.4'));    // false,不接受正号
console.log(DECIMAL_LNG.test('.5'));        // false,整数位不能省

注意 +116.4 也被判非法:经度的正号有无其实语义相同,这里刻意收紧,是为了让入库数据保持一种写法。如果你的表单允许用户输入正号,把 -? 改成 [+-]? 即可,这属于产品需要而非技术限制(编辑推断,按常见工程取舍给出)。

二、度分秒写法:秒的小数位就是毫秒来源

度分秒的难点不在正则本身,而在于「毫秒从哪来」。它并不单独出现,而是秒的小数部分:26.664" 里的 .664 换算成毫秒就是 664。所以正则里把它写成可选分组,解析时按秒乘以 1000 转换。

写分支较多时建议先在 JavaScript 速查手册核对一下字符转义,避免把度符号 ° 和引号写错。

// 116°24'26.664" 格式:度 1-3 位,分秒 1-2 位,秒可带小数
const DMS_LNG = /^(\d{1,3})°\s*(\d{1,2})'\s*(\d{1,2})(?:\.(\d{1,3}))?"$/;

function dmsToDecimal(input) {
  const m = DMS_LNG.exec(input.trim());
  if (!m) return NaN;
  const [, d, mi, s, ms = '0'] = m;
  const sign = input.trim().startsWith('-') ? -1 : 1;
  const sec = Number(`${s}.${ms}`);
  return sign * (Number(d) + Number(mi) / 60 + sec / 3600);
}

上面刻意用 \s* 容忍书写里的空格:116° 24' 26" 和 116°24'26" 都能命中。如果你的数据来自 OCR 或人工录入,这一步能省掉大量投诉。

三、三种常见坑:越界、负数符号与范围误判

第一类坑是「格式对了但值越界」。DECIMAL_LNG 允许三位整数,于是 199.5 会通过格式检查,可它并不是合法经度。解决方式只有范围检查:

const LIMIT = { lng: 180, lat: 90 };

function inRange(value, limit) {
  return Number.isFinite(value) && value >= -limit && value <= limit;
}

第二类坑是负号位置。-116°24'26" 的负号在最前面,若你的正则没有覆盖它,整串会因首字符不匹配而失败;而如果把负号放进度分组 -?(\d{1,3}),又会让 -0°30'00" 这类边界值悄悄通过。把符号作为整串属性一次性处理,比塞进每个分组更稳。

第三类是性能取舍:把区间硬写进正则((?:1[0-7]\d|180) 这类写法)确实能在一次匹配里完成范围判断,但每加一位数位就要重写分支,维护成本高。对本表单校验这种量级,格式+数值两次判断的性能差异可以忽略,可读性收益更大(上述结论为工程经验推断,未在本文环境做基准测试)。

四、动手验证:把两条正则放进同一个校验入口

下面这段是完整的入口函数,覆盖格式、转换、范围三段。想直接在线试跑可以粘贴到 JavaScript 在线运行里先看一遍结果。

function validateLng(raw) {
  const text = String(raw).trim().replace(/[:'"]/g, (c) => (
    { ':': ':', ''': "'", '"': '"' }[c]
  ));
  if (DECIMAL_LNG.test(text)) {
    const v = Number(text);
    return { ok: inRange(v, LIMIT.lng), value: v, type: 'decimal' };
  }
  if (DMS_LNG.test(text)) {
    const v = dmsToDecimal(text);
    return { ok: inRange(v, LIMIT.lng), value: v, type: 'dms' };
  }
  return { ok: false, value: NaN, type: 'unknown' };
}

预期结果(未在本机执行):输入 116.4074 返回 { ok: true, value: 116.4074, type: 'decimal' };输入 116°24'26.664" 返回 decimal 近似 116.4074;输入 199.5 返回 ok: false,因为越界;输入 116。4074 返回 ok: false,因为用了中文句号。错误处理的关键点是:不要只返回布尔值,把失败原因一起回传,表单才能给出可修复的提示。

语法细节可以在 JavaScript 教程里对照查阅。

经度输入格式怎么选正则?

五、进阶:带方位字母与纬度的坐标怎么收口

真实测绘数据常常带方位字母:东经写作 116°24'26.664"E,西经在度前面加负号或末尾加 W。如果直接把 E/W 喂给上面的正则,会因为末位是字母而整串失败。最省事的做法是先把末尾的方位字母剥离,再走原来的两条正则,符号单独处理:

function stripHemisphere(input) {
  const m = /^([\s\S]*?)\s*([NSEW])$/i.exec(input.trim());
  if (!m) return { text: input.trim(), sign: 1 };
  const sign = /[SW]/i.test(m[2]) ? -1 : 1;
  return { text: m[1], sign };
}

剥离后再调用 validateLng,最后把返回的 value 乘上 sign 即可得到带方向的十进制经度。纬度同理,只是范围从一百八十换成九十,把 LIMIT.lat 传进去即可,两条正则完全复用,不必为纬度再写一套。

💡 小提示:方位字母只改变正负号,不改变数值精度。与其在正则里硬塞 [NSEW]?,不如在正则之外做一层符号处理,正则本身保持「只管格式」的纯净职责,后续维护者一眼就能看懂。这样当某天数据里冒出 N/S 时,你只改符号函数,不用动已经验证过的正则。

六、把校验做成一个可复用的小工具

到了这一步,建议把 DECIMAL_LNG、DMS_LNG、inRange、stripHemisphere 和 validateLng 收进同一个模块导出,对外只暴露一个 validateLng,调用方不需要知道内部是两条正则还是三条。单元测试就按第三节列的反例表来写:合法值、越界值、带空格值、全角符号值、带方位字母值、空字符串各一条,断言通过或拒绝符合预期。把测试固下来,以后改正则先跑测试,能拦住绝大多数回归。

预期结果(未在本机执行):带 E 的 116°24'26.664"E 剥离后返回十进制约 116.4074;带 W 的同数值返回 -116.4074;空字符串在 stripHemisphere 阶段就被拦下,不会进到正则匹配。错误处理的关键点是:方位字母的解析要和格式校验解耦,这样任一侧改规则都不会拖累另一侧。

总结

  • 经度正则的职责是看住格式,不是守住区间,两者分开写最省心。
  • 小数度数与度分秒是两类输入,用两条正则分别处理,毫秒作为秒的可选小数位。
  • 入库前一定补一次数值范围检查,199.5 这种越界值只能靠比较运算拦住。
  • 人工录入场景先做 trim 与全角转半角,能挡掉绝大多数「看着一样却不匹配」的问题。

常见问题

Q:为什么经度不建议和纬度共用一条正则?

因为两者的整数位数和取值范围不同,经度是一百八十的量级,纬度是九十的量级。共用一条正则,范围判断就会失真;而只用共同部分,又失去了各自的范围校验。分两条写,语义清晰。

Q:毫秒一定要精确到三位吗?

不一定。毫秒是秒的小数位,位数取决于数据源精度:GPS 模块常给三位,人工录入可能一位都没有。正则写成 (\.\d{1,3})? 只是上限约定,实际按上游数据的最小精度来定,过度严格会把合法数据挡在门外。

Q:正则写好了,怎么快速验证覆盖度?

最省事的办法是先准备一组反例:合法值、越界值、带空格值、全角符号值、空字符串,各来一条,跑一遍看通过/拒绝是否符合预期。反例比正例更能暴露正则的漏洞。

延伸学习

0 人点赞