Pregunta

Estoy intentando escribir una expresión regular para que coincida con la sintaxis y split una variable personalizada en C #. La idea aquí es un formato personalizado de valores de cadena muy similar a la String.Format / {0} .NET estilo de formato de cadenas.

Por ejemplo el usuario podría definir un formato de cadena para ser evaluados en tiempo de ejecución, así:

D:\Path\{LanguageId}\{PersonId}\ 

El valor 'languageId' coincide con un campo de objeto de datos, y su valor actual sustituye.

Las cosas se complican cuando hay una necesidad de pasar argumentos al campo de formato. Por ejemplo:

{LanguageId:English|Spanish|French}

Esto tendría el significado de ejecutar alguna lógica condicional si el valor de 'languageId' era igual a uno de los argumentos.

Por último que necesitaría para apoyar argumentos mapa como este:

{LanguageId:English=>D:\path\english.xml|Spanish=>D:\path\spansih.xml}

A continuación es una enumeración de todos los valores posibles:

Comando ningún argumento : hacer algo especial

{@Date}

Comando solo argumento:

{@Date:yyyy-mm-dd}

No hay discusión:

{LanguageId}

argumento de la Lista única:

{LanguageId:English}

Multi-Argumento lista:

{LanguageId:English|Spanish}

solo argumento de ruta:

{LanguageId:English=>D:\path\english.xml}

Multi-Argumento mapa:

{LanguageId:English=>D:\path\english.xml|Spanish=>D:\path\spansih.xml}

Resumen:. La sintaxis se puede reducir a una tecla con la lista de tipo de parámetro opcional o un mapa (no ambos)

A continuación se muestra la expresión regular que tengo hasta ahora, que tiene algunos problemas, a saber doesnt manejar correctamente todos los espacios, en .NET no me llevo las divisiones que estoy esperando. Por ejemplo, en el primer ejemplo me he vuelto una única partida de '{} {languageId PERSONID}' en lugar de dos partidos distintos. También estoy seguro de que duerma manejar ruta de sistema de archivos, o delimitados, cadenas entre comillas. Cualquier ayuda para mí más de la joroba sería apreciada. O cualquier recomendación.

    private const string RegexMatch = @"
        \{                              # opening curly brace
        [\s]*                           # whitespace before command
        @?                              # command indicator
        (.[^\}\|])+                       # string characters represening command or metadata
        (                               # begin grouping of params
        :                               # required param separater 
        (                               # begin select list param type

        (                               # begin group of list param type
        .+[^\}\|]                       # string of characters for the list item
        (\|.+[^\}\|])*                  # optional multiple list items with separator
        )                               # end select list param type

        |                               # or select map param type

        (                               # begin group of map param type
        .+[^\}\|]=>.+[^\}\|]            # string of characters for map key=>value pair
        (\|.+[^\}\|]=>.+[^\}\|])*       # optional multiple param map items
        )                               # end group map param type

        )                               # end select map param type
        )                               # end grouping of params
        ?                               # allow at most 1 param group
        \s*
        \}                              # closing curly brace
        ";
¿Fue útil?

Solución

Usted está tratando de hacer demasiado con una expresión regular. Le sugiero que romper la tarea en pasos, siendo la primera un partido sencillo en algo que se parece a una variable. Expresiones regulares que podría ser tan simple como:

\{\s*([^{}]+?)\s*\}

Esto ahorra la cadena de toda la variable / comando en el grupo # 1, menos los apoyos y espacios en blanco circundante. Después de que se puede dividir en dos puntos, a continuación, tuberías, entonces "=>" secuencias según sea apropiado. No comprimir toda la complejidad en una expresión regular monstruo; Si alguna vez se las arreglan para la expresión regular escrito, le resultará imposible mantener cuando sus necesidades cambian más adelante.

