如何将字符串拆分为列表?
Posted
技术标签:
【中文标题】如何将字符串拆分为列表?【英文标题】:How do I split a string into a list? 【发布时间】:2010-09-10 10:58:54 【问题描述】:如果我有这个字符串:
2+24*48/32
创建此列表的最有效方法是什么:
['2', '+', '24', '*', '48', '/', '32']
【问题讨论】:
您想将一个字符串拆分为一个列表,但又不想使用 .split() 因为它返回一个列表?你在自相矛盾。如果您不想要列表,那么您想要想要什么? @Jim:我认为 Jibmo 的意思是 split() 只允许你指定一个分隔符,所以他必须为 '+' 调用一次,为 '-' 调用一次,为 '/ 调用一次'等... 抱歉解释不好,我的意思是 split 将返回一个列表,这意味着对于第二次拆分,我现在需要遍历列表中的字符串。语法不正确的例子.. string = "2+2-2" list = string.split(+) 返回 ['2', '+', '2-2'] 现在我需要遍历 3 个字符串 您应该提到您正在开发一个程序,该程序需要能够将这些字符串作为算术表达式进行计算。 Jerub 的回答涵盖了这一点,但那是因为他是一个读心术。 为什么不直接使用SymPy?它应该做你想要达到的目标。 【参考方案1】:恰好你想要拆分的标记已经是 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']
【讨论】:
很好的答案,我没有意识到这个模块存在:) 代替或手动分配STRING=1
,您可以通过执行from token import STRING
来使用token
模块中的常量。如果您需要多个令牌常量,这将特别有用。
为什么这么复杂的答案会被评为这么高?这是一个非常简单的问题。找到最简洁、最简洁的答案时发生了什么?【参考方案2】:
您可以使用re
模块中的split
。
re.split(pattern, string, maxsplit=0, flags=0)
通过出现的模式分割字符串。如果捕获括号 在pattern中使用,那么pattern中所有组的文本都是 也作为结果列表的一部分返回。
示例代码:
import re
data = re.split(r'(\D)', '2+24*48/32')
\D
当未指定 UNICODE 标志时,\D 匹配任何非数字 特点;这等价于集合 [^0-9]。
【讨论】:
【参考方案3】:>>> 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']
【讨论】:
【参考方案4】:这看起来像是一个解析问题,因此我不得不提出一个基于解析技术的解决方案。
虽然您似乎想要“拆分”这个字符串,但我认为您真正想要做的是“标记化”它。标记化或词法分析是解析之前的编译步骤。我在编辑中修改了我的原始示例,以在此处实现适当的递归体面解析器。这是手动实现解析器的最简单方法。
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()
括号处理的实现留给读者作为练习。这个例子会在加法之前正确地进行乘法运算。
【讨论】:
我现在正在阅读标记化以了解它。所以我不能说问题出在哪里,尽管我认为这个脚本将同时评估 * 和 / ,这是不正确的。 8/2*2 这个字符串应该打印 2 的结果,但它打印的结果是 8。 对不起,我错了,总是把 bomdas 从字面上看,结果乘法和除法在优先顺序上是相等的,先出现的先计算 在tokenize
:为什么要使用re
删除内置字符串函数上的空格?【参考方案5】:
这是一个解析问题,因此 regex 和 split() 都不是“好的”解决方案。请改用解析器生成器。
我会仔细查看pyparsing。 Python Magazine 也有一些关于 pyparsing 的不错的文章。
【讨论】:
【参考方案6】:s = "2+24*48/32"
p = re.compile(r'(\W+)')
p.split(s)
【讨论】:
【参考方案7】:正则表达式:
>>> import re
>>> splitter = re.compile(r'([+*/])')
>>> splitter.split("2+24*48/32")
您可以扩展正则表达式以包含您想要拆分的任何其他字符。
【讨论】:
【参考方案8】:另一个解决方案是完全避免编写这样的计算器。编写 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 +')
【讨论】:
你为什么不直接去实现,它只会多 5 行!【参考方案9】:>>> import re
>>> my_string = "2+24*48/32"
>>> my_list = re.findall(r"-?\d+|\S", my_string)
>>> print my_list
['2', '+', '24', '*', '48', '/', '32']
这样就可以了。我以前也遇到过这种问题。
【讨论】:
【参考方案10】:这并不能完全回答问题,但我相信它可以解决您想要实现的目标。我会将其添加为评论,但我还没有这样做的权限。
我个人会直接通过 exec 来利用 Python 的数学功能:
表达式 = "2+24*48/32"exec "result = " + 表达式打印 结果 38
【讨论】:
如果我错了,请原谅我,但使用result = eval(expression)
不是更好吗?
确实会;我很抱歉。【参考方案11】:
我确定蒂姆的意思是
splitter = re.compile(r'([\D])').
如果你完全复制他的内容,你只会得到digits
而不是operators
。
【讨论】:
以上是关于如何将字符串拆分为列表?的主要内容,如果未能解决你的问题,请参考以下文章