36

この文字列がある場合:

2+24*48/32

このリストを作成するための最も効率的な方法は何ですか:

['2'、'+'、'24'、'*'、'48'、'/'、'32']

4

11 に答える 11

51

たまたま、分割したいトークンが既に Python トークンであるため、組み込みtokenizeモジュールを使用できます。ほとんどワンライナーです。このプログラム:

from io import StringIO
from tokenize import generate_tokens

STRING = 1
print(
    list(
        token[STRING]
    for token in generate_tokens(StringIO("2+24*48/32").readline)
    if token[STRING]
    )
)

次の出力が生成されます。

['2', '+', '24', '*', '48', '/', '32']
于 2008-09-21T16:25:42.300 に答える
36

splitモジュールから使えreます。

re.split(パターン、文字列、maxsplit=0、フラグ=0)

パターンの出現によって文字列を分割します。pattern でキャプチャ用の括弧が使用されている場合、パターン内のすべてのグループのテキストも結果のリストの一部として返されます。

コード例:

import re
data = re.split(r'(\D)', '2+24*48/32')

\D

UNICODE フラグが指定されていない場合、\D は数字以外の任意の文字に一致します。これはセット [^0-9] と同等です。

于 2008-09-17T23:25:56.403 に答える
18
>>> import re
>>> re.findall(r'\d+|\D+', '2+24*48/32=10')

['2', '+', '24', '*', '48', '/', '32', '=', '10']

連続する数字または連続する非数字に一致します。

各一致は、リスト内の新しい要素として返されます。

使用方法によっては、正規表現を変更する必要がある場合があります。数値と小数点を一致させる必要がある場合など。

>>> re.findall(r'[0-9\.]+|[^0-9\.]+', '2+24*48/32=10.1')

['2', '+', '24', '*', '48', '/', '32', '=', '10.1']
于 2008-09-18T02:39:38.903 に答える
18

これは解析の問題のように見えるため、解析手法に基づく解決策を提示せざるを得ません。

この文字列を「分割」したいように見えるかもしれませんが、実際にやりたいことはそれを「トークン化」することだと思います。トークン化またはレクシングは、解析前のコンパイル手順です。ここで適切な再帰的で適切なパーサーを実装するために、編集で元の例を修正しました。これは、パーサーを手動で実装する最も簡単な方法です。

import re

patterns = [
    ('number', re.compile('\d+')),
    ('*', re.compile(r'\*')),
    ('/', re.compile(r'\/')),
    ('+', re.compile(r'\+')),
    ('-', re.compile(r'\-')),
]
whitespace = re.compile('\W+')

def tokenize(string):
    while string:

        # strip off whitespace
        m = whitespace.match(string)
        if m:
            string = string[m.end():]

        for tokentype, pattern in patterns:
            m = pattern.match(string)
            if m:
                yield tokentype, m.group(0)
                string = string[m.end():]

def parseNumber(tokens):
    tokentype, literal = tokens.pop(0)
    assert tokentype == 'number'
    return int(literal)

def parseMultiplication(tokens):
    product = parseNumber(tokens)
    while tokens and tokens[0][0] in ('*', '/'):
        tokentype, literal = tokens.pop(0)
        if tokentype == '*':
            product *= parseNumber(tokens)
        elif tokentype == '/':
            product /= parseNumber(tokens)
        else:
            raise ValueError("Parse Error, unexpected %s %s" % (tokentype, literal))

    return product

def parseAddition(tokens):
    total = parseMultiplication(tokens)
    while tokens and tokens[0][0] in ('+', '-'):
        tokentype, literal = tokens.pop(0)
        if tokentype == '+':
            total += parseMultiplication(tokens)
        elif tokentype == '-':
            total -= parseMultiplication(tokens)
        else:
            raise ValueError("Parse Error, unexpected %s %s" % (tokentype, literal))

    return total

def parse(tokens):
    tokenlist = list(tokens)
    returnvalue = parseAddition(tokenlist)
    if tokenlist:
        print 'Unconsumed data', tokenlist
    return returnvalue

def main():
    string = '2+24*48/32'
    for tokentype, literal in tokenize(string):
        print tokentype, literal

    print parse(tokenize(string))

if __name__ == '__main__':
    main()

括弧の処理の実装は、読者の課題として残されています。この例では、加算の前に正しく乗算が行われます。

于 2008-09-17T23:54:14.090 に答える
6

これは解析の問題であるため、正規表現も split() も「良い」解決策ではありません。代わりにパーサージェネレーターを使用してください。

pyparsingを詳しく調べます。Python Magazineには、pyparse に関するまともな記事もいくつかあります。

于 2008-09-19T07:37:18.587 に答える
5

s = "2+24*48/32"

p = re.compile(r'(\W+)')

p.split(s)

于 2008-09-17T23:25:52.833 に答える
4

これに対する別の解決策は、そのような電卓をまったく書かないようにすることです。RPN パーサーの作成ははるかに簡単で、中置記法を使用した数学の作成に固有のあいまいさがありません。

import operator, math
calc_operands = {
    '+': (2, operator.add),
    '-': (2, operator.sub),
    '*': (2, operator.mul),
    '/': (2, operator.truediv),
    '//': (2, operator.div),
    '%': (2, operator.mod),
    '^': (2, operator.pow),
    '**': (2, math.pow),
    'abs': (1, operator.abs),
    'ceil': (1, math.ceil),
    'floor': (1, math.floor),
    'round': (2, round),
    'trunc': (1, int),
    'log': (2, math.log),
    'ln': (1, math.log),
    'pi': (0, lambda: math.pi),
    'e': (0, lambda: math.e),
}

def calculate(inp):
    stack = []
    for tok in inp.split():
        if tok in self.calc_operands:
            n_pops, func = self.calc_operands[tok]
            args = [stack.pop() for x in xrange(n_pops)]
            args.reverse()
            stack.append(func(*args))
        elif '.' in tok:
            stack.append(float(tok))
        else:
            stack.append(int(tok))
    if not stack:
        raise ValueError('no items on the stack.')
    return stack.pop()
    if stack:
        raise ValueError('%d item(s) left on the stack.' % len(stack))

calculate('24 38 * 32 / 2 +')
于 2008-09-18T03:07:27.947 に答える
4

正規表現:

>>> import re
>>> splitter = re.compile(r'([+*/])')
>>> splitter.split("2+24*48/32")

正規表現を拡張して、分割したい他の文字を含めることができます。

于 2008-09-17T23:21:58.800 に答える
1
>>> import re
>>> my_string = "2+24*48/32"
>>> my_list = re.findall(r"-?\d+|\S", my_string)
>>> print my_list

['2', '+', '24', '*', '48', '/', '32']

これでうまくいきます。私は以前にこの種の問題に遭遇しました。

于 2012-01-14T16:21:15.617 に答える
0

ティムが意味したと確信しています

splitter = re.compile(r'([\D])'). 

彼が持っているものを正確にコピーすると、ではdigitsなく、しか得られませんoperators

于 2008-09-18T00:45:18.117 に答える
0

これは質問に正確に答えるものではありませんが、あなたが達成しようとしていることを解決すると信じています。コメントとして追加したいのですが、まだ許可がありません。

個人的には、Python の数学機能を exec で直接利用します。

式 = "2+24*48/32"
exec "結果 = " + 式
の出力結果
38

于 2010-08-19T00:38:43.097 に答える