Javascript Regex concreto para caracteres acentuados (diacríticos)

166

He buscado en Stack Overflow ( reemplazando caracteres ... eh , cómo JavaScript no sigue el estándar Unicode sobre RegExp , etc.) y realmente no he encontrado una respuesta concreta a la pregunta:

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

Estoy forzando un campo en una interfaz de usuario para que coincida con el formato: last_name, first_name (último [espacio de coma] primero) , y quiero proporcionar soporte para diacríticos, pero evidentemente en JavaScript es un poco más difícil que otros idiomas / plataformas.

Esta era mi versión original, hasta que quería agregar soporte diacrítico:

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

Actualmente estoy debatiendo uno de los tres métodos para agregar soporte, todos los cuales he probado y trabajo (al menos hasta cierto punto, realmente no sé cuál es el "alcance" del segundo enfoque). Aquí están:

Enumerar explícitamente todos los caracteres acentuados que me gustaría aceptar como válidos (cojos y demasiado 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àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+$/
  • Esto coincide correctamente un apellido / nombre con cualquiera de los caracteres acentuados admitidos en accentedCharacters.

Mi otro enfoque era usar la .clase de caracteres, para tener una expresión más simple:

var regex = /^.+,\s.+$/;
  • Esto coincidiría para casi cualquier cosa, por lo menos en la forma de: something, something. Eso está bien, supongo ...

El último enfoque, que acabo de encontrar, podría ser más simple ...

/^[a-zA-Z\u00C0-\u017F]+,\s[a-zA-Z\u00C0-\u017F]+$/
  • Coincide con una variedad de caracteres Unicode, probados y funcionando, aunque no intenté nada loco, solo las cosas normales que veo en nuestro departamento de idiomas para los nombres de los miembros de la facultad.

Aquí están mis preocupaciones:

  1. La primera solución es demasiado limitante y descuidada y complicada. Tendría que cambiarlo si olvido un personaje o dos, y eso no es muy práctico.
  2. La segunda solución es mejor, concisa, pero probablemente coincida mucho más de lo que debería. No pude encontrar ninguna documentación real sobre exactamente qué .coincide, solo la generalización de "cualquier carácter excepto el carácter de nueva línea" (de una tabla en el MDN ).
  3. La tercera solución parece ser la más precisa, pero ¿hay alguna trampa? No estoy muy familiarizado con Unicode, al menos en la práctica, pero mirar una tabla de códigos / continuación de esa tabla , \u00C0-\u017Fparece ser bastante sólido, al menos para mi entrada esperada.

    • La facultad no enviará formularios con sus nombres en su idioma nativo (por ejemplo, árabe, chino, japonés, etc.), por lo que no tengo que preocuparme por los caracteres fuera del latín.

Entonces, la (s) verdadera (s) pregunta (s) : ¿Cuál de estos tres enfoques es el más adecuado para la tarea? ¿O hay mejores soluciones?

Chris Cirefice
fuente
1
Parece que no hay una razón particular para usar las expresiones regulares más complicadas. Lo único sobre la solución más simple es que también coincidirá con "algo, algo, algo". Podrías usar algo como regex = /^[^,]+,\s[^,]+$/;para evitar eso.
usr2564301
44
De un vistazo, el primero no coincidirá con el nombre común "O'Donnell, Chris" ni con los apellidos compuestos con un guión, ni con los apellidos múltiples (etc.). Vea Los programadores de Falsehoods Believe About Names para conocer casi todas las posibles trampas.
usr2564301
" el .átomo coincide con cualquier cosa excepto las líneas nuevas " en realidad es bastante exacto :-)
Bergi
1
Si es posible que use una biblioteca adicional, puede echar un vistazo a mi respuesta aquí
stema
Jongware, en realidad acabo de leer ese artículo mientras buscaba SO para obtener una respuesta a mi pregunta; también me olvidé por completo de los guiones y apóstrofes y cosas similares, estaba más preocupado por hacerlo internacional primero: P Me alegra que lo hayas traído aunque! Y Stema, en realidad miré esa biblioteca y evité incorporar bibliotecas porque todo esto está en Google Apps Script: incorporar bibliotecas externas sería una pesadilla, y solo lo estaría usando (en este caso) para un campo en particular ... tipo de exageración: P
Chris Cirefice

Respuestas:

275

La forma más fácil de aceptar todos los acentos es 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 ver los caracteres enumerados en orden numérico.

Maycow Moura
fuente
2
Funciona bien, +1, pero ¿podrías explicar por qué funciona?
Pierre Henry
1
@PierreHenry the -define un rango, y esta técnica explota el orden de los personajes en el juego de caracteres para definir un rango continuo, lo que lo convierte en una solución súper concisa para el problema
Angad
8
¿No coincidirá esto con los caracteres de subrayado (y los otros caracteres que no son palabras entre Zy a)?
jcuenod
21
Esto coincide al menos con los caracteres [,], ^ y \, ninguno de los cuales debe incluirse.
Nate
2
No funciona, pocos caracteres en este rango no son caracteres acentuados (U + 00D7 es el signo de multiplicación, por ejemplo), vea esto: unicode-table.com/en
Jérémy Pouyet
39

