Permito que los usuarios creen comentarios dentro de mi aplicación.
He creado una expresión regular de JavaScript que coincide con los caracteres que me gustaría permitir en el comentario.
Esto incluye caracteres latinos básicos, algunos caracteres Latin-1 y Latin Extended-A, algunos símbolos adicionales y el retorno carraige y caracteres de nueva línea como podemos ver en la expresión regular aquí:

commentRegex = /^([A-Za-z0-9\u00C0-\u017F\u20AC\u2122\u2150\u00A9 \/.,\-_$!\'&*()="?#+%:;\<\[\]\r\r\n]{1,2000})$/; 

Luego uso la expresión regular para validar la entrada de la siguiente manera:

function validateInput(inputValue, regularExpression){
    var inputIsValid = regularExpression.test(inputValue);
    return inputIsValid;
}

var commentIsValid = validateInput(comment_input_text, commentRegex);
if(!commentIsValid){
    //comment does not contain valid characters. Notify the user.       
    //do not submit form
}else{
    //comment does contain valid characters. allow form to be submitted
}

Me gustaría permitir emojis en el comentario, así que investigué cuál es el rango Unicode para emojis y encontré este artículo que establece que los emojis se pueden combinar con los siguientes rangos:

var ranges = [
  '\ud83c[\udf00-\udfff]', // U+1F300 to U+1F3FF
  '\ud83d[\udc00-\ude4f]', // U+1F400 to U+1F64F
  '\ud83d[\ude80-\udeff]'  // U+1F680 to U+1F6FF
];

Me gustaría agregar estos rangos a mi expresión regular actual, sin embargo, no estoy seguro de la forma correcta de formatear esto. (Las expresiones regulares no son mi fuerte)
Intenté agregarlos (después de los otros valores Unicode) de la siguiente manera

commentRegex = /^([A-Za-z0-9\u00C0-\u017F\u20AC\u2122\u2150\u00A9\ud83c[\udf00-\udfff]\ud83d[\udc00-\ude4f]\ud83d[\ude80-\udeff] \/.,\-_$!\'&*()="?#+%:;\<\[\]\r\r\n]{1,2000})$/; 

Sin embargo, la expresión regular no funciona en absoluto. Agradecería su ayuda para formatearlo de la manera correcta. Gracias

0
Sarah 3 ene. 2017 a las 00:16

1 respuesta

La mejor respuesta

Como respuesta directa a su pregunta, propondría la siguiente expresión regular:

/^(?:[A-Za-z0-9\u00C0-\u017F\u20AC\u2122\u2150\u00A9 \/.,\-_$!\'&*()="?#+%:;\<\[\]\r\r\n]|(?:\ud83c[\udf00-\udfff])|(?:\ud83d[\udc00-\ude4f\ude80-\udeff])){1,2000}$/

Pero realmente, esto requiere algunas explicaciones antes de continuar ... Y antes que nada, volvamos a alguna definición ... Probablemente conozcas algunas de estas, pero son realmente necesarias para que la respuesta tenga sentido.

Las expresiones regulares son máquinas de estado que consumen "caracteres". Suena bastante simple, pero varios motores de expresiones regulares tienen una definición diferente de lo que es un "carácter", con dos variantes predominantes: o un carácter es un solo byte, o un carácter es una unidad de código UTF16 (es decir, cada secuencia de 16 bits cuando el el texto está codificado en UTF16). JavaScript usa la segunda variante.

Los caracteres emoji requieren dos unidades de código UTF16 consecutivas; esa es la razón por la que, en una expresión regular basada en UTF16, deben coincidir como dos caracteres consecutivos (por ejemplo, \ud83c[\udf00-\udfff]). Los dos caracteres forman un par, y esa secuencia debe mantenerse en la expresión regular.

En una expresión regular, una clase de carácter (por ejemplo, [a-z0-9 ,-]) coincidirá con un solo carácter de entrada, dado que está contenido en la lista de caracteres especificada. No hay secuencia ni orden en los caracteres dentro de esa clase: como máximo, se emparejará un carácter. Por lo tanto, los emojis no se pueden emparejar correctamente simplemente al incluir su unidad de código UTF16 en una larga lista de caracteres aceptados (bueno, al hacerlo, se obtendría una expresión regular que acepta todas las entradas válidas, pero también acepta muchas entradas no válidas).

Una clase de caracteres se puede reemplazar de manera equivalente por una larga lista de partículas "alternativas": (?:a|b|c|...|y|z|0|1...|9| |,|-). Tenga en cuenta aquí que utilicé un grupo de no captura, es decir (?:...), en lugar de un grupo de captura (...); esto es deseable siempre que no pretenda referirse al valor de un grupo, ya que existe un costo de desempeño asociado a la captura de ese valor. De hecho, una larga lista de alternativas es mucho menos eficiente que una partícula de clase de carácter; Sin embargo, existe una ventaja al hacerlo: las alternativas permiten hacer coincidir secuencias de varios caracteres. Por ejemplo, se podría decir (?:apple|banana|cherry|...). De esta forma, ahora es posible hacer coincidir correctamente los caracteres emoji: (?:\ud83c\udf00|\ud83c\udf01|\ud83c\udf02...\ud83c\udfff|...). Pero gastar todas las alternativas resultaría en una expresión regular ridículamente larga y difícil de mantener. Así que definitivamente querrás mezclar la clase de personaje y las alternativas de manera apropiada.

Entonces, su expresión regular tendrá básicamente la siguiente forma:

(?: [all acceptable single characters] |
    \ud83c [all acceptable low surrogates for pairs starting with d83c] |
    \ud83d [all acceptable low surrogates for pairs starting with d83d] )

Desde este punto, simplemente conecté las clases de caracteres que proporcionó en su pregunta y eliminé los espacios adicionales ...

En su pregunta, su expresión regular estaba rodeada por ^(...){1,2000}$, lo que significa que la expresión regular solo coincidiría si la cadena, desde el principio (es decir, ^) hasta el final (es decir, $ ) contenía entre 1 y 2000 de los caracteres permitidos. Agregar esto alrededor del patrón construido previamente debería dar la expresión regular que di al comienzo de mi respuesta. Sin embargo, debo advertirle que esta podría no ser la forma más apropiada de probar la longitud de la cadena de entrada. ¿Por qué imponen el límite de 2000 caracteres? ¿Ese límite se aplica realmente a su modelo de almacenamiento? Si es así, definitivamente debería considerar el hecho de que los emojis en realidad ocupan dos "caracteres" ... Y la relación será aún más compleja si su backend almacena valores con codificación UTF8 ... Por lo tanto, debería considerar verificar la longitud de el texto de entrada con una prueba distinta, escrita directamente en JavaScript, en lugar de utilizar un especificador de repetición de expresiones regulares. Si así lo decide, reemplace {1,2000} por un sufijo * (que simplemente significa "cualquier número de repeticiones").

1
jwatkins 3 ene. 2017 a las 01:59
Gracias. Esto luce bien. ¿puedes explicarme qué significa?:? y hace el | símbolo significa "O"?
 – 
Sarah
3 ene. 2017 a las 01:31
Gracias por su explicación. Esta expresión regular funciona perfectamente. Está permitiendo la mayoría de los emojis ahora, sin embargo, algunos no pasarán. No debí haber incluido la gama completa. ¿tienes alguna idea sobre eso? También noto que al final de mi expresión regular original tengo un corchete) entre} y $ así: {1,2000}) $ /; sin embargo, tiene el corchete de antemano de la siguiente manera:) {1,2000} $ / .. ¿Es esto un error en mi expresión regular original? Gracias
 – 
Sarah
3 ene. 2017 a las 01:48
Los paréntesis circundantes en su expresión regular original eran inútiles. Los paréntesis en mi expresión regular propuesta son necesarios para asegurar que el sufijo {1,2000} afecte a todo el grupo de alternativas.
 – 
jwatkins
3 ene. 2017 a las 02:02
Acabo de ver su edición sobre la longitud de la cadena de entrada. sí, en mi base de datos, configuré este campo en varchar (2000) y la intercalación es utf8mb4. Ok, buscaré verificar la longitud del texto de entrada con javascript en su lugar ... y luego reemplazaré el {1,2000} por un * como usted dice. Muchísimas gracias
 – 
Sarah
3 ene. 2017 a las 02:39
He puesto una longitud máxima de 2000 (en el campo del formulario html en lugar de usar javascript). maxlength = "2000" De esta manera, si el usuario ingresa, por ejemplo, 1000 emojis, no permitirá más entradas (ya que cuentan como dos caracteres) y así no tengo que preocuparme de que el texto exceda el espacio de almacenamiento de 2000 caracteres. ¿Crees que esto suena como una buena solución?
 – 
Sarah
3 ene. 2017 a las 13:45