为什么在使用某些正则表达式模式时我的函数没有正确替换

时间:2018-04-18 07:55:09

标签: javascript regex replace regex-negation regex-group

这是此SO question

的扩展

我做了一个功能,看看我是否可以正确格式化任何数字。以下答案适用于https://regex101.comhttps://regexr.com/等工具,但不在我的函数中(在节点和浏览器中尝试过): const

const format = (num, regex) => String(num).replace(regex, '$1')

基本上给出任何整数,它不应超过15位有效数字。给定任何小数,它不应超过2个小数点。

...这样 现在

format(0.12345678901234567890, /^\d{1,13}(\.\d{1,2}|\d{0,2})$/)

返回0.123456789012345678而不是0.123456789012345

format(0.123456789012345,/^-?(\d*\.?\d{0,2}).*/)

按预期返回格式化为2个小数点的数字。

2 个答案:

答案 0 :(得分:2)

让我试着解释一下发生了什么。

对于给定的输入0.12345678901234567890和正则表达式/^\d{1,13}(\.\d{1,2}|\d{0,2})$/,让我们一步一步地看看发生了什么。

  1. ^\d{1,13}确实匹配字符串0
  2. 的开头
  3. (\.现在您已经开设了一个新组,它确实匹配.
  4. \d{1,2}确实找到了数字12
  5. |\d{0,2}因此跳过此部分
  6. )所以这是捕获组的结束。
  7. $这表示字符串的结尾,但它不会匹配,因为您还剩下345678901234567890
  8. Javascript返回整个字符串,因为匹配最终失败。

    让我们在最后删除$,成为/^\d{1,13}(\.\d{1,2}|\d{0,2})/

    你回来".12345678901234567890"。这会产生一些问题。

    为什么前面的0被删除?

    因为它不是您的匹配组的一部分,附带()

    为什么我们只得到两位小数,即.12

    请记住,您正在进行replace。这意味着默认情况下,原始字符串将保留在原位,只有匹配的部分才会被替换。由于345678901234567890不是比赛的一部分,因此保持不变。唯一匹配的部分是0.12

答案 1 :(得分:0)

对标题问题的回答:你的函数没有替换,因为没有什么可以替换 - 正则表达式与字符串中的任何内容都不匹配。 csb的答案解释了所有细节。

但这可能不是你真正需要的答案。

现在,好像你有一个XY problem。你问为什么你对.replace()的电话不起作用,但.replace()绝对不是你应该使用的功能。 .replace()的角色正在替换部分字符串,而您实际上想要创建不同的字符串。此外,在评论中,您建议您的格式不仅用于向用户呈现数据,而且还打算在进一步计算中使用它。您还提到了cryptocurriencies。

让我们一个接一个地解决这些问题。

做什么而不是替换?

好吧,只需生成您需要的字符串,而不是替换您不喜欢的字符串中的内容。有一些边缘情况。而不是编写一体化正则表达式,只需逐个处理它们。

以下代码绝对不是最好的,但它的主要目的是简单并准确显示正在发生的事情。

function format(n) {
    const max_significant_digits = 15;
    const max_precision = 2;
    let digits_before_decimal_point;
    if (n < 0) {
        // Don't count minus sign.
        digits_before_decimal_point = n.toFixed(0).length - 1;
    } else {
        digits_before_decimal_point = n.toFixed(0).length;
    }
    if (digits_before_decimal_point > max_significant_digits) {
        throw new Error('No good representation for this number');
    }
    const available_significant_digits_for_precision =
        Math.max(0, max_significant_digits - digits_before_decimal_point);
    const effective_max_precision =
        Math.min(max_precision, available_significant_digits_for_precision);
    const with_trailing_zeroes = n.toFixed(effective_max_precision);
    // I want to keep the string and change just matching part,
    // so here .replace() is a proper method to use.
    const withouth_trailing_zeroes = with_trailing_zeroes.replace(/\.?0*$/, '');
    return withouth_trailing_zeroes;
}

因此,您可以按照自己的方式格式化数字。现在怎么办?

你能用这个字符串做什么?

嗯,您可以将其显示给用户。而这主要是它。该值四舍五入到(1)表示它在不同的基础上,(2)适合有限的精度,因此它对于任何计算都是无用的。而且,顺便说一句,如果你想要的是一个数字,为​​什么你首先将它转换为String

您尝试打印的价值首先是否有用?

嗯,这是最严重的问题。因为,你知道,floating point numbers are tricky。并且they are absolutely abysmal for representing money。因此,很可能您尝试格式化的数字已经是错误的数字

使用什么?

定点算术是最明显的答案。大部分时间都有效。但是,它在JS中相当棘手,数字几乎可以随时进入浮点表示。所以,use decimal arithmetic library更好。 (可选)切换到具有内置bignums和小数的语言,如Python