Intentando escribir una expresión regular para que coincida con los patrones de código de barras GS1 (https://en.wikipedia.org/wiki / GS1-128), que contienen 2 o más de estos patrones que tienen un identificador seguido de un cierto número de caracteres de datos.

Necesito algo que coincida con este código de barras porque contiene 2 del identificador y los patrones de datos:

Legible por humanos con los identificadores en parens: (01) 12345678901234 (17) 501200

Datos reales: 011234567890123417501200

Pero no debe coincidir con este código de barras cuando solo hay un patrón en:

Legible por humanos: (01) 12345678901234

Datos reales: 0112345678901234

Parece que lo siguiente debería funcionar:

var regex = /(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6})){2,}/g;
var str = "011234567890123417501200";

console.log(str.replace(regex, "$4"));
// matches 501200
console.log(str.replace(regex, "$1"));
// no match? why?

Por alguna extraña razón, tan pronto como elimino el {2,} funciona, pero necesito el {2,} para que solo devuelva coincidencias si hay más de una coincidencia.

// Remove {2,} and it will return the first match
var regex = /(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6}))/g;
var str = "011234567890123417501200";

console.log(str.replace(regex, "$4"));
// matches 501200
console.log(str.replace(regex, "$1"));
// matches 12345678901234
// but then the problem is it would also match single identifiers such as
var str2 = "0112345678901234";
console.log(str2.replace(regex, "$1"));
 

¿Cómo hago que esto funcione para que solo coincida y extraiga los datos si hay más de 1 conjunto de grupos de coincidencia?

¡Gracias!

7
Uniphonic 17 feb. 2017 a las 03:25

3 respuestas

La mejor respuesta

Su RegEx es lógico y sintácticamente correcto para las expresiones regulares compatibles con Perl (PCRE). El problema que creo que enfrenta es el hecho de que JavaScript tiene problemas con los grupos de captura repetidos. Es por eso que el RegEx funciona bien una vez que saque el {2,}. Al agregar el cuantificador, JavaScript se asegurará de devolver solo la última coincidencia.

Lo que recomendaría es eliminar el cuantificador {2,} y luego buscar coincidencias mediante programación. Sé que no es ideal para aquellos que son grandes fanáticos de RegEx, pero c'est la vie .

Por favor, vea el fragmento a continuación:

var regex = /(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6}))/g;
var str = "011234567890123417501200";

// Check to see if we have at least 2 matches.
var m = str.match(regex);
console.log("Matches list: " + JSON.stringify(m));
if (m.length < 2) {
    console.log("We only received " + m.length + " matches.");
} else {
    console.log("We received " + m.length + " matches.");
    console.log("We have achieved the minimum!");
}

// If we exec the regex, what would we get?
console.log("** Method 1 **");
var n;
while (n = regex.exec(str)) {
    console.log(JSON.stringify(n));
}

// That's not going to work.  Let's try using a second regex.
console.log("** Method 2 **");
var regex2 = /^(\d{2})(\d{6,})$/;
var arr = [];
var obj = {};
for (var i = 0, len = m.length; i < len; i++) {
    arr = m[i].match(regex2);
    obj[arr[1]] = arr[2];
}

console.log(JSON.stringify(obj));

// EOF

Espero que esto ayude.

2
Damian T. 17 feb. 2017 a las 19:03

Actualizar

1er ejemplo

Ejemplo con $ 1 $ 2 $ 3 $ 4 no sé por qué en matriz :)

Pero ves $ 1 -> abc $ 2 -> def $ 3 -> ghi $ 4 -> jkl

//              $1   $2     $3  $4
var regex = /(abc)|(def)|(ghi)|(jkl)/g;
var str = "abcdefghijkl";

// test  
console.log(str.replace(regex, "$1 1st "));
console.log(str.replace(regex, "$2 2nd "));
console.log(str.replace(regex, "$3 3rd "));
console.log(str.replace(regex, "$4 4th "));

2do ejemplo

Algo aquí está mezclando defectuosa

//              $1   $2     $3  $4
var regex = /((abc)|(def)|(ghi)|(jkl)){2,}/g;
var str = "abcdefghijkl";

