Regex para hacer coincidir la clave en YAML

Tengo un yaml que se parece a esto ..! El usuario puede definir el número N de xyz_flovor_id donde la clave _flovor_id será común. El objective es agarrar la clave *_flavor_id y extraer el valor de ella.

  server: tenant: "admin" availability_zone: "nova" cpu_overcommit_ratio: 1:1 memory_overcommit_ratio: 1:1 xyz_flovor_id: 1 abc_flavor_id: 2 

Soy capaz de calcular la expresión regular para que coincida con _flovor_id . Sin embargo, al intentar usar esto en el código, está lanzando un error. Aquí está mi código.

 def get_flavor_keys(params): pattern = re.compile(r'[^*]flavor_id') for key, value in params.iteritems(): print value if key == 'server': if pattern.match(value): print 'test' 

print value es volcar todo el archivo YAML (esperado). Seguimiento inmediato después de eso.

 Traceback (most recent call last): File "resource_meter.py", line 150, in  get_flavor_keys(items) File "resource_meter.py", line 15, in get_flavor_keys if pattern.match(value): TypeError: expected string or buffer 

Usted necesita este regex. Lo agrupé en par clave-valor:

 ^\s*(?P\w+_flavor_id):\s*(?P\d+) 

Demostración de Python: https://repl.it/Lk5W/0

 import re regex = r"^\s*(?P\w+_flavor_id):\s*(?P\d+)" test_str = (" server:\n" " tenant: \"admin\"\n" " availability_zone: \"nova\"\n" " cpu_overcommit_ratio: 1:1\n" " memory_overcommit_ratio: 1:1\n" " xyz_flavor_id: 1\n" " abc_flavor_id: 2\n") matches = re.finditer(regex, test_str, re.MULTILINE) for matchNum, match in enumerate(matches): print ("{key}:{value}".format(key = match.group('key'), value=match.group('value'))) 

Puedes usar esta expresión regular:

\b[^_\n]+_flavor_id:\s*(\d+)

Haga clic para ver la demostración

Explicación de expresiones regulares:

  • \b – límite de palabra
  • [^_\n]+ – 1+ ocurrencias de cualquier carácter que no sea _ ni un carácter de nueva línea
  • _flavor_id: – coincide con _flavor_id: literalmente
  • \s* – coincide con 0+ apariciones de un carácter de espacio en blanco
  • (\d+) – coincide y captura los dígitos 1+. Este es el valor que necesitabas.

No estoy bien versado en python pero regex101 nos permite generar el código. Por lo tanto, estoy pegando el código que puedes usar aquí.

 import re regex = r"\b[^_\n]+_flavor_id:\s*(\d+)" test_str = ("server:\n" " tenant: \"admin\"\n" " availability_zone: \"nova\"\n" " cpu_overcommit_ratio: 1:1\n" " memory_overcommit_ratio: 1:1\n" " xyz_flavor_id: 1\n" " abc_flavor_id: 2") matches = re.finditer(regex, test_str) for matchNum, match in enumerate(matches): matchNum = matchNum + 1 print ("Match {matchNum} was found at {start}-{end}: {match}".format(matchNum = matchNum, start = match.start(), end = match.end(), match = match.group())) for groupNum in range(0, len(match.groups())): groupNum = groupNum + 1 print ("Group {groupNum} found at {start}-{end}: {group}".format(groupNum = groupNum, start = match.start(groupNum), end = match.end(groupNum), group = match.group(groupNum))) 

Esta es la salida que tengo: introduzca la descripción de la imagen aquí

Obtienes ese error, porque el valor para el server claves no es una cadena, sino un dict (o una subclase de dict). Así es como se carga la asignación YAML en su entrada, que incluye la clave abc_flavor_id .

Aparte de eso, siempre es una mala idea usar expresiones regulares para analizar YAML (o cualquier otro formato de texto estructurado como HTML, XML, CVS), ya que es difícil, si no imposible, capturar todos los matices de la gramática. Si no fuera así no necesitarías un analizador.

Por ejemplo, un pequeño cambio en el archivo, simplemente agregando un comentario sobre qué valor debe actualizarse para que algún usuario edite el archivo, rompe los enfoques simplistas de expresiones regulares:

 server: tenant: "admin" availability_zone: "nova" cpu_overcommit_ratio: 1:1 memory_overcommit_ratio: 1:1 xyz_flovor_id: 1 abc_flavor_id: # extract the value for this key 2 

Esta documentación de YAML anterior es semánticamente idéntica a la suya, pero ya no funcionará con las otras respuestas publicadas actualmente.

Si alguna operación de carga / guardado de YAML transforma su entrada en (nuevamente semánticamente equivalente):

 server: {abc_flavor_id: 2, availability_zone: nova, cpu_overcommit_ratio: 61, memory_overcommit_ratio: 61, tenant: admin, xyz_flovor_id: 1} then tweaking a dumb regular expression will not begin to suffice (this is not a construed example, this is the default way to dump your data structure in PyYAML and in ruamel.yaml using 'safe'-mode). 

Lo que debe hacer es que la expresión regular coincida con las claves del valor asociado con el server , no con todo el documento:

 import re import sys from ruamel.yaml import YAML yaml_str = """\ server: tenant: "admin" availability_zone: "nova" cpu_overcommit_ratio: 1:1 memory_overcommit_ratio: 1:1 xyz_flovor_id: 1 abc_flavor_id: # extract the value for this key 2 """ def get_flavor_keys(params): pattern = re.compile(r'(?P.*)_flavor_id') ret_val = {} for key in params['server']: m = pattern.match(key) if m is not None: ret_val[m.group('key')] = params['server'][key] print('test', m.group('key')) return ret_val yaml = YAML(typ='safe') data = yaml.load(yaml_str) keys = get_flavor_keys(data) print(keys) 

esto te da:

 {'abc': 2} 

(el xyz_flovor_id por supuesto, no coincide, pero quizás sea un error tipográfico en tu publicación).