2010-10-15 9 views
1

J'ai la chaîne suivante:Regex en Python

schema(field1, field2, field3, field4 ... fieldn) 

je dois transformer la chaîne à un objet avec un attribut de nom schema et les noms des champs comme un autre attribut qui est une liste.

Comment faire cela en Python avec une expression régulière?

Répondre

1

expressions régulières pour des choses comme ça ont probablement besoin tests:

import unittest 

import re 

# Verbose regular expression! http://docs.python.org/library/re.html#re.X 
p = r""" 

(?P<name>[^(]+)   # Match the pre-open-paren name. 
\(      # Open paren 
(?P<fields>    # Comma-separated fields 
    (?: 
     [a-zA-Z0-9_-]+ 
     (?:,\)   # Subsequent fields must separated by space and comma 
    )* 
    [a-zA-Z0-9_-]+  # At least one field. No trailing comma or space allowed. 
) 

\)      # Close-paren 
""" 

# Compiled for speed! 
cp = re.compile(p, re.VERBOSE) 

class Foo(object): 
    pass 


def validateAndBuild(s): 
    """Validate a string and return a built object. 
    """ 
    match = cp.search(s) 
    if match is None: 
     raise ValueError('Bad schema: %s' % s) 

    schema = match.groupdict() 
    foo = Foo() 
    foo.name = schema['name'] 
    foo.fields = schema['fields'].split(', ') 

    return foo 



class ValidationTest(unittest.TestCase): 
    def testValidString(self): 
     s = "schema(field1, field2, field3, field4, fieldn)" 

     obj = validateAndBuild(s) 

     self.assertEqual(obj.name, 'schema') 

     self.assertEqual(obj.fields, ['field1', 'field2', 'field3', 'field4', 'fieldn']) 

    invalid = [ 
     'schema field1 field2', 
     'schema(field1', 
     'schema(field1 field2)', 
     ] 

    def testInvalidString(self): 
     for s in self.invalid: 
      self.assertRaises(ValueError, validateAndBuild, s) 


if __name__ == '__main__': 
    unittest.main() 
+1

comment est-ce différent de ma réponse? sauf avoir tout le code de test redondant et une regex laide? – SilentGhost

+0

@David, comment changer l'expression rationnelle pour rendre l'espace entre les champs facultatif? –

+0

Sur la ligne 13, changez «\)' en '\?)'. Cela rend l'espace échappé facultatif. (Voir la section "Quantificateurs" à .) –

5

Vous cherchez quelque chose comme ça?

>>> s = 'schema(field1, field2, field3, field4, field5)' 
>>> name, _, fields = s[:-1].partition('(') 
>>> fields = fields.split(', ') 
>>> if not all(re.match(r'[a-z]+\d+$', i) for i in fields): 
    print('bad input') 

>>> sch = type(name, (object,), {'attr': fields}) 
>>> sch 
<class '__main__.schema'> 
>>> sch.attr 
['field1', 'field2', 'field3', 'field4', 'field5'] 
+1

Merci, mais je suis à la recherche d'une solution qui, dans le processus, me permet également de valider que la chaîne est dans le format spécifié ci-dessus. –

+0

Je me demandais juste, avez-vous une raison spécifique pour utiliser 'partition()' au lieu de 'split (..., 1)' ou préférez-vous simplement? De toute façon, +1 :) – Wolph

+1

@Yasmin: qui est? – SilentGhost

0

Vous pouvez utiliser quelque chose comme (en deux tours parce que python re ne prend pas en charge la capture imbriquée (grâce SilentGhost pour pointer dehors)):

pattern = re.compile("^([a-z]+)\(([a-z,]*)\)$") 

ret = pattern.match(s) 

if ret==None: 
    ... 
else: 
    f = ret.groups() 
    name = f[0] 
    args = f[1] 

    arg_pattern = re.compile("^([a-z]+)(,[a-z]+)*$") 

    ret2 = arg_pattern.match(args) 

    # same checking as above 
    if (ret2==None): 
     ... 
    else: 
     args_f = ret2.groups() 
+1

cela fonctionne seulement avec deux arguments, Python re ne supporte pas les captures imbriquées – SilentGhost

+0

Cela marche-t-il pour les champs> 2? J'ai essayé avec quatre champs et les champs d'impression imprime le schéma, le premier et le dernier. Erreur? –

+1

Oui en effet (voir SilentGhost). J'essaye de résoudre cela ... – ThR37