// test  
console.log(str.replace(regex, "$1 1st "));
console.log(str.replace(regex, "$2 2nd "));
console.log(str.replace(regex, "$3 3rd "));
console.log(str.replace(regex, "$4 4th "));

Como puede ver, hay ($4)( )( )( ) en lugar de ($1)( )( )( ).

Si pienso correctamente, el problema es con corchetes externos () confuso 'pseudo' $ 1 es $ 4. Si tiene entre corchetes externos () un patrón y luego {2,}, entonces entre corchetes externos () es $ 4 pero en el subpatrón hay (?:01(\d{14})) pero se lee como no $ 1 pero defectuoso en este caso $ 4. Tal vez esto cause conflictos entre los valores recordados entre corchetes externos () y los primeros valores recordados pero entre corchetes (esto es $ 1) . Por eso no se muestra. En otras palabras, tiene ($ 4 ($ 1 $ 2 $ 3 $ 4)) y esto no es correcto.

Añado la imagen para mostrar lo que quiero decir.

enter image description here

Como dijo @Damian

Al agregar el cuantificador, JavaScript se asegurará de devolver solo la última coincidencia.

Entonces $ 4 es el último partido.

Actualización final

Agregué una pequeña prueba útil

var regex = /(?:01(\d{14})|10(\x1D{6,20})|11(\d{6})|17(\d{6})){2,}/g;
var str = "011234567890123417501200";

// test
console.log(str.replace(regex, "$1 1st "));
console.log(str.replace(regex, "$2 2nd "));
console.log(str.replace(regex, "$3 3rd "));
console.log(str.replace(regex, "$4 4th "));
0
grzesiekmq 17 feb. 2017 a las 22:54

La razón es que los grupos de captura solo dan la última coincidencia por ese grupo en particular. Imagine que tendría dos códigos de barras en su secuencia que tienen el mismo identificador 01 ... ahora queda claro que $1 no puede referirse a ambos al mismo tiempo. El grupo de captura solo retiene la segunda aparición.

Una forma sencilla, pero no tan elegante, es soltar {2,} y, en su lugar, repetir todo el patrón de expresión regular para que coincida con la segunda secuencia de código de barras. Creo que también debe usar ^ (inicio del ancla de cadena) para asegurarse de que la coincidencia esté al comienzo de la cadena, de lo contrario, podría recoger un identificador a mitad de camino de una secuencia no válida. Después del patrón de expresión regular repetido, también debe agregar .* si desea ignorar todo lo que sigue después de la segunda secuencia, y no tener que volver a usted cuando use replace.

Finalmente, como no sabe qué identificador se encontrará para la primera y la segunda coincidencia, debe reproducir $1$2$3$4 en su replace, sabiendo que solo uno de esos cuatro no estará vacío cuerda. Lo mismo para la segunda partida: $5$6$7$8.

Aquí está el código mejorado aplicado a su cadena de ejemplo:

var regex = /^(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6}))(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6})).*/;

var str = "011234567890123417501200";
console.log(str.replace(regex, "$1$2$3$4")); // 12345678901234
console.log(str.replace(regex, "$5$6$7$8")); // 501200

Si necesita hacer coincidir también los códigos de barras que siguen al segundo, no puede escapar de escribir un bucle. No puede hacerlo con solo una expresión regular basada en replace.

Con un bucle

Si se permite un bucle, puede usar el método regex#exec. Luego sugeriría agregar en su expresión regular una especie de "capturar todo", que coincidirá con un carácter si ninguno de los otros identificadores coincide. Si en el bucle detecta una coincidencia de "capturar todo", saldrá:

var str = "011234567890123417501200";
var regex = /(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6})|(.))/g;
//              1: ^^^^^^  2: ^^^^^^^^^^^^^  3: ^^^^^  4: ^^^^^ 5:^ (=failure)
var result = [], grp;
while ((grp = regex.exec(str)) && !grp[5]) result.push(grp.slice(1).join(''));

// Consider it a failure when not at least 2 matched.
if (result.length < 2) result = [];
console.log(result);
1
trincot 18 feb. 2017 a las 08:41