sscanf en Python

Estoy buscando un equivalente a sscanf() en Python. Quiero analizar los archivos /proc/net/* , 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 en su conjunto:

 >>> lines = open("/proc/net/dev").readlines() >>> for l in lines[2:]: >>> cols = l.split(string.whitespace + ":") >>> print len(cols) 1 

Que debería estar devolviendo 17, como se explicó anteriormente.

¿Hay 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?

Python no tiene un sscanf equivalente incorporado, y la mayoría de las veces realmente tiene mucho más sentido analizar la entrada trabajando directamente con la cadena, usando expresiones regulares o usando una herramienta de análisis.

Probablemente, en su mayoría útiles para traducir C, la gente ha 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 base a múltiples caracteres divididos, re.split es realmente la herramienta correcta.

También está el módulo de parse .

parse() está diseñado para ser lo contrario de format() (la función de formato de cadena más nueva en Python 2.6 y superior).

 >>> from parse import parse >>> parse('{} fish', '1') >>> parse('{} fish', '1 fish')  >>> parse('{} fish', '2 fish')  >>> parse('{} fish', 'red fish')  >>> parse('{} fish', 'blue fish')  

Cuando estoy en un estado de ánimo C, por lo general uso zip y listas de comprensión para el comportamiento similar a 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 utilicé algo como:

 strtobool = lambda s: {'true': True, 'false': False}[s] 

Puede dividir en un rango de caracteres utilizando el módulo re .

 >>> import re >>> r = re.compile('[ \t\n\r:]+') >>> r.split("abc:def ghi") ['abc', 'def', 'ghi'] 

Puede analizar con el módulo re utilizando grupos con nombre . No analizará las subcadenas a sus tipos de datos reales (por ejemplo, int ), pero es muy conveniente al 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 su ejemplo de sscanf con la variable podría ser:

 import re hex_digit_pattern = r"[\dA-Fa-f]" pat = r"\d+: " + \ r"(?PHEX+):(?PHEX+) " + \ r"(?PHEX+):(?PHEX+) " + \ r"HEX+ HEX+:HEX+ HEX+:HEX+ HEX+ +\d+ +\d+ " + \ r"(?P\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'} 

Hay una receta de ActiveState que implementa un análisis básico http://code.activestate.com/recipes/502213-simple-scanf-implementation/

puede girar el “:” al espacio y hacer la división.eg

 >>> f=open("/proc/net/dev") >>> for line in f: ... line=line.replace(":"," ").split() ... print len(line) 

no se necesita regex (para este caso)

Respuesta de orip upvoted. Creo que es un buen consejo usar re modulo. La aplicación Kodos es útil cuando se acerca a una tarea de expresión regular compleja con Python.

http://kodos.sourceforge.net/home.html

Actualización: la documentación de Python para su módulo de expresiones regulares incluye una sección sobre simulación de scanf, que me pareció más útil que cualquiera de las respuestas anteriores.

https://docs.python.org/2/library/re.html#simulating-scanf

Si los separadores son ‘:’, puede dividir en ‘:’ y luego usar x.strip () en las cadenas para deshacerse de cualquier espacio en blanco inicial o final. int () ignorará los espacios.

Hay una implementación de Python 2 por odiak .