2010-07-22 20 views
8

Dada una cadena de identificadores separados por :, ¿es posible construir una expresión regular para extraer los identificadores únicos en otra cadena, también separada por :?¿Qué expresión regular puede eliminar elementos duplicados de una cadena?

¿Cómo es posible lograr esto usando una expresión regular? He intentado s/(:[^:])(.*)\1/$1$2/g sin suerte, porque el (.*) es codicioso y se salta al último partido de $1.

Ejemplo: a:b:c:d:c:c:x:c:c:e:e:f debe dar a:b:c:d:x:e:f

Nota: estoy de codificación en Perl, pero yo agradecería mucho el uso de una expresión regular para esto.

+1

podría por favor mostrar un ejemplo de lo que está buscando, no lo entiendo del todo. – Anders

Respuesta

8

En .NET que soporta la repetición infinita de búsqueda hacia atrás en el interior, se podría buscar

(?<=\b\1:.*)\b(\w+):? 

y reemplazar todas las coincidencias con la cadena vacía.

Perl (al menos Perl 5) sólo admite lookbehinds de longitud fija, por lo que puede probar lo siguiente (utilizando búsqueda hacia delante, con un resultado ligeramente diferente):

\b(\w+):(?=.*\b\1:?) 

Si reemplaza que con la cadena vacía , todas anteriores se eliminarán las repeticiones de una entrada duplicada; el último se mantendrá. Así que en lugar de

a:b:c:d:x:e:f 

que se obtendría

a:b:d:x:c:e:f 

Si eso está bien, puede utilizar

$subject =~ s/\b(\w+):(?=.*\b\1:?)//g; 

Explicación:

primera expresión regular:

(?<=\b\1:.*): compruebe si puede hacer coincidir los contenidos de la referencia no. 1, seguido de dos puntos, en algún lugar antes de la cadena.

\b(\w+):?: Coincide con un identificador (de un límite de palabras al siguiente :), seguido opcionalmente por dos puntos.

Segunda regex:

\b(\w+):: partido de un identificador y un colon.

(?=.*\b\1:?): A continuación, compruebe si puede hacer coincidir el mismo identificador, opcionalmente seguido de dos puntos, en algún lugar adelante de la cadena.

+0

El orden de salida es irrelevante para mí, por lo que no lo mencioné en la pregunta (tal vez debería haber mencionado que era irrelevante :). Gracias, funcionó como un encanto! – Tom

+0

Actualice su respuesta, la solución que proporcionó solo funcionó si las palabras tenían un carácter. Olvidé mencionar eso también. Una mejor respuesta sería 's/\ b (\ w +): (? =. * \ 1:?) // g' – Tom

+0

@Tom: Excelente punto. He actualizado mi respuesta. La afirmación de límite de palabras también es necesaria frente a la retro-referencia. –

0

Si los identificadores están ordenados, puede hacerlo usando lookahead/lookbehind. Si no lo son, entonces esto está más allá del poder computacional de una expresión regular. Ahora, solo porque es imposible con expresiones regulares formales no significa que sea imposible si utiliza alguna característica específica de expresiones regulares de Perl, pero si desea mantener sus expresiones regulares en formato portátil, debe describir esta cadena en un lenguaje que admita variables.

+0

La ordenación no es relevante, consulte mi solución. –

+0

¿Qué quiere decir con características específicas de Perl? Los grupos de captura, las referencias retrospectivas, los límites de palabras y los lookaheads son ampliamente compatibles. De las características que se utilizan en esta discusión, la única que llamaría no portátil es miradas atrás, especialmente miradas sin límites. –

+0

@Tim: Diría que es relevante en el sentido de que, si los identificadores se clasificaran, la eliminación de duplicados sería trivial: 's/(\ w +) (: \ 1) + (? =: | $)/$ 1/g' –

1
$str = q!a:b:c:d:c:c:x:c:c:e:e:f!; 

1 while($str =~ s/(:[^:]+)(.*?)\1/$1$2/g); 

say $str 

de salida:

a:b:c:d:x:e:f 
+0

+1 para vacío while loop, aunque creo que una solución más completa podría ser: 'while {$ str = ~ s/(: [^:] + | [^:] +:) (. *) \ 1 (. *)/$ 1 $ 2 $ 3/g} 'para verificar la primera letra. – NorthGuard

0

aquí hay una versión awk, no hay necesidad de expresiones regulares.

$ echo "a:b:c:d:c:c:x:c:c:e:e:f" | awk -F":" '{for(i=1;i<=NF;i++)if($i in a){continue}else{a[$i];printf $i}}' 
abcdxef 

divida los campos en ":", recorra los campos divididos, almacene los elementos en una matriz. verificar existencia y si existe, omitir. De lo contrario, imprimirlos. Puedes traducir esto fácilmente en el código Perl.

Cuestiones relacionadas