Regex Javascript concreto para caracteres acentuados (diacríticos)

166

Eu olhei no Stack Overflow ( substituindo caracteres ... eh , como o JavaScript não segue o padrão Unicode referente ao RegExp etc.) e não encontrei realmente uma resposta concreta para a pergunta:

How can JavaScript match for accented characters (those with diacritical marks)?

Estou forçando um campo em uma interface do usuário para corresponder ao formato: last_name, first_name (último [espaço de vírgula] primeiro) e quero fornecer suporte para sinais diacríticos, mas evidentemente no JavaScript é um pouco mais difícil do que em outros idiomas / plataformas.

Esta foi a minha versão original, até eu querer adicionar suporte diacrítico:

/^[a-zA-Z]+,\s[a-zA-Z]+$/

Atualmente, estou debatendo um dos três métodos para adicionar suporte, todos os quais testei e trabalho (pelo menos até certo ponto, não sei realmente qual é a "extensão" da segunda abordagem). Aqui estão eles:

Listar explicitamente todos os caracteres acentuados que eu gostaria de aceitar como válidos (coxos e excessivamente complicados):


var accentedCharacters = "àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ";
// Build the full regex
var regex = "^[a-zA-Z" + accentedCharacters + "]+,\\s[a-zA-Z" + accentedCharacters + "]+$";
// Create a RegExp from the string version
regexCompiled = new RegExp(regex);
// regexCompiled = /^[a-zA-ZàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+,\s[a-zA-ZàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+$/
  • Isso corresponde corretamente a um nome / sobrenome com qualquer um dos caracteres acentuados suportados accentedCharacters.

Minha outra abordagem foi usar a .classe de caracteres, para ter uma expressão mais simples:

var regex = /^.+,\s.+$/;
  • Isso iria corresponder para praticamente nada, pelo menos na forma de: something, something. Tudo bem, suponho ...

A última abordagem, que acabei de descobrir, pode ser mais simples ...

/^[a-zA-Z\u00C0-\u017F]+,\s[a-zA-Z\u00C0-\u017F]+$/
  • Ele corresponde a uma variedade de caracteres unicode - testados e funcionando, embora eu não tenha tentado nada louco, apenas as coisas normais que vejo em nosso departamento de idiomas para nomes de membros da faculdade.

Aqui estão as minhas preocupações:

  1. A primeira solução é muito limitadora e desleixada e complicada. Precisaria ser mudado se eu esquecesse um personagem ou dois, e isso não é muito prático.
  2. A segunda solução é melhor, concisa, mas provavelmente corresponde muito mais do que realmente deveria. Não consegui encontrar nenhuma documentação real sobre exatamente o que .corresponde, apenas a generalização de "qualquer caractere, exceto o caractere de nova linha" (de uma tabela no MDN ).
  3. A terceira solução parece ser a mais precisa, mas existem algumas dicas? Eu não sou muito familiarizado com Unicode, pelo menos na prática, mas olhando para um código de mesa / continuação dessa mesa , \u00C0-\u017Fparece ser bastante sólido, pelo menos para a minha entrada esperado.

    • O corpo docente não enviará formulários com seus nomes no idioma nativo (por exemplo, árabe, chinês, japonês etc.), para que eu não precise me preocupar com caracteres fora do conjunto de caracteres latinos

Portanto, a (s) questão (s) real (is) : Qual dessas três abordagens é mais adequada para a tarefa? Ou existem soluções melhores?

