Regex Javascript concret pour les caractères accentués (diacritiques)

166

J'ai regardé Stack Overflow ( remplacement de caractères ... hein , comment JavaScript ne suit pas le standard Unicode concernant RegExp , etc.) et n'ai pas vraiment trouvé de réponse concrète à la question:

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

Je force un champ dans une interface utilisateur à correspondre au format: last_name, first_name (dernier [espace virgule] en premier) , et je veux fournir un support pour les signes diacritiques, mais évidemment en JavaScript, c'est un peu plus difficile que d'autres langages / plates-formes.

C'était ma version originale, jusqu'à ce que je veuille ajouter un support diacritique:

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

Actuellement, je suis en train de débattre de l'une des trois méthodes pour ajouter du support, que j'ai toutes testées et que je travaille (au moins dans une certaine mesure, je ne sais pas vraiment quelle est «l'étendue» de la deuxième approche). Les voici:

Liste explicite de tous les caractères accentués que je voudrais accepter comme valides (boiteux et trop compliqués):


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àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+$/
  • Cela correspond correctement à un nom / prénom avec l'un des caractères accentués pris en charge dans accentedCharacters.

Mon autre approche était d'utiliser la .classe de caractères, pour avoir une expression plus simple:

var regex = /^.+,\s.+$/;
  • Cela correspond à peu près tout, au moins sous la forme de: something, something. C'est bien je suppose ...

La dernière approche, que je viens de trouver, pourrait être plus simple ...

/^[a-zA-Z\u00C0-\u017F]+,\s[a-zA-Z\u00C0-\u017F]+$/
  • Il correspond à une gamme de caractères Unicode - testé et fonctionnel, même si je n'ai rien essayé de fou, juste les trucs normaux que je vois dans notre département de langue pour les noms des professeurs.

Voici mes préoccupations:

  1. La première solution est beaucoup trop limitative, et bâclée et compliquée à cela. Il faudrait le changer si j'oubliais un personnage ou deux, et ce n'est tout simplement pas très pratique.
  2. La deuxième solution est meilleure, concise, mais elle correspond probablement beaucoup plus qu'elle ne le devrait réellement. Je n'ai pas trouvé de documentation réelle sur exactement ce qui .correspond, juste la généralisation de "n'importe quel caractère sauf le caractère de nouvelle ligne" (à partir d'une table sur le MDN ).
  3. La troisième solution semble la plus précise, mais y a-t-il des pièges? Je ne suis pas très familier avec Unicode, du moins en pratique, mais en regardant une table de code / suite de cette table , \u00C0-\u017Fsemble être assez solide, du moins pour ma contribution attendue.

    • Les professeurs ne soumettront pas de formulaires avec leurs noms dans leur langue maternelle (par exemple, arabe, chinois, japonais, etc.), donc je n'ai pas à m'inquiéter des caractères non latins.

Donc la ou les vraies questions : laquelle de ces trois approches est la plus adaptée à la tâche? Ou existe-t-il de meilleures solutions?

Chris Cirefice
la source
1
Il ne semble y avoir aucune raison particulière d'utiliser les expressions rationnelles plus compliquées. La seule chose à propos de la solution la plus simple est qu'elle correspondra également à «quelque chose, quelque chose, quelque chose». Vous pouvez utiliser quelque chose comme regex = /^[^,]+,\s[^,]+$/;pour éviter cela.
usr2564301
4
En un coup d'œil, le premier ne correspondra pas au nom commun "O'Donnell, Chris" ni aux noms de famille composés d'un trait d'union, ni aux noms de famille multiples (etc.). Voir Falsehoods Programmers Believe About Names pour à peu près tous les pièges possibles.
usr2564301
" l' .atome correspond à tout sauf aux nouvelles lignes " est en fait assez exact :-)
Bergi
1
S'il vous est possible d'utiliser une bibliothèque supplémentaire, vous pouvez consulter ma réponse ici
stema
Jongware, en fait, je viens de lire cet article pendant que je parcourais SO pour une réponse à ma question - j'ai également complètement oublié les traits d'union et les apostrophes, etc. cependant! Et Stema, j'ai en fait regardé cette bibliothèque et j'évite d'incorporer des bibliothèques car tout est sur Google Apps Script - incorporer des bibliothèques externes serait un cauchemar, et je ne l'utiliserais (dans ce cas) que pour un domaine particulier ... genre d'exagération: P
Chris Cirefice

Réponses:

275

Le moyen le plus simple d'accepter tous les accents est le suivant:

[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 [ ] ^ \ × ÷

Voir https://unicode-table.com/en/ pour les caractères répertoriés par ordre numérique.

Maycow Moura
la source
2
Cela fonctionne bien, +1, mais pourriez-vous expliquer pourquoi cela fonctionne?
Pierre Henry
1
@PierreHenry le -définit une plage, et cette technique exploite l'ordre des caractères dans le jeu de caractères pour définir une plage continue, ce qui en fait une solution super concise au problème
Angad
8
cela ne correspond-il pas aux traits de soulignement (et aux autres caractères non verbaux entre Zet a)?
jcuenod
21
Cela correspond au moins aux caractères [,], ^ et \, dont aucun ne doit être inclus.
Nate
2
Ne fonctionne pas, quelques caractères de cette plage ne sont pas des caractères accentués (U + 00D7 est le signe de multiplication par exemple) voir ceci: unicode-table.com/en
Jérémy Pouyet
39

La plage latine accentuée \u00C0-\u017Fn'était pas tout à fait suffisante pour ma base de données de noms, j'ai donc étendu l'expression régulière à

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

J'ai ajouté ces blocs de code ( \u00C0-\u024Fcomprend trois blocs adjacents à la fois):

Notez que ce \u00C0-\u00FFn'est en fait qu'une partie du supplément Latin-1 . Cette plage ignore les signaux de commande non imprimables et tous les symboles, à l'exception de la multiplication × \u00D7et de la division ÷ mal placées \u00F7.

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

Si vous avez besoin de plus de points de code, vous pouvez trouver plus de plages sur la liste des caractères Unicode de Wikipedia . Par exemple, vous pouvez également ajouter Latin Extended-C , D et E , mais je les ai laissés de côté car seuls les historiens semblent intéressés par eux maintenant, et les ensembles D et E ne s'affichent même pas correctement dans mon navigateur.

La regex originale s'arrêtant à \u017Fborked sur le nom "Șenol". Selon l'analyseur Unicode de FontSpace , ce premier caractère est \u0218, LATIN MAJUSCULE S AVEC COMMA CI-DESSOUS. (Ouais, il est généralement orthographié avec une cédille-S \u015E, "Şenol." Mais je ne prends pas l'avion pour la Turquie pour aller lui dire: "Vous avez mal orthographié votre nom!")

Chaim Leib Halbert
la source
1
En regardant le bloc latin de la table Unicode , je pense que vous devriez également inclure \ u1e00- \ u1eff, donc je le fais[a-zA-Z\u00c0-\u024f\u1e00-\u1eff]
cprcrack
18

Laquelle de ces trois approches est la plus adaptée à la tâche?

Dépend de la tâche :-) Pour faire correspondre exactement tous les caractères latins et leurs versions accentuées, les gammes Unicode fournissent probablement la meilleure solution. Ils peuvent être étendus à tous les caractères non blancs, ce qui peut être fait en utilisant la \Sclasse de caractères.

Je force un champ dans une interface utilisateur à correspondre au format: last_name, first_name(dernier [espace virgule] en premier)

Le problème le plus fondamental que je vois ici ne sont pas les signes diacritiques, mais les espaces. Il existe quelques noms composés de plusieurs mots, par exemple pour les titres. Vous devriez donc opter pour le plus générique, qui autorise tout sauf la virgule qui distingue le premier du nom de famille:

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

Mais votre deuxième solution avec la .classe de caractères est tout aussi bien, vous n'aurez peut-être besoin que de vous soucier de plusieurs virgules.

Bergi
la source
Hm, vous avez peut-être raison. Je l'ai probablement trop compliqué ... Pouvez-vous expliquer le regex que vous avez fourni? Je travaille avec regex depuis un petit moment maintenant, mais seulement des trucs basiques, et vraiment je n'ai aucune idée de ce que fait le vôtre! Ha
Chris Cirefice
C'est une classe de caractères annulée - signifiant "tout ce qui n'est pas la virgule".
Bergi
Ah, alors ça se lit plus comme any_character_not_a_comma, any_character_not_a_comma? C'est ce que je pensais quand je l'ai lu pour la première fois, j'ai été un peu confus quand j'ai vu trois virgules là-dedans.
Chris Cirefice
Oui, exactement. Désolé pour la confusion avec les disparus spour l'espace blanc…
Bergi
1
@ MateoTibaquirá Vous pouvez simplifier [^\s]à\S
Bergi
15

La bibliothèque XRegExp a un plugin nommé Unicode qui aide à résoudre des tâches comme celle-ci.

<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>

C'est mentionné dans les commentaires sur la question, mais c'est facile à manquer. Je ne l'ai remarqué qu'après avoir soumis cette réponse.

épine
la source
Bien, il s'avère que je n'avais pas vraiment besoin de regex sur unicode, mais plutôt sur le motif anything, anything. Cela sera utile pour les futurs lecteurs :)
Chris Cirefice
12

Que dis-tu de ça?

/^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/
alchn
la source
2
Ne correspond pas Šš.
Gajus le
5

Et ça?

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

Il correspondra à chaque mot avec des caractères accentués ou non.

Javier Pallarés
la source
2
Mais OP veut autoriser les caractères accentués.
barbsan
3
/^[\pL\pM\p{Zs}.-]+$/u

Explication:

  • \pL - correspond à tout type de lettre de n'importe quelle langue
  • \pM - attache un caractère destiné à être combiné avec un autre caractère (par exemple des accents, des trémas, des encadrés, etc.)
  • \p{Zs} - correspond à un caractère d'espacement invisible, mais qui prend de la place
  • u - Les chaînes de motif et de sujet sont traitées comme UTF-8

Contrairement à d'autres expressions régulières proposées (telles que [A-Za-zÀ-ÖØ-öø-ÿ]), cela fonctionnera avec tous les caractères spécifiques à la langue, par exemple Ššcorrespond à cette règle, mais pas aux autres sur cette page.

Malheureusement, JavaScript ne prend pas en charge ces classes de manière native. Cependant, vous pouvez utiliser xregexp, par exemple

const XRegExp = require('xregexp');

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