Je cherche un équivalent à sscanf()
en Python. Je veux analyser /proc/net/*
fichiers, en C je pourrais faire quelque chose comme ça:
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);
J'ai d'abord pensé à utiliser str.split
, mais il ne se divise pas sur les caractères donnés, mais la chaîne sep
dans son ensemble:
>>> lines = open("/proc/net/dev").readlines()
>>> for l in lines[2:]:
>>> cols = l.split(string.whitespace + ":")
>>> print len(cols)
1
Qui devrait retourner 17, comme expliqué ci-dessus.
Existe-t-il un Python équivalent à sscanf
(pas RE), ou une fonction de fractionnement de chaîne dans la bibliothèque standard qui se divise sur une plage de caractères que je ne connais pas de?
Python n'a pas d'équivalent sscanf
intégré, et la plupart du temps, il est en fait beaucoup plus logique d'analyser l'entrée en travaillant directement avec la chaîne, en utilisant des expressions régulières ou en utilisant un outil d'analyse.
Probablement surtout utile pour traduire C, les gens ont implémenté sscanf
, comme dans ce module: http://hkn.eecs.berkeley.edu/~dyoo/python/scanf/
Dans ce cas particulier, si vous souhaitez simplement fractionner les données en fonction de plusieurs caractères fractionnés, re.split
est vraiment le bon outil.
Il existe également le module parse
.
parse()
est conçu pour être l'opposé de format()
(la nouvelle fonction de formatage de chaîne dans Python 2.6 et plus)).
>>> 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',) {}>
Lorsque je suis d'humeur C, j'utilise généralement des compréhensions Zip et List pour un comportement de type scanf. Comme ça:
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)
Notez que pour les chaînes de format plus complexes, vous devez utiliser des expressions régulières:
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)
Notez également que vous avez besoin de fonctions de conversion pour tous les types que vous souhaitez convertir. Par exemple, ci-dessus, j'ai utilisé quelque chose comme:
strtobool = lambda s: {'true': True, 'false': False}[s]
Vous pouvez diviser une plage de caractères en utilisant le module re
.
>>> import re
>>> r = re.compile('[ \t\n\r:]+')
>>> r.split("abc:def ghi")
['abc', 'def', 'ghi']
Vous pouvez analyser avec le module re
en utilisant groupes nommés . Il n'analysera pas les sous-chaînes à leurs types de données réels (par exemple int
) mais c'est très pratique lors de l'analyse des chaînes.
Étant donné cet exemple de ligne 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 exemple imitant votre exemple sscanf avec la variable pourrait être:
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'}
Il existe une recette ActiveState qui implémente un scanf de base http://code.activestate.com/recipes/502213-simple-scanf-implementation/
Mise à jour: la documentation Python pour son module regex, re
, comprend une section sur la simulation de scanf, que j'ai trouvée plus utile que n'importe laquelle des réponses ci-dessus.
La réponse de orip a voté. Je pense que c'est un bon conseil d'utiliser le module re. L'application Kodos est utile lors de l'approche d'une tâche d'expression rationnelle complexe avec Python.
vous pouvez transformer le ":" en espace et faire le split.eg
>>> f=open("/proc/net/dev")
>>> for line in f:
... line=line.replace(":"," ").split()
... print len(line)
aucune expression régulière nécessaire (dans ce cas)
Si les séparateurs sont ":", vous pouvez fractionner sur ":", puis utiliser x.strip () sur les chaînes pour supprimer tout espace de début ou de fin. int () ignorera les espaces.
Il y a une implémentation de Python 2 par odiak .