Y otra cosa: en este momento, que está centrado en conseguir el código para que funcione cuando la entrada es correcta, pero ¿qué pasa cuando los usuarios se equivocan? ¿No te gustaría darles retroalimentación útil? Expresiones regulares chupa en el que; que están estrictamente pasa / no pasa. Expresiones regulares pueden ser increíblemente útil, pero al igual que cualquier otra herramienta, usted tiene que conocer sus limitaciones antes de poder aprovechar todo su poder.

Otros consejos

Es posible que desee echar un vistazo a la aplicación de esta como una máquina Finate-Estado en lugar de una expresión regular, principalmente para puropses velocidad. http://en.wikipedia.org/wiki/Finite-state_machine

Edit: En realidad, para ser precisos, que desea buscar en las máquinas de estados finitos determinista: http : //en.wikipedia.org/wiki/Deterministic_finite-state_machine

Esto realmente debe ser analizada.

Por ejemplo, quería analizar esta usando Regexp::Grammars .

Por favor, disculpe la longitud.

#! /opt/perl/bin/perl
use strict;
use warnings;
use 5.10.1;

use Regexp::Grammars;

my $grammar = qr{
  ^<Path>$

  <objtoken: My::Path>
    <drive=([a-zA-Z])>:\\ <[elements=PathElement]> ** (\\) \\?

  <rule: PathElement>
    (?:
      <MATCH=BlockPathElement>
    |
      <MATCH=SimplePathElement>
    )

  <token: SimplePathElement>
    (?<= \\ ) <MATCH=([^\\]+)>

  <rule: My::BlockPathElement>
    (?<=\\){ \s*
    (?|
      <MATCH=Command>
    |
      <MATCH=Variable>
    )
    \s* }

  <objrule: My::Variable>
    <name=(\w++)> <options=VariableOptionList>?

  <rule: VariableOptionList>
      :
      <[MATCH=VariableOptionItem]> ** ([|])

  <token: VariableOptionItem>
    (?:
      <MATCH=VariableOptionMap>
    |
      <MATCH=( [^{}|]+? )>
    )

  <objrule: My::VariableOptionMap>
    \s*
    <name=(\w++)> => <value=([^{}|]+?)>
    \s*

  <objrule: My::Command>
    @ <name=(\w++)>
    (?:
      : <[arg=CommandArg]> ** ([|])
    )?

  <token: CommandArg>
    <MATCH=([^{}|]+?)> \s*

}x;

Pruebas con:

use YAML;
while( my $line = <> ){
  chomp $line;
  local %/;

  if( $line =~ $grammar ){
    say Dump \%/;
  }else{
    die "Error: $line\n";
  }
}

Con datos de ejemplo:

D:\Path\{LanguageId}\{PersonId}
E:\{ LanguageId : English | Spanish | French }
F:\Some Thing\{ LanguageId : English => D:\path\english.xml | Spanish => D:\path\spanish.xml }
C:\{@command}
c:\{@command :arg}
c:\{ @command : arg1 | arg2 }

Los resultados en:

---
'': 'D:\Path\{LanguageId}\{PersonId}'
Path: !!perl/hash:My::Path
  '': 'D:\Path\{LanguageId}\{PersonId}'
  drive: D
  elements:
    - Path
    - !!perl/hash:My::Variable
      '': LanguageId
      name: LanguageId
    - !!perl/hash:My::Variable
      '': PersonId
      name: PersonId

---
'': 'E:\{ LanguageId : English | Spanish | French }'
Path: !!perl/hash:My::Path
  '': 'E:\{ LanguageId : English | Spanish | French }'
  drive: E
  elements:
    - !!perl/hash:My::Variable
      '': 'LanguageId : English | Spanish | French'
      name: LanguageId
      options:
        - English
        - Spanish
        - French

