Sscanf в Python - PullRequest
       91

Sscanf в Python

56 голосов
/ 01 февраля 2010

Я ищу эквивалент sscanf() в Python. Я хочу разобрать /proc/net/* файлы, в C я мог бы сделать что-то вроде этого:

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);

Сначала я подумал использовать str.split, однако он не разбивается на заданные символы, а на строку sep в целом:

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

Который должен возвращать 17, как объяснено выше.

Есть ли в стандартной библиотеке Python, эквивалентный sscanf (не RE), или функция разделения строк в стандартной библиотеке, которая разбивает любой диапазон символов, о которых я не знаю?

Ответы [ 11 ]

60 голосов
/ 12 октября 2012

Существует также модуль parse.

parse() разработан как противоположность format() (более новая функция форматирования строк в Python 2.6 и выше).

>>> 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',) {}>
58 голосов
/ 18 июня 2012

Когда я нахожусь в настроении C, я обычно использую zip и списки для поведения, подобного scanf. Как это:

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)

Обратите внимание, что для более сложных строк формата вам необходимо использовать регулярные выражения:

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)

Обратите внимание, что вам нужны функции преобразования для всех типов, которые вы хотите преобразовать. Например, выше я использовал что-то вроде:

strtobool = lambda s: {'true': True, 'false': False}[s]
28 голосов
/ 01 февраля 2010

Python не имеет встроенного эквивалента sscanf, и в большинстве случаев имеет смысл анализировать входные данные, работая непосредственно со строкой, используя регулярные выражения или инструмент синтаксического анализа.

Вероятно, в основном это полезно для перевода C, люди реализовали sscanf, например, в этом модуле: http://hkn.eecs.berkeley.edu/~dyoo/python/scanf/

В этом конкретном случае, если вы просто хотите разделить данные на основе нескольких разделенных символов, re.split действительно правильный инструмент.

23 голосов
/ 01 февраля 2010

Вы можете разделить диапазон символов с помощью модуля re.

>>> import re
>>> r = re.compile('[ \t\n\r:]+')
>>> r.split("abc:def  ghi")
['abc', 'def', 'ghi']
13 голосов
/ 02 февраля 2010

Вы можете выполнить синтаксический анализ с модулем re, используя именованные группы . Он не будет анализировать подстроки по фактическим типам данных (например, int), но это очень удобно при разборе строк.

Учитывая эту строку образца из /proc/net/tcp:

line="   0: 00000000:0203 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 335 1 c1674320 300 0 0 0"

Примером подражания вашему примеру sscanf с переменной может быть:

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'}
2 голосов
/ 11 ноября 2010

Существует рецепт ActiveState, который реализует базовое сканирование http://code.activestate.com/recipes/502213-simple-scanf-implementation/

1 голос
/ 19 декабря 2016

Обновление: документация Python для его модуля regex, re, включает раздел о моделировании scanf, который я нашел более полезным, чем любой из ответов выше.

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

1 голос
/ 23 августа 2010

Upvoted ответ orip. Я думаю, что это разумный совет, чтобы использовать повторно модуль. Приложение Kodos полезно при приближении к сложной задаче регулярного выражения с Python.

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

1 голос
/ 01 февраля 2010

вы можете повернуть ":" в пробел и сделать split.eg

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

регулярное выражение не требуется (для этого случая)

0 голосов
/ 12 октября 2012
...