2010-01-21 14 views
10

Sé que es fácil hacer coincidir cualquier cosa excepto un personaje dado que usa una expresión regular.¿Cómo puedo unir cadenas que no coinciden con un patrón particular en Perl?

$text = "ab ac ad"; 
$text =~ s/[^c]*//g; # Match anything, except c. 

$text is now "c". 

No sé cómo "exceptuar" cadenas en lugar de caracteres. ¿Cómo podría "hacer coincidir algo, excepto 'ac'"? Intentó [^ (ac)] y [^ "ac"] sin éxito.

¿Es posible en absoluto?

+0

Para "cadena simple está (no) contenida en otra cadena", es mejor usar el índice (devuelve -1 si no está contenido). Escribí un analizador de archivos de registro de servidor de correo que primero usó regexp para esas comprobaciones simples, después de cambiar a index() el tiempo de ejecución se redujo a aproximadamente el 25% en comparación con la versión de expresiones regulares. Como índice de bonificación() podría ser más fácil de leer. – dbemerlin

+5

¿Cuál es su objetivo final exactamente? (1) coincide con una cadena que no contiene 'ac' (o alguna otra subcadena), o (2) reemplaza todo en una cadena excepto la subcadena' ac' (por ejemplo, cambia 'abacadac' en' acac')? Sospecho que es el último. –

+0

Para agregar al comentario de @Bart: Proporcione ejemplos de: (1) cadena que desea validar e invalidar O (2) cadena que desea extraer de esta cadena O (3) el resultado de reemplazos en esta cadena. – Kobi

Respuesta

0

Puede modificar fácilmente esta expresión regular para su propósito.

use Test::More 0.88; 

#Match any whole text that does not contain a string 
my $re=qr/^(?:(?!ac).)*$/; 
my $str='ab ac ad'; 

ok(!$str=~$re); 

$str='ab af ad'; 
ok($str=~$re); 

done_testing(); 
+0

Mark Byers: con una modificación simple, funcionaría en cualquier parte de la cadena, es solo un ejemplo. –

+0

Cambié de opinión: esto no funcionará por la misma razón que las otras soluciones que se publicaron aquí no funcionarán: eliminará la c. Intente hacer la 'modificación simple' en una prueba completa y vea qué sucede. –

+0

La pregunta era: "coincide con cualquier cosa, excepto 'ac'". Con la eliminación de^$ en mi expresión regular, coincidiría con cualquier cosa, deteniéndose en 'AC'. –

1

puede utilizar el índice()

$text = "ab ac ad"; 
print "ac not found" if (index($text,"ac") == -1); 
5

A continuación se resuelve la cuestión tal como se entiende en el segundo sentido descrito en Bart K. comentario:

>> $text='ab ac ad'; 
>> $text =~ s/(ac)|./\1/g; 
>> print $text; 
ac 

Además, 'abacadac' ->'acac'

Sin embargo, debe tenerse en cuenta que en la mayoría de las aplicaciones prácticas Las características negativas de búsqueda son más útiles que este enfoque.

+1

¡Creativo! :) (mínimo de 15 caracteres) –

+0

Gracias :) (el mismo límite aquí) –

+0

Pásame :-). Una pequeña mejora podría ser hacer s/(\ bac \ b) /./ \ 1/g que significa 'ac fac ac' -> acac –

2

Si solo quiere comprobar si la cadena no contiene "ac", simplemente use una negación.

$text = "ab ac ad"; 

print "ac not found" if $text !~ /ac/; 

o

print "ac not found" unless $text =~ /ac/; 
2
$text =~ s/[^c]*//g; // Match anything, except c. 

@ssn, Un par de comentarios acerca de su pregunta:

  1. "//" no es un comentario en Perl. Solo "#" es.
  2. "[^ c] *" - no hay necesidad de el "*" allí. "[^ c]" significa la clase de caracteres compuesta por todos los caracteres excepto la letra "c". Luego usa el modificador/g, , lo que significa que todas las ocurrencias en el texto serán reemplazadas (en su ejemplo, con nada). El modificador "cero o más" ("*") es por lo tanto redundante.

¿cómo iba a "coincidir con nada, excepto 'ca'"? Intentó [^ (ac)] y [^ "ac"] sin éxito.

