将 Tcl 列表转换为 Python 列表

问题描述 投票:0回答:3

我尝试将 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 list tcl
3个回答
3
投票

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']]]

1
投票

处理这个问题的最简单方法是获取 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。不过,这些作品可以自行发挥作用,所以我相当有信心。


0
投票

我今天需要这样做,并使用接受的答案作为起点,但是,它没有考虑包含空格的字符串。例如:

{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']]]
© www.soinside.com 2019 - 2024. All rights reserved.