Chris Cirefice
fonte
1
Parece não haver razão específica para usar os regexps mais complicados. A única coisa sobre a solução mais simples é que ela também corresponderá a "algo, algo, algo". Você poderia usar algo parecido regex = /^[^,]+,\s[^,]+$/;para evitar isso.
usr2564301
4
À primeira vista, o primeiro não corresponderá ao nome comum "O'Donnell, Chris", nem os sobrenomes compostos com um hífen, nem sobrenomes múltiplos (etc.). Veja os programadores de falsidades acreditam em nomes para quase todas as armadilhas possíveis.
usr2564301
" o .átomo corresponde a qualquer coisa, exceto as novas linhas ", na verdade, é bem exato :-)
Bergi
1
Se é possível para você usar uma biblioteca adicional que você pode ter um olhar para a minha resposta aqui
stema
Jongware, na verdade, acabei de ler esse artigo enquanto procurava na SO uma resposta para minha pergunta - também esqueci completamente hífens e apóstrofos e afins, estava mais preocupado em torná-lo internacional primeiro: P Fico feliz que você o trouxe embora! Stema, na verdade, olhei para essa biblioteca e evito incorporar bibliotecas porque isso é tudo no Script do Google Apps - incorporar bibliotecas externas seria um pesadelo e eu só a usaria (neste caso) para um campo específico ... tipo de exagero: P
Chris Cirefice

Respostas:

274

A maneira mais fácil de aceitar todos os sotaques é esta:

[A-zÀ-ú] // accepts lowercase and uppercase characters
[A-zÀ-ÿ] // as above but including letters with an umlaut (includes [ ] ^ \ × ÷)
[A-Za-zÀ-ÿ] // as above but not including [ ] ^ \
[A-Za-zÀ-ÖØ-öø-ÿ] // as above but not including [ ] ^ \ × ÷

Consulte https://unicode-table.com/en/ para caracteres listados em ordem numérica.

Maycow Moura
fonte
2
Funciona bem, +1, mas você poderia explicar por que funciona?
Pierre Henry
1
@PierreHenry os -define um intervalo, e esta técnica explora a ordenação de caracteres no conjunto de caracteres para definir uma faixa contínua, contribuindo para uma solução concisa super para o problema
Angad
8
essa correspondência não será sublinhada (e os outros caracteres que não são palavras entre Ze a)?
precisa saber é o seguinte
21
Isso corresponde a pelo menos os caracteres [,], ^ e \, nenhum dos quais deve ser incluído.
Nate
2
Não trabalhando, alguns personagens deste intervalo não são caracteres acentuados (U + 00D7 é o sinal de multiplicação por exemplo) ver isto: unicode-table.com/en
Jérémy Pouyet
39

O intervalo latino acentuado \u00C0-\u017Fnão era suficiente para o meu banco de dados de nomes, então estendi a regex para

[a-zA-Z\u00C0-\u024F]
[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF] // includes even more Latin chars

Eu adicionei esses blocos de código ( \u00C0-\u024Finclui três blocos adjacentes ao mesmo tempo):

Observe que, \u00C0-\u00FFna verdade, é apenas uma parte do suplemento Latin-1 . Esse intervalo ignora os sinais de controle não imprimíveis e todos os símbolos, com exceção da multiplicação desajeitadamente × \u00D7e divisão ÷ \u00F7.

[a-zA-Z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u024F] // exclude ×÷

Se você precisar de mais pontos de código, poderá encontrar mais intervalos na lista de caracteres Unicode da Wikipedia . Por exemplo, você também pode adicionar Latin Extended-C , D e E , mas eu os deixei de fora porque apenas os historiadores parecem interessados ​​neles agora, e os conjuntos D e E nem sequer são renderizados corretamente no meu navegador.

O regex original que parou em \u017Fborked com o nome "Șenol". De acordo com o Unicode Analyzer da FontSpace , esse primeiro caractere é \u0218, LETRAS MAIÚSCULAS LATINA COM Vírgula abaixo. (Sim, geralmente é soletrado com um cedilla-S \u015E, "olenol". Mas não estou voando para a Turquia para dizer a ele: "Você está escrevendo seu nome errado!")

Chaim Leib Halbert
fonte
1
Ter um olhar para o bloco latino mesa unicode , eu acho que você também deve incluir \ u1e00- \ u1eff, então eu estou fazendo[a-zA-Z\u00c0-\u024f\u1e00-\u1eff]
cprcrack
18