---
'': 'F:\Some Thing\{ LanguageId : English => D:\path\english.xml | Spanish => D:\path\spanish.xml }'
Path: !!perl/hash:My::Path
  '': 'F:\Some Thing\{ LanguageId : English => D:\path\english.xml | Spanish => D:\path\spanish.xml }'
  drive: F
  elements:
    - Some Thing
    - !!perl/hash:My::Variable
      '': 'LanguageId : English => D:\path\english.xml | Spanish => D:\path\spanish.xml '
      name: LanguageId
      options:
        - !!perl/hash:My::VariableOptionMap
          '': 'English => D:\path\english.xml '
          name: English
          value: D:\path\english.xml
        - !!perl/hash:My::VariableOptionMap
          '': 'Spanish => D:\path\spanish.xml '
          name: Spanish
          value: D:\path\spanish.xml

---
'': 'C:\{@command}'
Path: !!perl/hash:My::Path
  '': 'C:\{@command}'
  drive: C
  elements:
    - !!perl/hash:My::Command
      '': '@command'
      name: command

---
'': 'c:\{@command :arg}'
Path: !!perl/hash:My::Path
  '': 'c:\{@command :arg}'
  drive: c
  elements:
    - !!perl/hash:My::Command
      '': '@command :arg'
      arg:
        - arg
      name: command

---
'': 'c:\{ @command : arg1 | arg2 }'
Path: !!perl/hash:My::Path
  '': 'c:\{ @command : arg1 | arg2 }'
  drive: c
  elements:
    - !!perl/hash:My::Command
      '': '@command : arg1 | arg2 '
      arg:
        - arg1
        - arg2
      name: command

Programa de ejemplo:

my %ARGS = qw'
  LanguageId  English
  PersonId    someone
';

while( my $line = <> ){
  chomp $line;
  local %/;

  if( $line =~ $grammar ){
    say $/{Path}->fill( %ARGS );
  }else{
    say 'Error: ', $line;
  }
}

{
  package My::Path;

  sub fill{
    my($self,%args) = @_;

    my $out = $self->{drive}.':';

    for my $element ( @{ $self->{elements} } ){
      if( ref $element ){
        $out .= '\\' . $element->fill(%args);
      }else{
        $out .= "\\$element";
      }
    }

    return $out;
  }
}
{
  package My::Variable;

  sub fill{
    my($self,%args) = @_;

    my $name = $self->{name};

    if( exists $args{$name} ){
      $self->_fill( $args{$name} );
    }else{
      my $lc_name = lc $name;

      my @possible = grep {
        lc $_ eq $lc_name
      } keys %args;

      die qq'Cannot find argument for variable "$name"\n' unless @possible;
      if( @possible > 1 ){
        my $die = qq'Cannot determine which argument matches "$name" closer:\n';
        for my $possible( @possible ){
          $die .= qq'  "$possible"\n';
        }
        die $die;
      }

      $self->_fill( $args{$possible[1]} );
    }
  }
  sub _fill{
    my($self,$opt) = @_;

    # This is just an example.
    unless( exists $self->{options} ){
      return $opt;
    }

    for my $element ( @{$self->{options}} ){
      if( ref $element ){
        return '['.$element->value.']' if lc $element->name eq lc $opt;
      }elsif( lc $element eq lc $opt ){
        return $opt;
      }
    }

    my $name = $self->{name};
    my $die = qq'Invalid argument "$opt" for "$name" :\n';
    for my $valid ( @{$self->{options}} ){
      $die .= qq'  "$valid"\n';
    }
    die $die;
  }
}
{
  package My::VariableOptionMap;

  sub name{
    my($self) = @_;

    return $self->{name};
  }
}
{
  package My::Command;

  sub fill{
    my($self,%args) = @_;

    return '['.$self->{''}.']';
  }
}
{
  package My::VariableOptionMap;

  sub name{
    my($self) = @_;
    return $self->{name};
  }

  sub value{
    my($self) = @_;
    return $self->{value};
  }
}

Salida utilizando los datos de ejemplo:

D:\Path\English\someone
E:\English
F:\Some Thing\[D:\path\english.xml]
C:\[@command]
c:\[@command :arg]
c:\[@command : arg1 | arg2 ]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top