Por favor, lea la documentación sobre las clases de caracteres (Ver "perldoc perlre" en la línea de comandos, o en línea en http://perldoc.perl.org/perlre.html) - verás que establece que para la lista de caracteres dentro de la plaza paréntesis el RE se " unir cualquier personaje de la lista ". El orden de los significados no es relevante y no hay "cadenas", solo una lista de caracteres. "()" y las comillas dobles tampoco tienen un significado especial dentro de los corchetes.

Ahora no estoy exactamente seguro de por qué estás hablando de hacer coincidir, pero da un ejemplo de sustitución.Pero para ver si una cadena no coincide con el "ac" subcadena sólo tiene que anular el partido:

use strict; use warnings; 
my $text = "ab ac ad"; 
if ($text !~ m/ac/) { 
    print "Yey the text doesn't match 'ac'!\n"; # this shouldn't be printed 
} 

Digamos que tiene una cadena de texto en el que están incrustados múltiples ocurrencias de una subcadena. Si lo que desea es el texto que rodea a la sub-cadena, basta con retirar todas las apariciones de la subcadena:

$text =~ s/ac//g; 

Si desea que la inversa - para eliminar todo el texto excepto por todas las apariciones de la subcadena, me sugeriría algo así como:

use strict; use warnings; 
my $text = "ab ac ad ac ae"; 
my $sub_str = "ac"; 
my @captured = $text =~ m/($sub_str)/g; 
my $num = scalar @captured; 
print (($sub_str x $num) . "\n"); 

Esto básicamente cuenta el número de veces que la subcadena aparece en el texto e imprime la sub-cadena que varias veces usando el operador "x". No muy elegante, estoy seguro de que un Perl-guru podría encontrar algo mejor.


@ennuikiller:

my $text = "ab ac ad"; 
$text !~ s/(ac)//g; # Match anything, except ac. 

Esto es incorrecto, ya que genera una advertencia ("uso inútil de patrón negativo vinculante (~) en el contexto vacío!") "Advertencias de uso" y no hacer nada más que quitar todas las subseries "AC" del texto, lo que podría ser escrito más simplemente como he escrito anteriormente, con:

$text =~ s/ac//g; 
2

Actualización: En un comentario sobre su pregunta, mencionó que desea limpiar el marcado de wiki y eliminar las secuencias balanceadas de {{ ... }}. Sección 6 del Perl FAQ cubre lo siguiente: Can I use Perl regular expressions to match balanced text?

Considere el siguiente programa:

#! /usr/bin/perl 

use warnings; 
use strict; 

use Text::Balanced qw/ extract_tagged /; 

# for demo only 
*ARGV = *DATA; 

while (<>) { 
    if (s/^(.+?)(?=\{\{)//) { 
    print $1; 
    my(undef,$after) = extract_tagged $_, "{{" => "}}"; 

    if (defined $after) { 
     $_ = $after; 
     redo; 
    } 
    } 

    print; 
} 

__DATA__ 
Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. {{delete me}} Sed quis 
nulla ut dolor {{me too}} fringilla 
mollis {{ quis {{ ac }} erat. 

Su salida:

Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. Sed quis 
nulla ut dolor fringilla 
mollis {{ quis erat.

Para su ejemplo particular, se puede usar

$text =~ s/[^ac]|a(?!c)|(?<!a)c//g; 

Es decir, solo borre un a o c cuando no sean parte de una secuencia ac.

En general, esto es complicado de hacer con una expresión regular.

Supongamos que no quiere foo seguido de espacios en blanco opcionales y luego bar en $str. A menudo, es más claro y más fácil de verificar por separado. Por ejemplo:

die "invalid string ($str)" 
    if $str =~ /^.*foo\s*bar/; 

Usted también puede estar interesado en an answer to a similar question, donde escribí

my $nofoo = qr/ 
    (  [^f] | 
    f (?! o) | 
    fo (?! o \s* bar) 
)* 
/x; 

my $pattern = qr/^ $nofoo bar /x; 

Para entender la complicación, leído por Mark How Regexes Work Dominus. El motor compila expresiones regulares en máquinas de estado. Cuando llega el momento de combinar, alimenta la cadena de entrada a la máquina de estados y comprueba si la máquina de estados finaliza en un estado de aceptación.Por lo tanto, para excluir una cadena, debe especificar una máquina que acepte todas las entradas excepto una secuencia particular.

Lo que podría ayudar es un interruptor de expresión regular /v que crea la máquina de estado como de costumbre pero luego complementa el bit de estado de aceptación para todos los estados. Es difícil decir si esto realmente sería útil en comparación con los controles separados porque una expresión regular /v aún puede sorprender a las personas, solo que de diferentes maneras.

Si está interesado en los detalles teóricos, vea An Introduction to Formal Languages and Automata por Peter Linz.

Cuestiones relacionadas