El rango latino acentuado \u00C0-\u017Fno era suficiente para mi base de datos de nombres, así que extendí la expresión regular a

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

Agregué estos bloques de código ( \u00C0-\u024Fincluye tres bloques adyacentes a la vez):

Tenga en cuenta que en \u00C0-\u00FFrealidad es solo una parte del suplemento Latin-1 . Ese rango omite las señales de control no imprimibles y todos los símbolos, excepto la multiplicación x \u00D7y la división ÷ \u00F7.

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

Si necesita más puntos de código, puede encontrar más rangos en la Lista de caracteres Unicode de Wikipedia . Por ejemplo, también podría agregar Latin Extended-C , D y E , pero los omití porque solo los historiadores parecen interesados ​​en ellos ahora, y los conjuntos D y E ni siquiera se muestran correctamente en mi navegador.

La expresión regular original se detuvo en \u017Fel nombre "Șenol". De acuerdo con el analizador Unicode de FontSpace , ese primer personaje es \u0218, LETRA DE CAPITAL LATINO S CON COMA ABAJO. (Sí, generalmente se deletrea con una cedilla-S \u015E, "Şenol". Pero no voy a volar a Turquía para ir a decirle: "¡Estás deletreando mal tu nombre!")

Chaim Leib Halbert
fuente
1
Al echar un vistazo al bloque latino de la tabla Unicode , creo que también deberías incluir \ u1e00- \ u1eff, así que lo estoy haciendo[a-zA-Z\u00c0-\u024f\u1e00-\u1eff]
cprcrack
18

¿Cuál de estos tres enfoques es el más adecuado para la tarea?

Depende de la tarea :-) Para que coincida exactamente con todos los caracteres latinos y sus versiones acentuadas, los rangos Unicode probablemente brinden la mejor solución. Podrían extenderse a todos los caracteres que no sean espacios en blanco, lo que podría hacerse utilizando la \Sclase de caracteres.

Estoy forzando un campo en una interfaz de usuario para que coincida con el formato: last_name, first_name(último [espacio de coma] primero)

El problema más básico que estoy viendo aquí no son los signos diacríticos, sino los espacios en blanco. Hay algunos nombres que consisten en varias palabras, por ejemplo, para títulos. Por lo tanto, debe elegir el más genérico, que permite todo menos la coma que distingue el nombre del apellido:

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

Pero su segunda solución con la .clase de caracteres es igual de buena, entonces es posible que solo deba preocuparse por múltiples comas.

Bergi
fuente
Hm, tal vez tengas razón. Probablemente lo compliqué demasiado ... ¿Podría explicar la expresión regular que proporcionó? He estado trabajando con regex por un tiempo, pero solo cosas básicas, ¡y realmente no tengo idea de lo que realmente hace el tuyo! Ja
Chris Cirefice
Es una clase de caracteres negada , que significa "cualquier cosa además de la coma".
Bergi
Ah, entonces se lee más como any_character_not_a_comma, any_character_not_a_comma? Eso es lo que pensé cuando lo leí por primera vez, me confundí cuando vi tres comas allí.
Chris Cirefice
Sí exactamente. Perdón por la confusión con los desaparecidos spara el espacio en blanco ...
Bergi
1
@ MateoTibaquirá Puede simplificar [^\s]a\S
Bergi
15

La biblioteca XRegExp tiene un complemento llamado Unicode que ayuda a resolver tareas como esta.

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

Se menciona en los comentarios a la pregunta, pero es fácil pasarlo por alto. Lo noté solo después de enviar esta respuesta.

espina
fuente
Bien, resulta que en realidad no necesitaba regex en Unicode, sino más bien en el patrón anything, anything. Esto será útil para futuros lectores :)
Chris Cirefice
12

¿Qué tal esto?

/^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/
Alchn
fuente
2
No coincide Šš.
Gajus
5

¿Qué hay de esto?

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

Emparejará cada palabra con caracteres acentuados o no.

Javier Pallarés
fuente
2
Pero OP quiere permitir caracteres acentuados.
barbsan
3
/^[\pL\pM\p{Zs}.-]+$/u

Explicación:

  • \pL - coincide con cualquier tipo de letra de cualquier idioma
  • \pM - asigna un carácter destinado a combinarse con otro carácter (por ejemplo, acentos, diéresis, cajas de cerramiento, etc.)
  • \p{Zs} - coincide con un carácter de espacio en blanco que es invisible, pero ocupa espacio
  • u - Las cadenas de patrones y temas se tratan como UTF-8

A diferencia de otras expresiones regulares propuestas (como [A-Za-zÀ-ÖØ-öø-ÿ]), esto funcionará con todos los caracteres específicos del idioma, por ejemplo, Ššcoincide con esta regla, pero no coincide con otros en esta página.

Desafortunadamente, JavaScript de forma nativa no admite estas clases. Sin embargo, puede usar xregexp, por ejemplo

const XRegExp = require('xregexp');

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