2016-09-26 3 views
3

EDIT: Я отредактировал вопрос, чтобы исправить серьезную ошибку (что, к сожалению, аннулирует все ответы, предоставленные до сих пор): строки команд могут содержать пробелы между словами, поэтому нет решения, основанного на использовании пробелов в качестве разделителей между токены и их параметры будут работать! Я глубоко извиняюсь за это упущение в моем оригинальном посте.Прочитайте и проанализируйте файл жетонов?

У меня есть текстовый файл, содержащие команды в простом (гипотетическом) командном языке следующим образом:

$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) Могут быть много команд с параметрами (я называю их здесь «Альфа», «Бета» и т. Д.), Но независимо от их имен, за ними следует еще одна строка, содержащая параметры.

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 

Приведенные выше код работает, когда параметры данной команды в одной строке (сразу после маркеров команды), но терпит неудачу, когда параметры находятся в последующих строках (в этом случае, просто возвращает «Истина», потому что после токена команды не обнаруживаются никакие параметры). Если предложение «else» не закомментировано, оно просто берет все строки, содержащие параметры всех токенов, до конца файла. Фактически выполнение вышеуказанного кода лучше продемонстрирует проблему.

Что я хочу - это прочитать один конкретный токен (переданный функции) и получить только его параметры, независимо от того, распространяются ли они на несколько строк или сколько других токенов может быть в файле команд.

+0

Если это не будет ', если cline.startswith («$») '? (знак доллара вместо звездочки) –

+0

Да, извините. Я отредактировал вопрос, чтобы исправить это. – maurobio

ответ

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 
+0

Спасибо, @sphere! Это действительно хорошо работает ... но подождите! Что делать, если токены команды содержат пробелы (то есть, если они находятся в формате «$ BOOLEAN COMMAND» или «$ NUMERIC COMMAND 1 2 3 4 5»? Тогда этот код терпит неудачу, потому что он использует пробелы в качестве разделителей между командами и параметрами. – maurobio

+0

То же самое: мы не знали, что ваши команды могут иметь пробелы.Я бы переключился на разделение регулярных выражений. Можете ли вы подтвердить, что ваши команды ВСЕГДА сделаны из прописных букв и пробелов и всегда начинаются с $ -sign? – sphere

+0

Да, команды всегда находятся в UPPERCASE и могут (действительно) иметь пробелы между словами. Команда всегда начинается с символа '$', что действительно указывает синтаксическому анализатору начало новой командной строки. – maurobio

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, в противном случае оставьте это будет

Update

Я добавил патч для обработки команд с пробелами в именах. Тем не менее, это решение просто патч, он не работает, если у вас есть несколько пробелов, такие как:

$MY  COMMAND HERE 

В этом случае несколько пространств получили сжаты в один.

+0

Спасибо, @Hai Vu! Действительно приятное решение (я не знал структуру данных «deque», это действительно полезно). Однако ... это решение также терпит неудачу, если токены команды содержат пробелы. – maurobio

+0

В вашем оригинальном посте вы не указали, не показывали и не пробежали. Если вы обновите свой ввод, я посмотрю, что я могу сделать. –

+0

Я исправил свое решение, посмотрел комментарии между ** Добавлен ** и ** добавлен конец ** –

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()]

+0

Еще раз спасибо, @Hai Vu! Это выглядит проще. – maurobio

Смежные вопросы