问题描述
我希望输入字符串 "add [7,8,9+5,'io open'] 7&4 67"
像 ['add',"[7,'io open']",'7&4','67']
一样拆分,即在行内,字符串必须保留在引号内并且根本不能拆分,否则需要基于空格的拆分,像这样:
>>> import shlex
>>> shlex.split("add [7,\\'io\\ open\\'] 7&4 67")
['add','67']
但是如果可能的话,用户不应该使用 \\
,至少不要用于引号,但如果可能的话也不要用于字符串中的空格。
如上所示的函数 break_down()
会是什么样的?我尝试了以下操作,但它不处理字符串中的空格:
>>> import shlex
>>> def break_down(ln) :
... ln = ln.replace("'","\\'")
... ln = ln.replace('"','\\"')
... # User will still have to escape in-string whitespace
... return shlex.split(ln) # Note : Can't use posix=False; will split by in-string whitespace and has no escape seqs
...
>>> break_down("add [7,'io\\ open'] 7&4 67")
['add','67']
>>> break_down("add [7,'io open'] 7&4 67")
['add','io","open']",'67']
也许有更好的函数/方法/技术可以做到这一点,我对整个标准库还不是很有经验。或者我可能只需要编写一个自定义的 split()
?
编辑 1:进展
>>> def break_down(ln) :
... ln = r"{}".format(ln) # escape sequences don't require \\
... ln = ln.replace("'",r"\'")
... ln = ln.replace('"',r'\"')
... return shlex.split(ln)
所以现在用户只需要使用一个 \
来转义任何引号/空格等,有点像他们在 shell 中所做的那样。看起来可行。
解决方法
我通过编写自定义词法系统(有点)解决了这个问题。
我决定使用 re
,因为无论如何代码都大量使用 re
,并且在 this reddit comment 的帮助下,已经解决了这个问题:
def lex(ln):
ln = ln.split('#')[0] # Strip comments
tkn_delims,relst = '\'\'""{}()[]',[] # Edit tkn_delims to add more delimiters
for i in range(0,len(tkn_delims),2):
# Add regex for delimiter
relst.append(r'\{0}[^{1}]*\{1}'.format(tkn_delims[i],tkn_delims[i+1]))
regex = '|'.join(relst) + r'|\S+' # Build regex
import re
return re.findall(regex,ln)
编辑:感谢@furas 的评论:“第一反应:你不能在参数中使用 #...”,代码编辑为仅在 {{1} 时识别评论的开始} 显示为 token 的第一个元素。因此:
-
#
词法到<command> '#...' ['#...#']
-
['command',"'#...'","['#...#']"]
或<command> '...' # does xyz
词法<command> '...' #does xyz
。
已编辑['<command>',"'...'"]
:
lex()