Qual dessas três abordagens é mais adequada para a tarefa?

Depende da tarefa :-) Para corresponder exatamente a todos os caracteres latinos e suas versões acentuadas, os intervalos Unicode provavelmente fornecem a melhor solução. Eles podem ser estendidos a todos os caracteres que não sejam espaços em branco, o que pode ser feito usando a \Sclasse de caracteres.

Estou forçando um campo em uma interface do usuário para corresponder ao formato: last_name, first_name(último [espaço de vírgula] primeiro)

O problema mais básico que estou vendo aqui não são diacríticos, mas espaços em branco. Existem alguns nomes que consistem em várias palavras, por exemplo, para títulos. Portanto, você deve usar o mais genérico, que permite tudo, menos a vírgula que distingue o primeiro e o sobrenome:

/[^,]+,\s[^,]+/

Mas sua segunda solução com a .classe de caracteres é tão boa quanto você, então você pode precisar se preocupar com várias vírgulas.

Bergi
fonte
Hm, talvez você esteja certo. Eu provavelmente exagerei ... Você poderia explicar o regex que você forneceu? Estou trabalhando com regex há um tempo, mas apenas coisas básicas, e realmente não tenho idéia do que o seu realmente faz! Ha
Chris Cirefice
É uma classe de personagem negada - significando "qualquer coisa além da vírgula".
21913 Bergi
Ah, então parece mais any_character_not_a_comma, any_character_not_a_comma? Foi o que pensei quando li pela primeira vez. Fiquei meio confuso quando vi três vírgulas lá.
Chris Cirefice
Sim, exatamente. Desculpe pela confusão com os desaparecidos sno espaço em branco ...
Bergi
1
@ MateoTibaquirá Você pode simplificar [^\s]para\S
Bergi
15

A biblioteca XRegExp possui um plug-in chamado Unicode que ajuda a resolver tarefas como essa.

<script src="xregexp.js"></script>
<script src="addons/unicode/unicode-base.js"></script>
<script>
  var unicodeWord = XRegExp("^\\p{L}+$");

  unicodeWord.test("Русский"); // true
  unicodeWord.test("日本語"); // true
  unicodeWord.test("العربية"); // true
</script>

É mencionado nos comentários da pergunta, mas é fácil perder. Percebi isso somente depois que enviei esta resposta.

Espinho
fonte
Bom, acontece que eu realmente não precisava me regex no unicode, mas no padrão anything, anything. Isso será útil para futuros leitores :)
Chris Cirefice
12

Que tal agora?

/^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/
alchn
fonte
2
Não corresponde Šš.
Gajus
5

Que tal isso?

^([a-zA-Z]|[à-ú]|[À-Ú])+$

Combina todas as palavras com caracteres acentuados ou não.

Javier Pallarés
fonte
2
Mas o OP quer permitir caracteres acentuados.
barbsan
3
/^[\pL\pM\p{Zs}.-]+$/u

Explicação:

  • \pL - corresponde a qualquer tipo de letra de qualquer idioma
  • \pM - alcança um caractere que deve ser combinado com outro caractere (por exemplo, acentos, trema, caixas anexas, etc.)
  • \p{Zs} - corresponde a um caractere de espaço em branco invisível, mas ocupa espaço
  • u - Strings de padrão e assunto são tratados como UTF-8

Diferente de outros regex propostos (como [A-Za-zÀ-ÖØ-öø-ÿ]), isso funcionará com todos os caracteres específicos do idioma, por exemplo, Ššé correspondido por esta regra, mas não por outros nesta página.

Infelizmente, o JavaScript nativo não suporta essas classes. No entanto, você pode usar xregexp, por exemplo,

const XRegExp = require('xregexp');

const isInputRealHumanName = (input: string): boolean => {
  return XRegExp('^[\\pL\\pM-]+ [\\pL\\pM-]+$', 'u').test(input);
};
Gajus
fonte