sscanf en Python
parsing split (11)
Estoy buscando un equivalente a sscanf()
en Python. Quiero analizar /proc/net/*
archivos, en CI podría hacer algo como esto:
int matches = sscanf(
buffer,
"%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %*X %*X:%*X %*X:%*X %*X %*d %*d %ld %*512s/n",
local_addr, &local_port, rem_addr, &rem_port, &inode);
Al principio pensé en usar str.split
, sin embargo, no se divide en los caracteres dados, sino en la cadena sep
como un todo:
>>> lines = open("/proc/net/dev").readlines()
>>> for l in lines[2:]:
>>> cols = l.split(string.whitespace + ":")
>>> print len(cols)
1
Que debería devolver 17, como se explicó anteriormente.
¿Existe un Python equivalente a sscanf
(no RE), o una función de división de cadenas en la biblioteca estándar que se divide en cualquiera de un rango de caracteres que no conozco?
Actualización: la documentación de Python para su módulo regex, re
, incluye una sección sobre simulación de escaneo, que encontré más útil que cualquiera de las respuestas anteriores.
Cuando estoy de humor C, generalmente uso compresión y lista de comprensiones para un comportamiento similar al scanf. Me gusta esto:
input = ''1 3.0 false hello''
(a, b, c, d) = [t(s) for t,s in zip((int,float,strtobool,str),input.split())]
print (a, b, c, d)
Tenga en cuenta que para cadenas de formato más complejas, necesita usar expresiones regulares:
import re
input = ''1:3.0 false,hello''
(a, b, c, d) = [t(s) for t,s in zip((int,float,strtobool,str),re.search(''^(/d+):([/d.]+) (/w+),(/w+)$'',input).groups())]
print (a, b, c, d)
Tenga en cuenta también que necesita funciones de conversión para todos los tipos que desee convertir. Por ejemplo, arriba he usado algo como:
strtobool = lambda s: {''true'': True, ''false'': False}[s]
Hay una implementación de Python 2 por odiak .
Hay una receta de ActiveState que implementa un scanf básico http://code.activestate.com/recipes/502213-simple-scanf-implementation/
Puede analizar el módulo usando grupos nombrados . No analizará las subcadenas a sus tipos de datos reales (por ejemplo, int
) pero es muy conveniente para analizar cadenas.
Dada esta línea de muestra de /proc/net/tcp
:
line=" 0: 00000000:0203 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 335 1 c1674320 300 0 0 0"
Un ejemplo que imita tu ejemplo de sscanf con la variable podría ser:
import re
hex_digit_pattern = r"[/dA-Fa-f]"
pat = r"/d+: " + /
r"(?P<local_addr>HEX+):(?P<local_port>HEX+) " + /
r"(?P<rem_addr>HEX+):(?P<rem_port>HEX+) " + /
r"HEX+ HEX+:HEX+ HEX+:HEX+ HEX+ +/d+ +/d+ " + /
r"(?P<inode>/d+)"
pat = pat.replace("HEX", hex_digit_pattern)
values = re.search(pat, line).groupdict()
import pprint; pprint values
# prints:
# {''inode'': ''335'',
# ''local_addr'': ''00000000'',
# ''local_port'': ''0203'',
# ''rem_addr'': ''00000000'',
# ''rem_port'': ''0000''}
Puede dividir en un rango de caracteres usando el módulo re
.
>>> import re
>>> r = re.compile(''[ /t/n/r:]+'')
>>> r.split("abc:def ghi")
[''abc'', ''def'', ''ghi'']
Python no tiene un equivalente de sscanf
incorporado, y la mayoría de las veces realmente tiene mucho más sentido analizar la entrada al trabajar directamente con la cadena, usar expresiones regulares o usar una herramienta de análisis sintáctico.
Probablemente más útil para traducir C, las personas han implementado sscanf
, como en este módulo: http://hkn.eecs.berkeley.edu/~dyoo/python/scanf/
En este caso particular, si solo desea dividir los datos en función de múltiples caracteres divididos, re.split
es realmente la herramienta adecuada.
Respuesta de orip vuelta arriba. Creo que es un buen consejo usar el módulo Re. La aplicación Kodos es útil al abordar una tarea de expresiones regulares complejas con Python.
Si los separadores son '':'', puede dividir en '':'', y luego usar x.strip () en las cadenas para deshacerse de cualquier espacio en blanco inicial o posterior. int () ignorará los espacios.
También está el módulo de parse
.
parse()
está diseñado para ser lo contrario de format()
(la función de formateo de cadena más nueva en Python 2.6 y superior).
>>> from parse import parse
>>> parse(''{} fish'', ''1'')
>>> parse(''{} fish'', ''1 fish'')
<Result (''1'',) {}>
>>> parse(''{} fish'', ''2 fish'')
<Result (''2'',) {}>
>>> parse(''{} fish'', ''red fish'')
<Result (''red'',) {}>
>>> parse(''{} fish'', ''blue fish'')
<Result (''blue'',) {}>
puede girar el ":" al espacio, y hacer el split.eg
>>> f=open("/proc/net/dev")
>>> for line in f:
... line=line.replace(":"," ").split()
... print len(line)
no se necesitan expresiones regulares (para este caso)