阅读并解析令牌文件?

时间:2016-09-26 12:45:57

标签: python python-2.7

编辑我编辑了一个问题来纠正一个重大错误(遗憾的是,到目前为止提供的所有答案都无效):命令行可以在单词之间包含空格,所以没有解决方法基于使用空格作为标记之间的分隔符及其参数将起作用!我在原帖中对这一遗漏深表歉意。

我有一个包含简单(假设)命令语言命令的文本文件,如下所示:

$BOOLEAN_COMMAND

$NUMERIC COMMAND ALPHA 1 3 6 9 10

$NUMERIC COMMAND BETA
2 7 9 10 15
25 40 900 2000
$NUMERIC COMMAND GAMMA 6 9 11

1)每个“COMMAND”以一个特殊字符('$')开头,后跟一个数字序列(“命令参数”)。

2)没有参数的命令被认为是“布尔命令”,默认情况下假设值为True。

3)可以有许多带参数的命令(我在这里将它们称为“Alpha”,“Beta”等),但无论它们的名称如何,都会跟随一行包含参数的一行。

4)包含命令的行之间可能有或没有空行。

我编写了一个函数,它读取包含所述命令和参数的文件,并仅返回特定命令的参数(作为函数参数传递)。这是:

def get_params(fname, command):
    fspecs = open(fname,"r")

    params = []
    for cline in fspecs:
        cline = cline.strip()
        if not cline:
            continue     # Blank line
        if cline.startswith('$'):
            if command in cline:
                params = cline.partition(command)[-1].split()
        #else:    # Continuation of a command.
        #    params.append(cline)
    fspecs.close()

    if len(params) == 0: # Boolean command, defaults to True
        ret_val = True
    else:
        ret_val = ' '.join(params) # Numeric command, gets parameters
    return ret_val

p = get_params('command_file', '$BOOLEAN COMMAND')
print p # returns True
p = get_params('command_file', '$NUMERIC COMMAND ALPHA')
print p # returns 1 3 6 9 10
p = get_params('command_file', '$NUMERIC COMMAND BETA')
print p # should return 2 7 9 10 15, but returns True

当给定命令的参数在一行(紧接在命令令牌之后)时,上述代码有效,但当参数在后续行中时失败(在这种情况下,它只返回'True',因为没有参数在命令令牌后找到)。如果'else'子句没有被注释掉,它只需要包含任何令牌参数的所有行,直到文件的末尾。实际运行上面的代码将更好地证明问题。

我想要的是能够读取一个特定的令牌(传递给该函数)并获取其参数,无论它们是否延伸到多行或者命令文件中可能有多少个其他令牌。

3 个答案:

答案 0 :(得分:2)

由于命令可能需要多行,因此更容易不按换行拆分文本文件。我建议按' $'分拆。代替。

此示例代码有效:

def get_params(fname, desired_command):
    with open(fname,"r") as f:
        content = f.read()
    for element in content.split('$'):
        element = element.replace('\n', ' ').strip()
        if not element:
            continue
        if ' ' in element:
            command, result = element.split(' ', 1)
        else:
            command, result = element, True
        if desired_command == command or desired_command == '${}'.format(command):
            return result

这是我的编辑,它适用于包含命令的空格:

import re

COMMAND_RE = re.compile('([A-Z_ ]+[A-Z]) ?(.+)? *')

def get_params(fname, desired_command):
    with open(fname,"r") as f:
        content = f.read()
    for element in content.split('$'):
        element = element.replace('\n', ' ').strip()
        if not element:
            continue
        command, result = COMMAND_RE.search(element).groups()
        if desired_command == command or desired_command == '${}'.format(command):
            return result or True

答案 1 :(得分:1)

这是我的方法:根据空白(空格,制表符和新行)拆分所有内容。然后构造一个字典,命令名称作为键,参数作为值。从该字典中,您可以查找任何命令的参数。此方法仅打开和读取文件一次:

from collections import deque

def parse_commands_file(filename):
    with open(filename) as f:
        tokens = deque(f.read().split())

    command2parameters = dict()
    while tokens:
        command_name = tokens.popleft()
        # Added
        while tokens and tokens[0].isalpha() and not tokens[0].startswith('$'):
            command_name = command_name + ' ' + tokens.popleft()
        # end added

        parameters = []
        while tokens and not tokens[0].startswith('$'):
            parameters.append(int(tokens.popleft()))
        command2parameters[command_name] = parameters or True

    return command2parameters

if __name__ == '__main__':
    command = parse_commands_file('commands.txt')
    print '$BOOLEAN_COMMAND:', command.get('$BOOLEAN_COMMAND')
    print '$NUMERIC_COMMAND_ALPHA:', command.get('$NUMERIC_COMMAND_ALPHA')
    print '$NUMERIC_COMMAND_BETA:', command.get('$NUMERIC_COMMAND_BETA')

输出:

$BOOLEAN_COMMAND: True
$NUMERIC_COMMAND_ALPHA: [1, 3, 6, 9, 10]
$NUMERIC_COMMAND_BETA: [2, 7, 9, 10, 15, 25, 40, 900, 2000]

讨论

  • 我使用deque数据结构,代表双端队列。这种结构的行为类似于列表,但在插入和从两端弹出方面效率更高
  • 在解析参数时,我将它们转换为int,您可以将它们转换为浮动或保留它们
  • 表达式parameters or True基本上说:如果参数为空,请使用True,否则请保留

更新

我添加了一个补丁来处理名称中带空格的命令。但是,此解决方案只是一个补丁,如果您有多个空格,它不起作用,如:

$MY      COMMAND HERE

在这种情况下,多个空格被压缩成一个。

答案 2 :(得分:1)

这是另一种解决方案。这个使用正则表达式,它不会在命令中挤压多个空格:

import re

def parse_commands_file(filename):
    command_pattern = r"""
        (\$[A-Z _]+)*  # The command, optional
        ([0-9 \n]+)*   # The parameter which might span multiple lines, optional
    """
    command_pattern = re.compile(command_pattern, flags=re.VERBOSE)

    with open(filename) as f:
        tokens = re.findall(command_pattern, f.read())
        return {cmd.strip(): [int(n) for n in params.split()] for cmd, params in tokens}

if __name__ == '__main__':
    command = parse_commands_file('commands.txt')
    print '$BOOLEAN_COMMAND:', command.get('$BOOLEAN_COMMAND')
    print '$NUMERIC COMMAND ALPHA:', command.get('$NUMERIC COMMAND ALPHA')
    print '$NUMERIC COMMAND BETA:', command.get('$NUMERIC COMMAND BETA')

讨论

基本上,命令模式表示每行可能包含命令名和数字参数两部分,两者都是可选的。

请注意,命令可能包含尾随空格,这就是我们使用表达式cmd.strip()将其删除的原因。

此外,re.findall()返回的参数部分需要通过空格分隔它们来解析,然后使用表达式int转换为[int(n) for n in params.split()]

相关问题