JavaScript RegEx: Formatuj ciąg z dynamiczną długością do formatu blokowego

Aug 31 2020

Następujące formaty RegEx podane są jako ciąg do następującego wzorca bloku wyjściowego:

123 456 78 90 (= 3 cyfry 3 cyfry 2 cyfry 2 cyfry)

RegEx:

string.replace(/^(\d{3})(\d{3})(\d{2})(\d{2})$/g, '$1 $2 $3 $4');

Ten przykład działa tylko wtedy, gdy dany ciąg wejściowy odpowiada dokładnej długości 10 cyfr.

Jak mogę dostosować wyrażenie RegEx do pracy z każdą długością ciągu?

  • 1234 -> 123 4
  • 1234567 -> 123 456 7
  • 123456789 -> 123 456 78 9
  • 1234567890123 -> 123 456 78 90 12 3

Tło: Chcę bezpośrednio sformatować ciąg wejściowy pola wejściowego formularza, gdy użytkownik wprowadzi liczbę ...

Odpowiedzi

2 anubhava Aug 31 2020 at 15:05

Korzystając z nowoczesnego lookbehind JavaScript, który umożliwia dynamiczne potwierdzanie długości, możesz użyć tego wyrażenia regularnego, aby dopasować:

/(?<=^(?:\d{6}(?:\d{2})*|(?:\d{3}){1,2}))(?=\d+$)/g

Po prostu zastąp to pojedynczą spacją, tj " "

RegEx Demo

Spojrzeć za siebie:

  • (?<=: Rozpocznij asercję
    • ^(?:\d{6}(?:\d{2})*: Upewnij się, że mamy 6 cyfr, po których następuje 0 lub więcej par cyfr
    • |: LUB
    • (?:\d{3}){1,2}): Spraw, abyśmy mieli za sobą jeden lub dwa zestawy 3-cyfrowych zestawów
  • ): Koniec asercji

Patrz przed siebie:

  • (?=\d+$): Upewnij się, że mamy przed sobą co najmniej jedną cyfrę
3 T.J.Crowder Aug 31 2020 at 15:08

Rozwiązania anubhavy i Wiktora są sprytne, ale nie sądzę, żebym użył do tego wyrażenia regularnego; rozwiązania wydają się zbyt skomplikowane, aby je utrzymać. (To jest wezwanie do oceny). Oto podejście, które pobiera poszczególne cyfry i wstawia spacje po trzeciej cyfrze i po każdej kolejnej cyfrze, której pozycja indeksu jest liczbą nieparzystą:

result = [...str].map((d, i) => d + (i === 2 || (i >= 5 && i % 2 === 1) ? " " : "")).join("");

Przykład na żywo:

const tests = [
    ["1234", "123 4"],
    ["1234567", "123 456 7"],
    ["123456789", "123 456 78 9"],
    ["1234567890123", "123 456 78 90 12 3"],
];

function format(str) {
    return [...str].map((d, i) => d + (i === 2 || (i >= 5 && i % 2 === 1) ? " " : "")).join("");
}

for (const [str, expected] of tests) {
    const result = format(str);
    console.log(`|${str}|`, "=>", `|${result}|`, result === expected ? "OK" : "*** ERROR");
}

Oto wersja, która działa tylko w środowiskach ES5:

result = str.split("").map(function(d, i) {
    return d + (i === 2 || (i >= 5 && i % 2 === 1) ? " " : "");
}).join("");
2 WiktorStribiżew Aug 31 2020 at 15:09

Możesz użyć

const rx = /^(\d{3})(\d{1,3})?(\d{1,2})?(\d{1,2})?$/; $('body').on('input', '.info', function(e) {
  this.value = this.value.replace(/\s+/g,'')
   .replace(/^(\d{10}).*/, '$1') .replace(rx, (_,w,x,y,z) => z ? `${w} ${x} ${y} ${z}` : y ? `${w} ${x} ${y}` :
      x ? `${w} ${x}` : w);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input class="info" maxlength=13>

Wyrażenie ^(\d{3})(\d{1,3})?(\d{1,2})?(\d{1,2})?$regularne wykona formatowanie na żywo razem z funkcją wywołania zwrotnego używaną jako argument zastępczy. Pierwsze trzy cyfry są obowiązkowe, drugie, trzecie i czwarte bloki są opcjonalne, ale zawierają co najmniej jedną cyfrę. Spacje są dodawane tylko wtedy, gdy w bloku jest co najmniej jedna cyfra. Numer jest ponownie formatowany za każdym razem, gdy jest edytowany:

  • .replace(/\s+/g,'') usuwa dodane spacje
  • .replace(/^(\d{10}).*/, '$1') zachowuje tylko pierwsze dziesięć cyfr, jeśli jest ich więcej