是否可以使用一次传递进行数字分组(例如,将数字1000
转换为字符串"1 000"
,只使用正则表达式? (我知道正则表达式和语言设施之间的界限在某些系统中有点模糊 - 在回复之前倾听你的良心。)
我要问的原因:另一个开发人员最近问我如何在JavaScript中进行数字分组,并使用regexp向我展示了一个稍微不正确的JavaScript函数。我给了他a better alternative,但他的正则表达式唠叨我,因为这种重写肯定是常规语法应该能做的事情,但我真的无法弄清楚如何为它编写正则表达式。
这是我的第一次天真尝试,我知道这是不正确的:
function group(n) { return n.toString().replace(/(\d{3})/g, "$1 "); }
这种方法有两个缺陷; group(1000)
产生"100 0"
,group(100)
产生"100 "
(尾随空格)。你可以这样修理它:
String.prototype.reverse = function () {
var a = [];
for (var i = this.length; i >= 0; --i) a.push(this[i]);
return a.join("");
};
function group(n) {
return n.toString().reverse().replace(/(\d{3})/g, "$1 ").
trimRight().reverse();
}
但这不是一个,不是两个,不是三个,而是四个通道(两个反转,一个替换,trimRight
)!然后我冒险进入后面的土地,并提出:
function group(n) { return n.toString().replace(/(\d{3}(?!\d))/g, " $1");
...根本不起作用(编辑 - 可能因为我混淆了后视和负面预测...... ) - 它只匹配最后三位数字({{ 1}}成为group(1000000000)
)。前瞻工作更好一点:
"1000000 000"
这或多或少让我回到了我开始的地方 - 我摆脱了尾随空格,但function group(n) { return n.toString().replace(/(\d{3})(?=\d)/g, "$1 "); }
仍然产生group(1000)
。
那么 - 这可以通过单个regexp替换传递来完成吗?我是语言不可知的,因为这应该只需要使用正则表达式设施。
注意:这不是关于如何进行本地化的问题,而是我没有参与过早优化。我只是好奇这是否可能,如果不是,为什么不呢。
答案 0 :(得分:8)
这是一个适用于JavaScript的版本:
return n.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1 ");
答案 1 :(得分:5)
在Perl中这样做:
$num =~ s/(?<=\d)(\d{3})(?=(\d{3})*(\D|$))/ $1/g;
要打破它:
(?<=\d)
- 我们正在检查我们的匹配是否有一个使用lookbehind的数字
(\d{3})
- 我们正在寻找一组三位数
(?=
- 我们正在使用前瞻,所以三位数必须跟着
(\d{3})*
- 这将匹配0个或更多3个数字组,即0,3,6 ...个数字。
(\D|$)
- 这将匹配非数字或字符串的结尾。
所以我们要查找一个数字,后跟3位数字,然后是0,3,6 ...数字,然后不再有数字。
不幸的是,JavaScript在其正则表达式中没有lookbehind,因此这种模式在JavaScript中不起作用。如果你放弃了后面的观察,你会得到一个前导空格,放在3,6,9 ......数字的数字前面。
答案 2 :(得分:4)
n.toString().replace(/(\d)(?=(\d{3})+\b)/g,"$1 ")
在每个数字之后添加一个空格,后跟3i数字。例如,在123456789
中,这些数字将匹配:2
,6
工作演示:http://jsbin.com/iruzu