我尝试将 Tcl 列表翻译为 python 列表。
有两个问题:
{{12 34}}
翻译不正确。Python 3 代码:
import tkinter
class TclInterpreter(object):
def __init__(self):
self._tcl = tkinter.Tcl()
def eval(self, tcl_cmd):
return self._tcl.eval(tcl_cmd)
class TclPyListTranslator(object):
def __init__(self, tcl):
self._tcl = tcl
def to_py(self, tcl_list, dtype=str):
# convert a Tcl List to python list, also convert elements of each leaf
# node to dtype
self._tcl.eval("set tcl_list %s" % tcl_list)
numItems = int(self._tcl.eval("llength $tcl_list"))
if numItems > 1:
result = [self._tcl.eval("lindex $tcl_list %d" % i) for i in range(
numItems)]
for i in range(numItems):
result[i] = self.to_py("{" + result[i] + "}", dtype)
else:
result = dtype(self._tcl.eval("lindex $tcl_list %d" % 0))
return result
inter = TclInterpreter()
translator = TclPyListTranslator(inter)
tcl_list = "{12 {{12 34}} {56 {78 {11 12} 10}}}"
# prints ['12', '12 34', ['56', ['78', ['11', '12'], '10']]]
# The '12 34' is incorrect
print(translator.to_py(tcl_list))
# does not run
print(translator.to_py(tcl_list, int))
Python 解析器:
def add_element(cache, element):
if element != '':
cache[-1].append(element)
return ''
def parse(raw_tcl_list):
out = []
cache = [out]
element = ''
escape = False
for char in tcl_list:
if escape:
element += char
escape = False
elif char == "\\":
escape = True
elif char in [" ", "\t", "\r", "\n"]:
element = add_element(cache, element)
elif char == "{":
a = []
cache[-1].append(a)
cache.append(a)
elif char == "}":
element = add_element(cache, element)
cache.pop()
else:
element += char
return out[0]
import pprint
pprint.pprint(
parse("{ 12 apple {100} {} {{12 34}} \n {56\n { \\{78 {11 12 11} 10}}}"))
输出:
['12',
'apple',
['100'],
[],
[['12', '34']],
['56', ['{78', ['11', '12', '11'], '10']]]
处理这个问题的最简单方法是获取 Tcl 端的代码(它本身理解 Tcl 列表)来生成 Python 值的字符串形式,然后
eval
在 Python 中生成。然而,复杂的部分是 Tcl 的类型系统与 Python 的类型系统完全不同(以至于我不打算解释它,因为这是一个非常复杂和技术性的争论),决定嵌套列表的叶子在哪里结构不平凡。需要一些假设。有了这些假设,我们就可以用不多的代码完成相当不错的工作。
您需要的 Tcl 端代码是这样的(在您需要整数叶子的情况下):
proc toPythonList {value} {
if {[string is integer -strict $value]} {
return $value
}
set result "\["
foreach item $value {
append result [toPythonList $item] ", "
}
append result "\]"
return $result
}
这意味着你可以做到这一点(我已经添加了一个非常简单的适应不同类型叶子的版本):
class TclPyListTranslator(object):
def __init__(self, tcl):
self._tcl = tcl
self._tcl.eval("""
proc isLeaf.int {value} {
string is integer -strict $value
}
proc isLeaf.str {value} {
expr {![string match "{*}" $value]}
}
proc toPythonLeaf.int {value} { return $value }
proc toPythonLeaf.str {value} { return "\"$value\"" }
proc toPythonList {value dtype} {
if {[isLeaf.$dtype $value]} {
return [toPythonLeaf.$dtype $value]
}
set result "\["
foreach item $value {
append result [toPythonList $item] ", "
}
append result "\]"
return $result
}
""")
def to_py(self, tcl_list, dtype=str):
# convert a Tcl List to python list
return eval(self._tcl.eval("toPythonList %s %s" % (tcl_list, dtype.__name__))
警告:上面的代码应该可以工作,但我无法测试它,因为我没有在任何 Python 解释器中配置 tkinter。不过,这些作品可以自行发挥作用,所以我相当有信心。
我今天需要这样做,并使用接受的答案作为起点,但是,它没有考虑包含空格的字符串。例如:
{hello world "foo bar"}
将导致 ['hello', 'world', '"foo', 'bar"']
而不是 ['hello', 'world', 'foo bar']
这是修改后的实现:
class TCLListParser(object):
NO_ESCAPE = 0
SINGLE_ESCAPE = 1
STRING_ESCAPE = 2
def __init__(self):
self._out = None
self._buffer = None
self._stack = None
def _flush(self):
if self._buffer is not None:
self._stack[-1].append(self._buffer)
self._buffer = None
def _add_char(self, char):
if self._buffer is None:
self._buffer = char
else:
self._buffer += char
def parse(self, tcl_list):
self._out = []
self._stack = [self._out]
self._buffer = None
escape = self.NO_ESCAPE
for char in tcl_list:
# Single escapes
if escape & self.SINGLE_ESCAPE:
self._add_char(char)
escape &= ~self.SINGLE_ESCAPE
elif char == '\\':
escape |= self.SINGLE_ESCAPE
# Strings with spaces, like "hello world"
elif char == '"':
escape ^= self.STRING_ESCAPE
else:
if escape & self.STRING_ESCAPE:
self._add_char(char)
elif char in [" ", "\t", "\r", "\n"]:
self._flush()
elif char == "{":
_ = []
self._stack[-1].append(_)
self._stack.append(_)
elif char == "}":
self._flush()
self._stack.pop()
else:
self._add_char(char)
return self._out[0]
parser = TCLListParser()
pprint.pprint(parser.parse('{ 12 "big apple" {100} {} {{12 34}} \n {56\n { \\{78 {11 12 11} 10}}}'))
结果:
['12',
'big apple',
['100'],
[],
[['12', '34']],
['56', ['{78', ['11', '12', '11'], '10']]]