2017-01-12 1 views
-1

매개 변수가있는 문자열이 유효한지 알 수있는 방법이 있는지, 내부에 얼마나 많은 필드가 있는지 계산하고 싶습니다. 네이티브 파이썬 함수를 선호하지만 그것에 대해 아무것도 찾지 못했습니다. 하자이 기능은 count_variables라고 말할문자열에서 필드를 계산하는 방법은 무엇입니까?

나는 것 : 언급 한 내가 @ dot.Py 파이썬 2.7

을 사용하고

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 

, 가벼운 기능을 is_valid 수 쉬워 져라. 매개 변수가 필요없는 문자열 유효성 검사 만 가능

is_valid("Test") # -> True 

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

... 

감사합니다.

+2

당신이 스스로 일을 시도? – depperm

+0

가능한 답변이 너무 많거나 좋은 대답은이 형식에 비해 너무 길어집니다. 응답 집합의 범위를 좁히거나 몇 단락에서 대답 할 수있는 문제를 찾아내는 세부 정보를 추가하십시오. –

+1

나는 바퀴를 재발 명하지 않는 것이 중요하다고 생각한다. 그래서 내가 네이티브 메소드가 존재하는지 묻는 것을 선호합니다. 매개 변수가있는 경우 "형식"메서드가 예외를 발생시킬 수 있지만 그렇지 않습니다. 매개 변수없이 유효성을 검사해야합니다. 그것이 존재하지 않는다면 나는 그것을 나의 자신으로 할 것이지만, 나는 그럴 수 있습니다. – 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에 다음과 같은 경우에 처리하는 : 나는 @VPfB의 답변에 따라이 솔루션을 제안

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

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 
관련 문제