Alguns de nossos parceiros estão com ofertas especiais em cursos de programação e de Inglês. Confira os cursos, seus cupons de desconto e inicie 2020 atualizado.

Entenda o que é normalização no JavaScript

Entenda o que é normalização no JavaScript

7 de setembro de 2019 0 Por Ramos de Souza Janones
Powered by Rock Convert

Entenda o que é normalização no JavaScript. As formas de normalização NFC, NFD, NFKC e NFKD, entenda com exemplos práticos.

As formas de normalização Javascript NFC, NFD, NFKC e NFKD são definidas pelo Unicode.

De maneira bem resumida, alguns caracteres possuem mais de uma forma de serem representados. Por exemplo, o á (letra a com acento agudo), segundo o Unicode, pode ser representada de duas maneiras:

  1. composta – como o code point U+00E1 (LATIN SMALL LETTER A WITH ACUTE) (á)
  2. decomposta – como uma combinação de dois code points (nesta ordem):

A primeira forma é chamada NFC (Canonical Composition), e a segunda, NFD (Canonical Decomposition). As duas formas acima são consideradas “canonicamente equivalentes”, quando se trata de representar a letra a com acento agudo. Ou seja, são duas formas de se representar a mesma coisa.

O acento agudo (U+0301), neste caso, é um dos chamados combining diacritical marks (ou combining characters): caracteres que podem ser combinados com outros (como os acentos do português, por exemplo). Eles sempre aparecem depois do caractere ao qual se aplicam (no exemplo acima, ele aparece depois do a), e caso haja mais de um, a normalização sempre os retorna em uma ordem predefinida (existem regras que definem a ordem relativa entre eles).

O detalhe é que, somente ao olhar um texto (dependendo da fonte usada e dos caracteres envolvidos), não há como saber se eles estão em NFD ou NFC, já que ambos resultam no mesmo símbolo (no caso, o “á”). Vale lembrar também que nem todos os caracteres acentuados existentes no mundo possuem uma forma composta, e a única forma de serem representados é em NFD (com uma letra “base” seguida de um ou mais combining characters).

Curso completo de Games, inclusive Realidade Aumentada.Powered by Rock Convert

Existem ainda as formas NFKD (Compatibility Decomposition) e NFKC (Compatibility Composition)  no Javascript , que se baseiam no conceito de que há caracteres que são “compatíveis”, porém não canonicamente equivalentes.

Leia também:  

Um exemplo são os Letter Like Symbols, que são caracteres que se parecem com letras, mas não são exatamente as letras em si. Por exemplo, o DOUBLE-STRUCK CAPITAL H: ℍ.

Segue uma imagem caso o seu browser não renderize o caractere corretamente:

inserir a descrição da imagem aqui

O codepoint deste caractere é U+210D, e ao normalizá-lo para NFKD ou NFKC, ele se torna a letra “H” maiúscula (codepoint U+0048):

let str = String.fromCodePoint(0x210d);
// imprime a string e os respectivos code points
console.log(str); // ℍ
console.log(str.codePointAt().toString(16)); // 210d
console.log(str.normalize('NFKC')); // H
console.log(str.normalize('NFKC').codePointAt().toString(16)); // 48
console.log(str.normalize('NFKD')); // H
console.log(str.normalize('NFKD').codePointAt().toString(16)); // 48

As formas de normalização NFC, NFD, NFKC e NFKD no Javascript são definidas pelo Unicode.

De maneira bem resumida, alguns caracteres possuem mais de uma forma de serem representados. Por exemplo, o á (letra a com acento agudo), segundo o Unicode, pode ser representada de duas maneiras:

  1. composta – como o code point U+00E1 (LATIN SMALL LETTER A WITH ACUTE) (á)
  2. decomposta – como uma combinação de dois code points (nesta ordem):

A primeira forma é chamada NFC (Canonical Composition), e a segunda, NFD (Canonical Decomposition). As duas formas acima são consideradas “canonicamente equivalentes”, quando se trata de representar a letra a com acento agudo. Ou seja, são duas formas de se representar a mesma coisa.

O acento agudo (U+0301), neste caso, é um dos chamados combining diacritical marks (ou combining characters): caracteres que podem ser combinados com outros (como os acentos do português, por exemplo). Eles sempre aparecem depois do caractere ao qual se aplicam (no exemplo acima, ele aparece depois do a), e caso haja mais de um, a normalização sempre os retorna em uma ordem predefinida (existem regras que definem a ordem relativa entre eles).

