JavaScript RegEx: string de formato com comprimento dinâmico para formato de bloco

Aug 31 2020

Os seguintes formatos RegEx fornecem string para o seguinte padrão de bloco de saída:

123 456 78 90 (= 3 dígitos 3 dígitos 2 dígitos 2 dígitos )

RegEx:

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

Este exemplo só funciona se a string de entrada fornecida corresponder ao comprimento exato de 10 dígitos.

Como posso ajustar o RegEx para trabalhar com cada comprimento de string?

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

Background: Eu quero formatar a string de entrada de um campo de entrada de formulário diretamente, quando um usuário insere o número...

Respostas

2 anubhava Aug 31 2020 at 15:05

Usando o lookbehind Javascript moderno que permite a afirmação de comprimento dinâmico, você pode usar este regex para corresponder:

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

Basta substituir isso por um único espaço, ou seja" "

Demonstração RegEx

Olhar para trás:

  • (?<=: Iniciar afirmação
    • ^(?:\d{6}(?:\d{2})*: Certifique-se de que temos 6 dígitos seguidos por 0 ou mais dígitos pares atrás
    • |: OU
    • (?:\d{3}){1,2}): Faça com que tenhamos um ou dois conjuntos de conjuntos de 3 dígitos atrás
  • ): Finalizar afirmação

Olhe para frente:

  • (?=\d+$): Certifique-se de que temos pelo menos um dígito à frente
3 T.J.Crowder Aug 31 2020 at 15:08

as soluções de anubhava e Wiktor são inteligentes, mas não acho que usaria uma expressão regular para fazer isso; as soluções parecem complexas demais para serem mantidas. (Esta é uma chamada de julgamento.) Aqui está uma abordagem que obtém os dígitos individuais e insere espaços após o terceiro dígito e após cada dígito subsequente cuja posição do índice é um número ímpar:

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

Exemplo ao vivo:

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");
}

Aqui está uma versão que funciona em ambientes somente 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

Você pode usar

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>

O ^(\d{3})(\d{1,3})?(\d{1,2})?(\d{1,2})?$regex executará a formatação ao vivo junto com a função de retorno de chamada usada como argumento de substituição. Os três primeiros dígitos são obrigatórios, o segundo, terceiro e quarto blocos são opcionais, mas contêm pelo menos um dígito. Os espaços só são adicionados se houver pelo menos um dígito no bloco. O número é reformatado cada vez que o número é editado:

  • .replace(/\s+/g,'')remove os espaços adicionados
  • .replace(/^(\d{10}).*/, '$1')mantém apenas os primeiros dez dígitos se houver mais