Teniendo tantas cosas importantes por hacer en un domingo, no tuve mejor idea que procrastinar con expresiones regulares :grinning_face:
Aclaración preliminar: queda a criterio del lector investigar qué son los “grupos sin captura” y las “búsquedas anticipadas negativas”.
^(?:(?!0)\d{6,8}|(?!0)\d{5,7}A)(?:-\d)?$
^: Inicio de la cadena(?:(?!0)\d{6,8}|(?!0)\d{5,7}A):(?:): Grupo sin captura (non-captouring group): no “memoriza” el texto coincidente; agrupa tokens sin convertirse en un grupo de captura(?!): Búsqueda anticipada negativa (negative lookahead): El grupo no debe coincidir con la expresión dada(?!0): Evitar que la cadena empiece con “0”\d{6,8}: Dígitos del 0 al 9 (\d es el equivalente de [0-9]), de 6 a 8 veces\|: “o” (tal como el operador “or”)\d{5,7}A: Dígitos del 0 al 9 (\d es el equivalente de [0-9]), de 5 a 7 veces, seguido de una “A” (para contemplar documentos con caracteres alfanuméricos)(?:): Grupo sin captura (non-captouring group): no “memoriza” el texto coincidente; agrupa tokens sin convertirse en un grupo de captura-\d: Un guión, seguido de dígitos del 0 al 9 (\d es el equivalente de [0-9])?: Opcional: Puede o no aparecer$: Fin de la cadena^[1-9]\d{4,6}[\dA-D](?:-\d)?$
Esta versión se me ocurrió en base al regex de este xsd del SIFEN, específicamente del xs:simpleType tRuc.
^: Inicio de la cadena[1-9]: Un dígito del 1 al 9 (para evitar que la cadena empiece con “0”)\d{4,6}: Dígitos del 0 al 9 (\d es el equivalente de [0-9]), de 6 a 8 veces[\dA-D]: Un dígito del 0 al 9 (\d es el equivalente de [0-9]), o la letra A/B/C/D mayúscula(?:): Grupo sin captura (non-captouring group): no “memoriza” el texto coincidente; agrupa tokens sin convertirse en un grupo de captura-\d: Un guión, seguido de dígitos del 0 al 9 (\d es el equivalente de [0-9])?: Opcional: Puede o no aparecer$: Fin de la cadena012345 # no coincide
0123456 # no coincide
01234567 # no coincide
012345678 # no coincide
12345 # no coincide
123456 # coincide
1234657 # coincide
12345678 # coincide
123456789 # no coincide
01234A # no coincide
012345A # no coincide
0123456A # no coincide
01234567A # no coincide
012345678A # no coincide
12345A # coincide
123456A # coincide
1234567A # coincide
12345678A # no coincide
123456789A # no coincide
12345- # no coincide
123456- # no coincide
12345678- # no coincide
123456789- # no coincide
12345A- # no coincide
123456A- # no coincide
1234567A- # no coincide
12345678A- # no coincide
123456789A- # no coincide
123456-0 # coincide
1234567-0 # coincide
12345678-0 # coincide
123456789-0 # no coincide
12345A-0 # coincide
123456A-0 # coincide
1234567A-0 # coincide
12345678A-0 # no coincide
123456789A-0 # no coincide
Si por algún motivo sí se necesita trabajar con los grupos de captura (ej.: para calcular el dígito verificador del documento sin guión), las versiones con grupos de captura serían las siguientes:
^((?!0)\d{6,8}|(?!0)\d{5,7}A)(-\d)?$
^[1-9]\d{4,6}[\dA-D](-\d)?$
Regex no es la panacea para validar cadenas. Ya decían por ahí algo así como lo siguiente:
No está demás mencionar que pueden contactarme si tienen sugerencias de corrección para los regex. En la sección inicial del sitio se encuentran los enlaces de contacto :OK_hand:
Fueron testeados en Javascript. Si funcionan para otros lenguajes, me avisan también :folded_hands:
Por último, dejé esta respuesta en StackOverflow ya que la respuesta aceptada no me convencía del todo (?