O detalhe é que, somente ao olhar um texto (dependendo da fonte usada e dos caracteres envolvidos), não há como saber se eles estão em NFD ou NFC, já que ambos resultam no mesmo símbolo (no caso, o “á”). Vale lembrar também que nem todos os caracteres acentuados existentes no mundo possuem uma forma composta, e a única forma de serem representados é em NFD (com uma letra “base” seguida de um ou mais combining characters).


Existem ainda as formas NFKD (Compatibility Decomposition) e NFKC (Compatibility Composition), que se baseiam no conceito de que há caracteres que são “compatíveis”, porém não canonicamente equivalentes.

Um exemplo são os Letter Like Symbols, que são caracteres que se parecem com letras, mas não são exatamente as letras em si. Por exemplo, o DOUBLE-STRUCK CAPITAL H: ℍ.

Segue uma imagem caso o seu browser não renderize o caractere corretamente:

inserir a descrição da imagem aqui

O codepoint deste caractere é U+210D, e ao normalizá-lo para NFKD ou NFKC, ele se torna a letra “H” maiúscula (codepoint U+0048):

let str = String.fromCodePoint(0x210d);
// imprime a string e os respectivos code points
console.log(str); // ℍ
console.log(str.codePointAt().toString(16)); // 210d
console.log(str.normalize('NFKC')); // H
console.log(str.normalize('NFKC').codePointAt().toString(16)); // 48
console.log(str.normalize('NFKD')); // H
console.log(str.normalize('NFKD').codePointAt().toString(16)); // 48

No exemplo acima, eu usei o caractere ANGSTOM SIGN (codepoint U+212B), que é basicamente a letra “A” com uma “bolinha” em cima: Å.

inserir a descrição da imagem aqui

Mas este caractere possui compatibilidade com a “letra A com a bolinha”, que por sua vez possui as duas formas:

Dependendo da fonte, todas as 3 opções (o caractere ANGSTROM SIGN, a letra A com “bolinha” em NFC e em NFD) podem ser mostradas da mesma maneira (algumas fontes podem ter um símbolo ligeiramente diferente para o Angstrom, por exemplo, mas esse comportamento varia bastante).

Resumindo grosseiramente, as formas NFC e NFD não mudam a, digamos, “essência” dos caracteres envolvidos (já que eles são “canonicamente equivalentes”). Já as formas NFKC e NFKD mudam essa “essência”, já que elas resultam em outros caracteres diferentes, e de maneira unidirecional (já que a equivalência é um para muitos – vários outros caracteres letter like podem se tornar um “A” ao serem normalizados para NFKC ou NFKD).

Além disso, as formas NFKC e NFKD podem mudar o significado de um texto. Exemplo Javascript :

let str = '3' + String.fromCodePoint(0xb2);
console.log(str);
console.log(str.normalize('NFKC'));

Um dos usos da normalização NFD é para remover os acentos (na verdade, quaisquer combining characters) de uma string, como foi feito na resposta que você linkou.

Se a string estiver em NFD e eu simplesmente inverter a ordem dos codepoints, o combining character ficará antes do caractere no qual ele estava aplicado, e passará a ser aplicado em outro caractere.

Outro uso seria para colocar strings em ordem alfabética, ou fazer buscas (normalizando todos os termos para a mesma forma, você evita variações dos mesmos caracteres, facilitando os respectivos algoritmos).

As formas NFKC e NFKD são usadas – entre outras coisas – para normalizar ligaturas, como por exemplo o caractere LATIN SMALL LIGATURE FF (U+FB00): ff.

inserir a descrição da imagem aqui

Parece duas letras “f” juntas, mas é um único caractere. Quando normalizado para NFKC ou NFKD, ele se torna dois caracteres “f” (U+0066 – LATIN SMALL LETTER F):

let str = String.fromCodePoint(0xfb00);
console.log(str); // ff
console.log(str.normalize('NFKC')); // ff

Um dos usos da normalização NFD é para remover os acentos (na verdade, quaisquer combining characters) de uma string, como foi feito na resposta que você linkou.

Se a string estiver em NFD e eu simplesmente inverter a ordem dos codepoints, o combining character ficará antes do caractere no qual ele estava aplicado, e passará a ser aplicado em outro caractere.

Outro uso seria para colocar strings em ordem alfabética, ou fazer buscas (normalizando todos os termos para a mesma forma, você evita variações dos mesmos caracteres, facilitando os respectivos algoritmos).

