1. Without regex
The Number.toLocaleString () method can be used.
The toLocaleString () method returns a string that contains a representation of the number according to the language.
var numero = 123456.95432,
comas, puntos, local;
const noTruncarDecimales = {maximumFractionDigits: 20};
// Convertimos al número en 3 formatos diferentes
comas = numero.toLocaleString('en-US', noTruncarDecimales);
puntos= numero.toLocaleString('es', noTruncarDecimales);
local = numero.toLocaleString( undefined, noTruncarDecimales);
//Mostrar el resultado
document.getElementById('resultado').innerText =
" Separador de miles con comas: " + comas
+ "\nSeparador de miles con puntos: " + puntos
+ "\n Config local del usuario: " + local;
<pre id="resultado"></pre>
2. With regex
The problem with the regular expression you are using is that it verifies that there is not a full word limit (with \B
) before the number, and this is true between any two digits. Therefore, take the decimal part as if it were another number.
In other languages, it would be much easier to correct, but JavaScript does not support \G
or lookbehinds . What we can do to solve it easily, is to make it also coincide with a point followed by one or more digits, with \.\d+
. And if it matches, replace it with those same characters (that is, not replace).
Pattern:
/(\.\d+)|\B(?=(\d{3})+(?!\d))/g
-
(\.\d+)
- Group 1 (captures the text): Matches a literal point followed by one or more digits. Then, if it finds the decimal part of a number, we will replace it with the same value.
-
|
- O ( alternation : matches everything above or with everything below).
-
\B(?=(\d{3})+(?!\d))
- The regex of the question.
-
\B
- Matches a position that is not a word limit.
-
(?=(\d{3})+(?!\d))
- Positive assertion. Try matching the pattern without consuming characters.
-
(\d{3})+
- Followed by digits whose number is a multiple of 3.
-
(?!\d)
- which are then not followed by another digit.
This strategy has the benefit of being able to be used both in a number and in a complete text (it will format all the numbers), as well as the intention of the original function.
Replacement:
When replacing, instead of using a fixed value, we will use a function, to see if the first group was captured (the decimal part).
function(m,g1){ // Si coincidió '(\.\d+)', g1 tendrá el texto.
return g1 || "," // Sino, reemplazamos con coma (como en el regex original)
}
Code:
function milesNumeros(numero) {
return numero.toString().replace(/(\.\d+)|\B(?=(\d{3})+(?!\d))/g, function(m,g1){
return g1 || ","
});
};
// ----------- Pruebas -----------
var formateado = milesNumeros("Texto con números 36598365.36 y más números 3.659836536");
console.log(formateado);
formateado = milesNumeros(12345.6789012);
console.log(formateado);
3. Another option, with ECMAScript6 (not very compatible)
Note: This is not standardized in all browsers (see Sticky flag ("y")
).
Using the /y
modifier, you can anchor the match at the end of the last replacement. So, we can differentiate if we are in a position where we just replaced by a comma.
/((?!^)|(?:^|.*?[^\d.])\d{1,3})(\d{3})(?=(?:\d{3})*(?!\d))/gy
-
((?!^)|(?:^|.*?[^\d.])\d{1,3})
- Group 1: Matches and captures the previous text to a position of a comma. Options:
-
(?!^)
- It is not at the beginning of the text, then it can only be that it has just been replaced by a comma in this position.
-
(?:^|.*?[^\d.])\d{1,3})
- 2.a At the beginning of the text ( ^
), or 2.b we have replaced the last comma (as in (1)) but not there are more commas to insert, so you must consume characters to search if there is another number in the text .*?[^\d.]
... And then this (from 2.a or 2.b) between 1 and 3 digits \d{1,3}
.
-
(\d{3})
- Group 2: Match and capture the 3 digits that go after the comma.
-
(?=(?:\d{3})*(?!\d))
- Positive assertion verifying that there is a number of digits that is a multiple of 3, and then it is not followed by a digit (but it does not consume characters).
Options: /gy
- g (global) replace all matches, not just the first one; e and (sticky) anchoring the regular expression at the beginning of the text or at the end of the last match.
function milesNumeros(numero) {
return numero.toString()
.replace(/((?!^)|(?:^|.*?[^\d.,])\d{1,3})(\d{3})(?=(?:\d{3})*(?!\d))/gy, "$1,$2");
};
// ----------- Pruebas -----------
var formateado = milesNumeros("Texto con números 36598365.36 y más números 3.659836536");
console.log(formateado);
formateado = milesNumeros(12345.6789012);
console.log(formateado);