2017-01-12 4 views
-1

Я хотел бы знать, есть ли способ узнать, является ли строка с параметром действительной и подсчет количества полей внутри. Я бы предпочел родную функцию Python, но я ничего не нашел об этом. Пусть говорят, что эта функция называется count_variablesКак считать поля в строке?

я бы:

count_variables("Test") # -> 0 

count_variables("Test {0} {1}") # -> 2 

count_variables("Test {0} {2}") # -> raise error {1} is missing 

count_variables("Test {} {}") # -> 2 

count_variables("Test{ {} {}") # -> raise error { is not escaped 

count_variables("Test {} {0}") # -> raise error cannot switch from automatic field numbering to manual field 

Я использую Python 2.7

Как @ dot.Py уже упоминалось, более легкую функцию is_valid может быть проще. Только проверка строки без необходимых параметров

is_valid("Test") # -> True 

is_valid("Test {0} {2}") # -> False 

... 

Благодарим за помощь.

+2

вы пытались сделать это самостоятельно? – depperm

+0

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

+1

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

ответ

2

Моя идея заключается в том, чтобы использовать string.Formatter.parse считать переменные, а затем на самом деле попробовать форматирование с точно, что многих переменных.

Он подходит для примеров, перечисленных в вопросе, но в остальном он не очень хорошо протестирован.

import string 

def vcount(fmt): 
    try: 
     cnt = sum(1 for text, name, spec, conv in string.Formatter().parse(fmt) if name is not None) 
     fmt.format(*range(cnt)) 
    except Exception as err: 
     print("error: {}".format(err)) 
     return None # or raise ValueError(err) 
    print(cnt) 
    return cnt 

vcount("Test") # -> 0 
vcount("Test {0} {1}") # -> 2 
vcount("Test {0} {2}") # -> raise error 
vcount("Test {} {}") # -> 2 
vcount("Test{ {} {}") # -> raise error 
vcount("Test {} {0}") # -> raise error 

UPDATE: другой подход, не эквивалентен исходному ответ. См. Комментарии. Сообщение об ошибке для недопустимого ввода может ввести в заблуждение.

def vcount(fmt): 
    try: 
     names = [name for text, name, spec, conv in string.Formatter().parse(fmt) if name is not None] 
     if all(name == "" for name in names): 
      # unnumbered fields "{} {}" 
      cnt = len(names) 
     else: 
      # numbered "{0} {1} {2} {0}" 
      cnt = 1 + max(int(name) for name in names) 
     fmt.format(*range(cnt)) 
    except Exception as err: 
     print("error: {}".format(err)) 
     return None # or raise ValueError(err) 
    print(cnt) 
    return cnt 
+2

'fmt.format (* range (cnt))' собирается поднять ошибку в спецификациях нечислового формата. –

+0

Правда, но на самом деле это то, что мне нужно. :) – M07

+0

@VPfB Это была хорошая идея, чтобы использовать count, а затем использовать метод format. Хорошая работа;) – M07

2

Я не знаю, есть ли встроенный способ, но я внедрил решение самостоятельно. Я тестировал его под Python 3.5 и Python 2.7. Это "исправить", поскольку она проходит тестовые случаи вы предоставили:

Реализация

import re 
import unittest 


class Numbering: 
    NONE = 0 
    MANUAL = 1 
    AUTOMATIC = 2 


def consecutive_variables(variables): 
    sorted_variables = sorted(variables) 
    return all(a == b - 1 for a, b in zip(sorted_variables[:-1], sorted_variables[1:])) 


def count_variables(data): 
    numbering = Numbering.NONE 
    last_variable = 0 
    variables = [] 

    for i in range(len(data)): 
     c = data[i] 

     if c == '{': 
      match = re.match(r'(\d|^{|^})*?(?=})', data[i + 1:]) 

      if not match: 
       raise ValueError('Invalid variable formatting') 

      variable_body = match.group(0) 

      if variable_body == '': 
       if numbering == Numbering.MANUAL: 
        raise ValueError('Cannot switch from manual to automatic numbering') 

       numbering = Numbering.AUTOMATIC 
       variables.append(last_variable) 
       last_variable += 1 
      else: 
       if numbering == Numbering.AUTOMATIC: 
        raise ValueError('Cannot switch from automatic to manual numbering') 

       numbering = Numbering.MANUAL 
       variables.append(int(variable_body)) 

      i += len(variable_body) + 1 
      assert data[i] == '}' 

    if not consecutive_variables(variables): 
     raise ValueError('Variables are not consecutive') 

    return len(variables) 

тесты

class TestCountVariables(unittest.TestCase): 
    def test_1(self): 
     self.assertEqual(count_variables("Test"), 0) 

    def test_2(self): 
     self.assertEqual(count_variables("Test {0} {1}"), 2) 

    def test_3(self): 
     with self.assertRaises(ValueError): 
      count_variables("Test {0} {2}") 

    def test_4(self): 
     self.assertEqual(count_variables("Test {} {}"), 2) 

    def test_5(self): 
     with self.assertRaises(ValueError): 
      count_variables("Test{ {} {}") 

    def test_6(self): 
     with self.assertRaises(ValueError): 
      count_variables("Test {} {0}") 


if __name__ == '__main__': 
    unittest.main() 

Выход

...... 
---------------------------------------------------------------------- 
Ran 6 tests in 0.000s 

OK 
+0

Извините, но я не хотел использовать регулярное выражение для этого метода. Спасибо за ваше время. – M07

2

Вы можете создать объект string.Format и использовать его метод parse, чтобы разбить строку на (literal_text, field_name, format_spec, conversion) кортежи. Это приведет к некоторым ошибкам, например, небезопасным {, но другие, такие как неправильно пронумерованные поля, не будут пойманы.

Как чувство кишки, я думаю, вы могли бы создать ребенка из string.Format, который возвращает макет данных для его различных вызовов, детали перекодирования по мере их поступления. Затем вы поймаете все ошибки. Это должно быть проще, чем разобраться в себе.

Насколько получать отсчеты и ловить некоторые ошибки формата, это будет делать:

import string 

def count_variables(fmtstr): 
    parser = string.Formatter().parse(fmtstr) 
    items = [] 
    while True: 
     try: 
      item = next(parser) 
      items.append(item) 
      literal_text, field_name, format_spec, conversion = item 
      # analyze here... 
     except ValueError as e: 
      retval = e 
      break 
     except StopIteration: 
      retval = len(items) 
      break 
    print fmtstr + ':', retval 
    return retval 
+0

Это было хорошее начало, используя string.Formatter(). Спасибо за помощь. – M07

0

В дополнение к this answer и обрабатывать следующий случай:

vcount("Test {0} {1} {0} ") # -> 3 (Should be 2) 

Я предлагаю это решение, основанное на @VPfB ответ

def count_and_check_fields(string_format): 
    try: 
     unnamed_fields_count = 0 
     named_fields = set() 
     for literal_text, field_name, format_spec, conversion in string.Formatter().parse(string_format): 
      if field_name is not None: 
       if field_name: 
        named_fields.add(field_name) 
       else: 
        unnamed_fields_count += 1 

     fields_count = len(named_fields) + unnamed_fields_count 
     string_format.format(*range(fields_count)) 

     return fields_count, None 
    except Exception as err: 
     return None, err.message 

count_and_check_fields("Test {0} {1} {0} ") # -> 2 
count_and_check_fields("Test {} {} {} ") # -> 3 
Смежные вопросы