Python 中的 sScanf

我正在寻找与 Python 中的 sscanf()等价的程序。我想解析 /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) ,或者在标准库中有一个字符串分割函数,可以在我不知道的字符范围内进行分割?

145371 次浏览

您可以使用 re模块对一个字符范围进行拆分。

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

你可以把“ :”转到空格,然后把它分开

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

不需要正则表达式(在这种情况下)

Python 没有内置 sscanf等价物,而且大多数时候,通过直接使用字符串、使用 regexp 或使用解析工具来解析输入实际上更有意义。

可能对于翻译 C 最有用的是,人们已经实现了 sscanf,比如在这个模块: http://hkn.eecs.berkeley.edu/~dyoo/python/scanf/

在这种特殊情况下,如果您只想基于多个拆分字符来拆分数据,那么 re.split确实是正确的工具。

如果分隔符是“ :”,那么您可以在“ :”上进行分隔,然后在字符串上使用 x.Strip ()来去除任何前导或尾随的空格。Int ()将忽略空格。

您可以使用 命名的团体解析模块 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'}

当我处于 C 语言的状态时,我通常会使用 zip 和 list 来理解类似于 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]

还有 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',) {}>

你可以安装熊猫和使用 pandas.read_fwf固定宽度的格式文件。例如使用 /proc/net/arp:

In [230]: df = pandas.read_fwf("/proc/net/arp")


In [231]: print(df)
IP address HW type Flags         HW address Mask Device
0   141.38.28.115     0x1   0x2  84:2b:2b:ad:e1:f4    *   eth0
1   141.38.28.203     0x1   0x2  c4:34:6b:5b:e4:7d    *   eth0
2   141.38.28.140     0x1   0x2  00:19:99:ce:00:19    *   eth0
3   141.38.28.202     0x1   0x2  90:1b:0e:14:a1:e3    *   eth0
4    141.38.28.17     0x1   0x2  90:1b:0e:1a:4b:41    *   eth0
5    141.38.28.60     0x1   0x2  00:19:99:cc:aa:58    *   eth0
6   141.38.28.233     0x1   0x2  90:1b:0e:8d:7a:c9    *   eth0
7    141.38.28.55     0x1   0x2  00:19:99:cc:ab:00    *   eth0
8   141.38.28.224     0x1   0x2  90:1b:0e:8d:7a:e2    *   eth0
9   141.38.28.148     0x1   0x0  4c:52:62:a8:08:2c    *   eth0
10  141.38.28.179     0x1   0x2  90:1b:0e:1a:4b:50    *   eth0


In [232]: df["HW address"]
Out[232]:
0     84:2b:2b:ad:e1:f4
1     c4:34:6b:5b:e4:7d
2     00:19:99:ce:00:19
3     90:1b:0e:14:a1:e3
4     90:1b:0e:1a:4b:41
5     00:19:99:cc:aa:58
6     90:1b:0e:8d:7a:c9
7     00:19:99:cc:ab:00
8     90:1b:0e:8d:7a:e2
9     4c:52:62:a8:08:2c
10    90:1b:0e:1a:4b:50


In [233]: df["HW address"][5]
Out[233]: '00:19:99:cc:aa:58'

默认情况下,它试图自动计算出格式,但是您可以为更明确的指令提供一些选项(参见 文件)。熊猫中也有 其他输氧程序,对于其他文件格式也很强大。

有一个关于如何使用 libc中的 sscanf官方 Python 文档中的示例:

# import libc
from ctypes import CDLL
if(os.name=="nt"):
libc = cdll.msvcrt
else:
# assuming Unix-like environment
libc = cdll.LoadLibrary("libc.so.6")
libc = CDLL("libc.so.6")  # alternative


# allocate vars
i = c_int()
f = c_float()
s = create_string_buffer(b'\000' * 32)


# parse with sscanf
libc.sscanf(b"1 3.14 Hello", "%d %f %s", byref(i), byref(f), s)


# read the parsed values
i.value  # 1
f.value  # 3.14
s.value # b'Hello'