web-dev-qa-db-ja.com

sscanf in Python

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ではない)に相当)、または標準ライブラリに文字列分割関数がありますが、これは知らない文字の範囲で分割しますの?

57
Matt Joiner

Pythonにはsscanfに相当する組み込み機能がありません。ほとんどの場合、文字列を直接操作したり、正規表現を使用したり、解析ツールを使用して入力を解析する方がはるかに理にかなっています。

おそらくCの翻訳に最も役立つでしょう。人々はこのモジュールのようにsscanfを実装しています: http://hkn.eecs.berkeley.edu/~dyoo/python/scanf/

この特定のケースでは、複数の分割文字に基づいてデータを分割する場合は、re.splitは本当に正しいツールです。

28
Mike Graham

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',) {}>
61
Craig McQueen

私がCムードにあるとき、私は通常、scanfのような振る舞いのためにZipとリストの内包表記を使用します。このような:

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]
58
Chris Dellin

reモジュールを使用して、文字の範囲で分割できます。

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

名前付きグループ を使用して、モジュール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'}
14
orip

基本的なscanfを実装するActiveStateレシピがあります http://code.activestate.com/recipes/502213-simple-scanf-implementation/

2
markovg

更新:正規表現モジュールreのPythonドキュメント)には、scanfのシミュレーションに関するセクションが含まれています。

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

2
Jon

投票したoripの答え。 reモジュールを使用するのは適切なアドバイスだと思います。 Kodosアプリケーションは、Pythonで複雑な正規表現タスクにアプローチするときに役立ちます。

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

1
codeasone

「:」をスペースに変えて、split.egを実行できます。

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

正規表現は必要ありません(この場合)

1
ghostdog74

区切り文字が「:」の場合、「:」で分割し、文字列でx.strip()を使用して、先頭または末尾の空白を削除できます。 int()はスペースを無視します。

0
Lennart Regebro

odiakによるPython 2実装 があります。

0
Janus Troelsen