As formas NFKC e NFKD são usadas – entre outras coisas – para normalizar ligaturas, como por exemplo o caractere LATIN SMALL LIGATURE FF (U+FB00): ff.

inserir a descrição da imagem aqui

Parece duas letras “f” juntas, mas é um único caractere. Quando normalizado para NFKC ou NFKD, ele se torna dois caracteres “f” (U+0066 – LATIN SMALL LETTER F):

let str = String.fromCodePoint(0xfb00);
console.log(str); // ff
console.log(str.normalize('NFKC')); // ff

 foram adicionados ao Unicode por motivos de compatibilidade com antigos character sets, que já possuíam tais caracteres. Com isso, também foram criados os mapeamentos entre eles e suas respectivas formas NFKC e NFKD.


Além disso, as diferentes formas podem afetar o comportamento do seu programa em Javascript , dependendo de como você trabalha com as strings:

let s1 = 'sabiá';
let s2 = 'sabiá';
// uma está em NFC, outra em NFD, portanto são diferentes
console.log(s1 == s2); // false
// normalizando, ambas passam a ser iguais
console.log(s1.normalize('NFC') === s2.normalize('NFC')); // true
// transformar string em array de codepoints
function codepoints(s) { return Array.from(s).map(c => c.codePointAt().toString(16)); }
// imprimindo os codepoints é possível ver a diferença
console.log(codepoints(s1)); // [ "73", "61", "62", "69", "e1" ]
console.log(codepoints(s2)); // [ "73", "61", "62", "69", "61", "301" ]

No exemplo acima, temos a mesma string em NFC e em NFD. Não dá para notar a diferença somente olhando as strings, já que ambas são renderizadas da mesma forma. Repare que a comparação com == deu false, e somente normalizando-as para a mesma forma elas passam a ser iguais.

Esse problema poderia ocorrer se o usuário digitasse uma string (que ele pode ter copiado e colado de outro lugar, e nesse lugar ela estava em NFD, e obviamente o usuário nem percebeu, já que visualmente não há diferença) e você a comparasse com outra string no seu código (que está em uma forma diferente do que foi digitado) – ou seja, se s1 for o que o usuário digitou e s2 é uma string hardcoded no seu código, caso elas estejam em formas diferentes (uma em NFC e outra em NFD), a comparação poderia não funcionar. Mesmo se você fizesse console.log da string s1, não conseguiria perceber o problema, já que ela seria mostrada como “sabiá”, independente de estar em NFC ou NFD.

Outro caso no  Javascript  em que pode dar diferença é com expressões regulares. Por exemplo, se eu quero verificar strings com exatamente 5 caracteres:

let s = 'sabiá';
let r = /^.{5}$/; // contém exatamente 5 caracteres

console.log(r.test(s.normalize(‘NFC’))); // true
console
.log(r.test(s.normalize(‘NFD’))); // false

Sabemos que o ponto corresponde a qualquer caractere (exceto quebras de linha), mas na verdade, ele corresponde a um codepoint. E como a string em NFD possui 6 codepoints (já que o “á” é decomposto em dois codepoints), a regex não dá match nesse caso.

Você pode ler mais sobre esses problemas entre JavaScript x Unicode neste artigo.

 » Programação » Javascript

 

LEIA TAMBÉM:  Como saber se um ano é bissexto com Javascript?
React Native Do Zero Ao Profissional: crie apps para Android e IOSPowered by Rock Convert
Siga os bons!

Ramos de Souza Janones

Janones, é um empreendedor brasileiro apaixonado por empreendedorismo e tecnologia. Ao longo dos anos trabalhando com o desenvolvimento de softwares desktop desde a linguagem Clipper, passando pelo Delphi e atualmente com Java.

Optou pela formação de Publicidade e Marketing por sua segunda empresa de tecnologia ter participado do "boom" da internet nos anos 90 e na procura de melhorar seus conhecimentos em negócios.

Em razão da principal formação e profundos conhecimentos em programação e banco de dados, é capaz de realizar o desenvolvimento de aplicativos web, desktop e mobile com maior criatividade e inovação que profissionais de desenvolvimento com uma formação única e mais especifica, dedicada somente ao desenvolvimento de softwares.

Com toda sua experiência com empresas de software, sua formação e paixão por negócios escreveu o livro "Marketing para Empresas e Profissionais de Software", publicado pela editora carioca Ciência Moderna em 2012. Além de outros livros sobre programação.

Últimos posts por Ramos de Souza Janones (exibir todos)




Frontend Do Zero Ao Profissional